VirtualBox

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

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

Main: Generate enum value to string conversion functions for the API. Use these for logging instead of the Global::stringify* ones as they are untranslated, the Global:: ones are for use in error message when translated enum value names are desired (questionable).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 541.4 KB
Line 
1/* $Id: MachineImpl.cpp 93410 2022-01-24 14:45:10Z 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{
9617 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9618
9619 HRESULT rc = S_OK;
9620
9621 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9622
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 = Utf8Str(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
9848 return rc;
9849}
9850
9851/**
9852 * Saves and commits machine data, user data and hardware data.
9853 *
9854 * Note that on failure, the data remains uncommitted.
9855 *
9856 * @a aFlags may combine the following flags:
9857 *
9858 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9859 * Used when saving settings after an operation that makes them 100%
9860 * correspond to the settings from the current snapshot.
9861 * - SaveS_Force: settings will be saved without doing a deep compare of the
9862 * settings structures. This is used when this is called because snapshots
9863 * have changed to avoid the overhead of the deep compare.
9864 *
9865 * @note Must be called from under this object's write lock. Locks children for
9866 * writing.
9867 *
9868 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9869 * initialized to false and that will be set to true by this function if
9870 * the caller must invoke VirtualBox::i_saveSettings() because the global
9871 * settings have changed. This will happen if a machine rename has been
9872 * saved and the global machine and media registries will therefore need
9873 * updating.
9874 * @param alock Reference to the lock for this machine object.
9875 * @param aFlags Flags.
9876 */
9877HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9878 AutoWriteLock &alock,
9879 int aFlags /*= 0*/)
9880{
9881 LogFlowThisFuncEnter();
9882
9883 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9884
9885 /* make sure child objects are unable to modify the settings while we are
9886 * saving them */
9887 i_ensureNoStateDependencies(alock);
9888
9889 AssertReturn(!i_isSnapshotMachine(),
9890 E_FAIL);
9891
9892 if (!mData->mAccessible)
9893 return setError(VBOX_E_INVALID_VM_STATE,
9894 tr("The machine is not accessible, so cannot save settings"));
9895
9896 HRESULT rc = S_OK;
9897 bool fNeedsWrite = false;
9898
9899 /* First, prepare to save settings. It will care about renaming the
9900 * settings directory and file if the machine name was changed and about
9901 * creating a new settings file if this is a new machine. */
9902 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9903 if (FAILED(rc)) return rc;
9904
9905 // keep a pointer to the current settings structures
9906 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9907 settings::MachineConfigFile *pNewConfig = NULL;
9908
9909 try
9910 {
9911 // make a fresh one to have everyone write stuff into
9912 pNewConfig = new settings::MachineConfigFile(NULL);
9913 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9914
9915 // now go and copy all the settings data from COM to the settings structures
9916 // (this calls i_saveSettings() on all the COM objects in the machine)
9917 i_copyMachineDataToSettings(*pNewConfig);
9918
9919 if (aFlags & SaveS_ResetCurStateModified)
9920 {
9921 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9922 mData->mCurrentStateModified = FALSE;
9923 fNeedsWrite = true; // always, no need to compare
9924 }
9925 else if (aFlags & SaveS_Force)
9926 {
9927 fNeedsWrite = true; // always, no need to compare
9928 }
9929 else
9930 {
9931 if (!mData->mCurrentStateModified)
9932 {
9933 // do a deep compare of the settings that we just saved with the settings
9934 // previously stored in the config file; this invokes MachineConfigFile::operator==
9935 // which does a deep compare of all the settings, which is expensive but less expensive
9936 // than writing out XML in vain
9937 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9938
9939 // could still be modified if any settings changed
9940 mData->mCurrentStateModified = fAnySettingsChanged;
9941
9942 fNeedsWrite = fAnySettingsChanged;
9943 }
9944 else
9945 fNeedsWrite = true;
9946 }
9947
9948 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9949
9950 if (fNeedsWrite)
9951 // now spit it all out!
9952 pNewConfig->write(mData->m_strConfigFileFull);
9953
9954 mData->pMachineConfigFile = pNewConfig;
9955 delete pOldConfig;
9956 i_commit();
9957
9958 // after saving settings, we are no longer different from the XML on disk
9959 mData->flModifications = 0;
9960 }
9961 catch (HRESULT err)
9962 {
9963 // we assume that error info is set by the thrower
9964 rc = err;
9965
9966 // restore old config
9967 delete pNewConfig;
9968 mData->pMachineConfigFile = pOldConfig;
9969 }
9970 catch (...)
9971 {
9972 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9973 }
9974
9975 if (fNeedsWrite)
9976 {
9977 /* Fire the data change event, even on failure (since we've already
9978 * committed all data). This is done only for SessionMachines because
9979 * mutable Machine instances are always not registered (i.e. private
9980 * to the client process that creates them) and thus don't need to
9981 * inform callbacks. */
9982 if (i_isSessionMachine())
9983 mParent->i_onMachineDataChanged(mData->mUuid);
9984 }
9985
9986 LogFlowThisFunc(("rc=%08X\n", rc));
9987 LogFlowThisFuncLeave();
9988 return rc;
9989}
9990
9991/**
9992 * Implementation for saving the machine settings into the given
9993 * settings::MachineConfigFile instance. This copies machine extradata
9994 * from the previous machine config file in the instance data, if any.
9995 *
9996 * This gets called from two locations:
9997 *
9998 * -- Machine::i_saveSettings(), during the regular XML writing;
9999 *
10000 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10001 * exported to OVF and we write the VirtualBox proprietary XML
10002 * into a <vbox:Machine> tag.
10003 *
10004 * This routine fills all the fields in there, including snapshots, *except*
10005 * for the following:
10006 *
10007 * -- fCurrentStateModified. There is some special logic associated with that.
10008 *
10009 * The caller can then call MachineConfigFile::write() or do something else
10010 * with it.
10011 *
10012 * Caller must hold the machine lock!
10013 *
10014 * This throws XML errors and HRESULT, so the caller must have a catch block!
10015 */
10016void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10017{
10018 // deep copy extradata, being extra careful with self assignment (the STL
10019 // map assignment on Mac OS X clang based Xcode isn't checking)
10020 if (&config != mData->pMachineConfigFile)
10021 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10022
10023 config.uuid = mData->mUuid;
10024
10025 // copy name, description, OS type, teleport, UTC etc.
10026 config.machineUserData = mUserData->s;
10027
10028 if ( mData->mMachineState == MachineState_Saved
10029 || mData->mMachineState == MachineState_AbortedSaved
10030 || mData->mMachineState == MachineState_Restoring
10031 // when doing certain snapshot operations we may or may not have
10032 // a saved state in the current state, so keep everything as is
10033 || ( ( mData->mMachineState == MachineState_Snapshotting
10034 || mData->mMachineState == MachineState_DeletingSnapshot
10035 || mData->mMachineState == MachineState_RestoringSnapshot)
10036 && (!mSSData->strStateFilePath.isEmpty())
10037 )
10038 )
10039 {
10040 Assert(!mSSData->strStateFilePath.isEmpty());
10041 /* try to make the file name relative to the settings file dir */
10042 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10043 }
10044 else
10045 {
10046 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10047 config.strStateFile.setNull();
10048 }
10049
10050 if (mData->mCurrentSnapshot)
10051 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10052 else
10053 config.uuidCurrentSnapshot.clear();
10054
10055 config.timeLastStateChange = mData->mLastStateChange;
10056 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10057 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10058
10059 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10060 if (FAILED(rc)) throw rc;
10061
10062 // save machine's media registry if this is VirtualBox 4.0 or later
10063 if (config.canHaveOwnMediaRegistry())
10064 {
10065 // determine machine folder
10066 Utf8Str strMachineFolder = i_getSettingsFileFull();
10067 strMachineFolder.stripFilename();
10068 mParent->i_saveMediaRegistry(config.mediaRegistry,
10069 i_getId(), // only media with registry ID == machine UUID
10070 strMachineFolder);
10071 // this throws HRESULT
10072 }
10073
10074 // save snapshots
10075 rc = i_saveAllSnapshots(config);
10076 if (FAILED(rc)) throw rc;
10077}
10078
10079/**
10080 * Saves all snapshots of the machine into the given machine config file. Called
10081 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10082 * @param config
10083 * @return
10084 */
10085HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10086{
10087 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10088
10089 HRESULT rc = S_OK;
10090
10091 try
10092 {
10093 config.llFirstSnapshot.clear();
10094
10095 if (mData->mFirstSnapshot)
10096 {
10097 // the settings use a list for "the first snapshot"
10098 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10099
10100 // get reference to the snapshot on the list and work on that
10101 // element straight in the list to avoid excessive copying later
10102 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10103 if (FAILED(rc)) throw rc;
10104 }
10105
10106// if (mType == IsSessionMachine)
10107// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10108
10109 }
10110 catch (HRESULT err)
10111 {
10112 /* we assume that error info is set by the thrower */
10113 rc = err;
10114 }
10115 catch (...)
10116 {
10117 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10118 }
10119
10120 return rc;
10121}
10122
10123/**
10124 * Saves the VM hardware configuration. It is assumed that the
10125 * given node is empty.
10126 *
10127 * @param data Reference to the settings object for the hardware config.
10128 * @param pDbg Pointer to the settings object for the debugging config
10129 * which happens to live in mHWData.
10130 * @param pAutostart Pointer to the settings object for the autostart config
10131 * which happens to live in mHWData.
10132 */
10133HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10134 settings::Autostart *pAutostart)
10135{
10136 HRESULT rc = S_OK;
10137
10138 try
10139 {
10140 /* The hardware version attribute (optional).
10141 Automatically upgrade from 1 to current default hardware version
10142 when there is no saved state. (ugly!) */
10143 if ( mHWData->mHWVersion == "1"
10144 && mSSData->strStateFilePath.isEmpty()
10145 )
10146 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10147
10148 data.strVersion = mHWData->mHWVersion;
10149 data.uuid = mHWData->mHardwareUUID;
10150
10151 // CPU
10152 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10153 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10154 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10155 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10156 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10157 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10158 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10159 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10160 data.fPAE = !!mHWData->mPAEEnabled;
10161 data.enmLongMode = mHWData->mLongMode;
10162 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10163 data.fAPIC = !!mHWData->mAPIC;
10164 data.fX2APIC = !!mHWData->mX2APIC;
10165 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10166 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10167 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10168 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10169 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10170 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10171 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10172 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10173 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10174 data.cCPUs = mHWData->mCPUCount;
10175 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10176 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10177 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10178 data.strCpuProfile = mHWData->mCpuProfile;
10179
10180 data.llCpus.clear();
10181 if (data.fCpuHotPlug)
10182 {
10183 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10184 {
10185 if (mHWData->mCPUAttached[idx])
10186 {
10187 settings::Cpu cpu;
10188 cpu.ulId = idx;
10189 data.llCpus.push_back(cpu);
10190 }
10191 }
10192 }
10193
10194 /* Standard and Extended CPUID leafs. */
10195 data.llCpuIdLeafs.clear();
10196 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10197
10198 // memory
10199 data.ulMemorySizeMB = mHWData->mMemorySize;
10200 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10201
10202 // firmware
10203 data.firmwareType = mHWData->mFirmwareType;
10204
10205 // HID
10206 data.pointingHIDType = mHWData->mPointingHIDType;
10207 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10208
10209 // chipset
10210 data.chipsetType = mHWData->mChipsetType;
10211
10212 // iommu
10213 data.iommuType = mHWData->mIommuType;
10214
10215 // paravirt
10216 data.paravirtProvider = mHWData->mParavirtProvider;
10217 data.strParavirtDebug = mHWData->mParavirtDebug;
10218
10219 // emulated USB card reader
10220 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10221
10222 // HPET
10223 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10224
10225 // boot order
10226 data.mapBootOrder.clear();
10227 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10228 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10229
10230 /* VRDEServer settings (optional) */
10231 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10232 if (FAILED(rc)) throw rc;
10233
10234 /* BIOS settings (required) */
10235 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10236 if (FAILED(rc)) throw rc;
10237
10238 /* Trusted Platform Module settings (required) */
10239 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10240 if (FAILED(rc)) throw rc;
10241
10242 /* NVRAM settings (required) */
10243 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10244 if (FAILED(rc)) throw rc;
10245
10246 /* Recording settings (required) */
10247 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10248 if (FAILED(rc)) throw rc;
10249
10250 /* GraphicsAdapter settings (required) */
10251 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10252 if (FAILED(rc)) throw rc;
10253
10254 /* USB Controller (required) */
10255 data.usbSettings.llUSBControllers.clear();
10256 for (USBControllerList::const_iterator
10257 it = mUSBControllers->begin();
10258 it != mUSBControllers->end();
10259 ++it)
10260 {
10261 ComObjPtr<USBController> ctrl = *it;
10262 settings::USBController settingsCtrl;
10263
10264 settingsCtrl.strName = ctrl->i_getName();
10265 settingsCtrl.enmType = ctrl->i_getControllerType();
10266
10267 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10268 }
10269
10270 /* USB device filters (required) */
10271 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10272 if (FAILED(rc)) throw rc;
10273
10274 /* Network adapters (required) */
10275 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10276 data.llNetworkAdapters.clear();
10277 /* Write out only the nominal number of network adapters for this
10278 * chipset type. Since Machine::commit() hasn't been called there
10279 * may be extra NIC settings in the vector. */
10280 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10281 {
10282 settings::NetworkAdapter nic;
10283 nic.ulSlot = (uint32_t)slot;
10284 /* paranoia check... must not be NULL, but must not crash either. */
10285 if (mNetworkAdapters[slot])
10286 {
10287 if (mNetworkAdapters[slot]->i_hasDefaults())
10288 continue;
10289
10290 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10291 if (FAILED(rc)) throw rc;
10292
10293 data.llNetworkAdapters.push_back(nic);
10294 }
10295 }
10296
10297 /* Serial ports */
10298 data.llSerialPorts.clear();
10299 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10300 {
10301 if (mSerialPorts[slot]->i_hasDefaults())
10302 continue;
10303
10304 settings::SerialPort s;
10305 s.ulSlot = slot;
10306 rc = mSerialPorts[slot]->i_saveSettings(s);
10307 if (FAILED(rc)) return rc;
10308
10309 data.llSerialPorts.push_back(s);
10310 }
10311
10312 /* Parallel ports */
10313 data.llParallelPorts.clear();
10314 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10315 {
10316 if (mParallelPorts[slot]->i_hasDefaults())
10317 continue;
10318
10319 settings::ParallelPort p;
10320 p.ulSlot = slot;
10321 rc = mParallelPorts[slot]->i_saveSettings(p);
10322 if (FAILED(rc)) return rc;
10323
10324 data.llParallelPorts.push_back(p);
10325 }
10326
10327 /* Audio adapter */
10328 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10329 if (FAILED(rc)) return rc;
10330
10331 rc = i_saveStorageControllers(data.storage);
10332 if (FAILED(rc)) return rc;
10333
10334 /* Shared folders */
10335 data.llSharedFolders.clear();
10336 for (HWData::SharedFolderList::const_iterator
10337 it = mHWData->mSharedFolders.begin();
10338 it != mHWData->mSharedFolders.end();
10339 ++it)
10340 {
10341 SharedFolder *pSF = *it;
10342 AutoCaller sfCaller(pSF);
10343 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10344 settings::SharedFolder sf;
10345 sf.strName = pSF->i_getName();
10346 sf.strHostPath = pSF->i_getHostPath();
10347 sf.fWritable = !!pSF->i_isWritable();
10348 sf.fAutoMount = !!pSF->i_isAutoMounted();
10349 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10350
10351 data.llSharedFolders.push_back(sf);
10352 }
10353
10354 // clipboard
10355 data.clipboardMode = mHWData->mClipboardMode;
10356 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10357
10358 // drag'n'drop
10359 data.dndMode = mHWData->mDnDMode;
10360
10361 /* Guest */
10362 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10363
10364 // IO settings
10365 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10366 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10367
10368 /* BandwidthControl (required) */
10369 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10370 if (FAILED(rc)) throw rc;
10371
10372 /* Host PCI devices */
10373 data.pciAttachments.clear();
10374 for (HWData::PCIDeviceAssignmentList::const_iterator
10375 it = mHWData->mPCIDeviceAssignments.begin();
10376 it != mHWData->mPCIDeviceAssignments.end();
10377 ++it)
10378 {
10379 ComObjPtr<PCIDeviceAttachment> pda = *it;
10380 settings::HostPCIDeviceAttachment hpda;
10381
10382 rc = pda->i_saveSettings(hpda);
10383 if (FAILED(rc)) throw rc;
10384
10385 data.pciAttachments.push_back(hpda);
10386 }
10387
10388 // guest properties
10389 data.llGuestProperties.clear();
10390#ifdef VBOX_WITH_GUEST_PROPS
10391 for (HWData::GuestPropertyMap::const_iterator
10392 it = mHWData->mGuestProperties.begin();
10393 it != mHWData->mGuestProperties.end();
10394 ++it)
10395 {
10396 HWData::GuestProperty property = it->second;
10397
10398 /* Remove transient guest properties at shutdown unless we
10399 * are saving state. Note that restoring snapshot intentionally
10400 * keeps them, they will be removed if appropriate once the final
10401 * machine state is set (as crashes etc. need to work). */
10402 if ( ( mData->mMachineState == MachineState_PoweredOff
10403 || mData->mMachineState == MachineState_Aborted
10404 || mData->mMachineState == MachineState_Teleported)
10405 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10406 continue;
10407 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10408 prop.strName = it->first;
10409 prop.strValue = property.strValue;
10410 prop.timestamp = (uint64_t)property.mTimestamp;
10411 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10412 GuestPropWriteFlags(property.mFlags, szFlags);
10413 prop.strFlags = szFlags;
10414
10415 data.llGuestProperties.push_back(prop);
10416 }
10417
10418 /* I presume this doesn't require a backup(). */
10419 mData->mGuestPropertiesModified = FALSE;
10420#endif /* VBOX_WITH_GUEST_PROPS defined */
10421
10422 *pDbg = mHWData->mDebugging;
10423 *pAutostart = mHWData->mAutostart;
10424
10425 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10426 }
10427 catch (std::bad_alloc &)
10428 {
10429 return E_OUTOFMEMORY;
10430 }
10431
10432 AssertComRC(rc);
10433 return rc;
10434}
10435
10436/**
10437 * Saves the storage controller configuration.
10438 *
10439 * @param data storage settings.
10440 */
10441HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10442{
10443 data.llStorageControllers.clear();
10444
10445 for (StorageControllerList::const_iterator
10446 it = mStorageControllers->begin();
10447 it != mStorageControllers->end();
10448 ++it)
10449 {
10450 HRESULT rc;
10451 ComObjPtr<StorageController> pCtl = *it;
10452
10453 settings::StorageController ctl;
10454 ctl.strName = pCtl->i_getName();
10455 ctl.controllerType = pCtl->i_getControllerType();
10456 ctl.storageBus = pCtl->i_getStorageBus();
10457 ctl.ulInstance = pCtl->i_getInstance();
10458 ctl.fBootable = pCtl->i_getBootable();
10459
10460 /* Save the port count. */
10461 ULONG portCount;
10462 rc = pCtl->COMGETTER(PortCount)(&portCount);
10463 ComAssertComRCRet(rc, rc);
10464 ctl.ulPortCount = portCount;
10465
10466 /* Save fUseHostIOCache */
10467 BOOL fUseHostIOCache;
10468 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10469 ComAssertComRCRet(rc, rc);
10470 ctl.fUseHostIOCache = !!fUseHostIOCache;
10471
10472 /* save the devices now. */
10473 rc = i_saveStorageDevices(pCtl, ctl);
10474 ComAssertComRCRet(rc, rc);
10475
10476 data.llStorageControllers.push_back(ctl);
10477 }
10478
10479 return S_OK;
10480}
10481
10482/**
10483 * Saves the hard disk configuration.
10484 */
10485HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10486 settings::StorageController &data)
10487{
10488 MediumAttachmentList atts;
10489
10490 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10491 if (FAILED(rc)) return rc;
10492
10493 data.llAttachedDevices.clear();
10494 for (MediumAttachmentList::const_iterator
10495 it = atts.begin();
10496 it != atts.end();
10497 ++it)
10498 {
10499 settings::AttachedDevice dev;
10500 IMediumAttachment *iA = *it;
10501 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10502 Medium *pMedium = pAttach->i_getMedium();
10503
10504 dev.deviceType = pAttach->i_getType();
10505 dev.lPort = pAttach->i_getPort();
10506 dev.lDevice = pAttach->i_getDevice();
10507 dev.fPassThrough = pAttach->i_getPassthrough();
10508 dev.fHotPluggable = pAttach->i_getHotPluggable();
10509 if (pMedium)
10510 {
10511 if (pMedium->i_isHostDrive())
10512 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10513 else
10514 dev.uuid = pMedium->i_getId();
10515 dev.fTempEject = pAttach->i_getTempEject();
10516 dev.fNonRotational = pAttach->i_getNonRotational();
10517 dev.fDiscard = pAttach->i_getDiscard();
10518 }
10519
10520 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10521
10522 data.llAttachedDevices.push_back(dev);
10523 }
10524
10525 return S_OK;
10526}
10527
10528/**
10529 * Saves machine state settings as defined by aFlags
10530 * (SaveSTS_* values).
10531 *
10532 * @param aFlags Combination of SaveSTS_* flags.
10533 *
10534 * @note Locks objects for writing.
10535 */
10536HRESULT Machine::i_saveStateSettings(int aFlags)
10537{
10538 if (aFlags == 0)
10539 return S_OK;
10540
10541 AutoCaller autoCaller(this);
10542 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10543
10544 /* This object's write lock is also necessary to serialize file access
10545 * (prevent concurrent reads and writes) */
10546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10547
10548 HRESULT rc = S_OK;
10549
10550 Assert(mData->pMachineConfigFile);
10551
10552 try
10553 {
10554 if (aFlags & SaveSTS_CurStateModified)
10555 mData->pMachineConfigFile->fCurrentStateModified = true;
10556
10557 if (aFlags & SaveSTS_StateFilePath)
10558 {
10559 if (!mSSData->strStateFilePath.isEmpty())
10560 /* try to make the file name relative to the settings file dir */
10561 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10562 else
10563 mData->pMachineConfigFile->strStateFile.setNull();
10564 }
10565
10566 if (aFlags & SaveSTS_StateTimeStamp)
10567 {
10568 Assert( mData->mMachineState != MachineState_Aborted
10569 || mSSData->strStateFilePath.isEmpty());
10570
10571 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10572
10573 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10574 || mData->mMachineState == MachineState_AbortedSaved);
10575/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10576 }
10577
10578 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10579 }
10580 catch (...)
10581 {
10582 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10583 }
10584
10585 return rc;
10586}
10587
10588/**
10589 * Ensures that the given medium is added to a media registry. If this machine
10590 * was created with 4.0 or later, then the machine registry is used. Otherwise
10591 * the global VirtualBox media registry is used.
10592 *
10593 * Caller must NOT hold machine lock, media tree or any medium locks!
10594 *
10595 * @param pMedium
10596 */
10597void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10598{
10599 /* Paranoia checks: do not hold machine or media tree locks. */
10600 AssertReturnVoid(!isWriteLockOnCurrentThread());
10601 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10602
10603 ComObjPtr<Medium> pBase;
10604 {
10605 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10606 pBase = pMedium->i_getBase();
10607 }
10608
10609 /* Paranoia checks: do not hold medium locks. */
10610 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10611 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10612
10613 // decide which medium registry to use now that the medium is attached:
10614 Guid uuid;
10615 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10616 if (fCanHaveOwnMediaRegistry)
10617 // machine XML is VirtualBox 4.0 or higher:
10618 uuid = i_getId(); // machine UUID
10619 else
10620 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10621
10622 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10623 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10624 if (pMedium->i_addRegistry(uuid))
10625 mParent->i_markRegistryModified(uuid);
10626
10627 /* For more complex hard disk structures it can happen that the base
10628 * medium isn't yet associated with any medium registry. Do that now. */
10629 if (pMedium != pBase)
10630 {
10631 /* Tree lock needed by Medium::addRegistry when recursing. */
10632 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10633 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10634 {
10635 treeLock.release();
10636 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10637 treeLock.acquire();
10638 }
10639 if (pBase->i_addRegistryRecursive(uuid))
10640 {
10641 treeLock.release();
10642 mParent->i_markRegistryModified(uuid);
10643 }
10644 }
10645}
10646
10647/**
10648 * Creates differencing hard disks for all normal hard disks attached to this
10649 * machine and a new set of attachments to refer to created disks.
10650 *
10651 * Used when taking a snapshot or when deleting the current state. Gets called
10652 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10653 *
10654 * This method assumes that mMediumAttachments contains the original hard disk
10655 * attachments it needs to create diffs for. On success, these attachments will
10656 * be replaced with the created diffs.
10657 *
10658 * Attachments with non-normal hard disks are left as is.
10659 *
10660 * If @a aOnline is @c false then the original hard disks that require implicit
10661 * diffs will be locked for reading. Otherwise it is assumed that they are
10662 * already locked for writing (when the VM was started). Note that in the latter
10663 * case it is responsibility of the caller to lock the newly created diffs for
10664 * writing if this method succeeds.
10665 *
10666 * @param aProgress Progress object to run (must contain at least as
10667 * many operations left as the number of hard disks
10668 * attached).
10669 * @param aWeight Weight of this operation.
10670 * @param aOnline Whether the VM was online prior to this operation.
10671 *
10672 * @note The progress object is not marked as completed, neither on success nor
10673 * on failure. This is a responsibility of the caller.
10674 *
10675 * @note Locks this object and the media tree for writing.
10676 */
10677HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10678 ULONG aWeight,
10679 bool aOnline)
10680{
10681 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10682
10683 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10684 AssertReturn(!!pProgressControl, E_INVALIDARG);
10685
10686 AutoCaller autoCaller(this);
10687 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10688
10689 AutoMultiWriteLock2 alock(this->lockHandle(),
10690 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10691
10692 /* must be in a protective state because we release the lock below */
10693 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10694 || mData->mMachineState == MachineState_OnlineSnapshotting
10695 || mData->mMachineState == MachineState_LiveSnapshotting
10696 || mData->mMachineState == MachineState_RestoringSnapshot
10697 || mData->mMachineState == MachineState_DeletingSnapshot
10698 , E_FAIL);
10699
10700 HRESULT rc = S_OK;
10701
10702 // use appropriate locked media map (online or offline)
10703 MediumLockListMap lockedMediaOffline;
10704 MediumLockListMap *lockedMediaMap;
10705 if (aOnline)
10706 lockedMediaMap = &mData->mSession.mLockedMedia;
10707 else
10708 lockedMediaMap = &lockedMediaOffline;
10709
10710 try
10711 {
10712 if (!aOnline)
10713 {
10714 /* lock all attached hard disks early to detect "in use"
10715 * situations before creating actual diffs */
10716 for (MediumAttachmentList::const_iterator
10717 it = mMediumAttachments->begin();
10718 it != mMediumAttachments->end();
10719 ++it)
10720 {
10721 MediumAttachment *pAtt = *it;
10722 if (pAtt->i_getType() == DeviceType_HardDisk)
10723 {
10724 Medium *pMedium = pAtt->i_getMedium();
10725 Assert(pMedium);
10726
10727 MediumLockList *pMediumLockList(new MediumLockList());
10728 alock.release();
10729 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10730 NULL /* pToLockWrite */,
10731 false /* fMediumLockWriteAll */,
10732 NULL,
10733 *pMediumLockList);
10734 alock.acquire();
10735 if (FAILED(rc))
10736 {
10737 delete pMediumLockList;
10738 throw rc;
10739 }
10740 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10741 if (FAILED(rc))
10742 {
10743 throw setError(rc,
10744 tr("Collecting locking information for all attached media failed"));
10745 }
10746 }
10747 }
10748
10749 /* Now lock all media. If this fails, nothing is locked. */
10750 alock.release();
10751 rc = lockedMediaMap->Lock();
10752 alock.acquire();
10753 if (FAILED(rc))
10754 {
10755 throw setError(rc,
10756 tr("Locking of attached media failed"));
10757 }
10758 }
10759
10760 /* remember the current list (note that we don't use backup() since
10761 * mMediumAttachments may be already backed up) */
10762 MediumAttachmentList atts = *mMediumAttachments.data();
10763
10764 /* start from scratch */
10765 mMediumAttachments->clear();
10766
10767 /* go through remembered attachments and create diffs for normal hard
10768 * disks and attach them */
10769 for (MediumAttachmentList::const_iterator
10770 it = atts.begin();
10771 it != atts.end();
10772 ++it)
10773 {
10774 MediumAttachment *pAtt = *it;
10775
10776 DeviceType_T devType = pAtt->i_getType();
10777 Medium *pMedium = pAtt->i_getMedium();
10778
10779 if ( devType != DeviceType_HardDisk
10780 || pMedium == NULL
10781 || pMedium->i_getType() != MediumType_Normal)
10782 {
10783 /* copy the attachment as is */
10784
10785 /** @todo the progress object created in SessionMachine::TakeSnaphot
10786 * only expects operations for hard disks. Later other
10787 * device types need to show up in the progress as well. */
10788 if (devType == DeviceType_HardDisk)
10789 {
10790 if (pMedium == NULL)
10791 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10792 aWeight); // weight
10793 else
10794 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10795 pMedium->i_getBase()->i_getName().c_str()).raw(),
10796 aWeight); // weight
10797 }
10798
10799 mMediumAttachments->push_back(pAtt);
10800 continue;
10801 }
10802
10803 /* need a diff */
10804 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10805 pMedium->i_getBase()->i_getName().c_str()).raw(),
10806 aWeight); // weight
10807
10808 Utf8Str strFullSnapshotFolder;
10809 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10810
10811 ComObjPtr<Medium> diff;
10812 diff.createObject();
10813 // store the diff in the same registry as the parent
10814 // (this cannot fail here because we can't create implicit diffs for
10815 // unregistered images)
10816 Guid uuidRegistryParent;
10817 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10818 Assert(fInRegistry); NOREF(fInRegistry);
10819 rc = diff->init(mParent,
10820 pMedium->i_getPreferredDiffFormat(),
10821 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10822 uuidRegistryParent,
10823 DeviceType_HardDisk);
10824 if (FAILED(rc)) throw rc;
10825
10826 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10827 * the push_back? Looks like we're going to release medium with the
10828 * wrong kind of lock (general issue with if we fail anywhere at all)
10829 * and an orphaned VDI in the snapshots folder. */
10830
10831 /* update the appropriate lock list */
10832 MediumLockList *pMediumLockList;
10833 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10834 AssertComRCThrowRC(rc);
10835 if (aOnline)
10836 {
10837 alock.release();
10838 /* The currently attached medium will be read-only, change
10839 * the lock type to read. */
10840 rc = pMediumLockList->Update(pMedium, false);
10841 alock.acquire();
10842 AssertComRCThrowRC(rc);
10843 }
10844
10845 /* release the locks before the potentially lengthy operation */
10846 alock.release();
10847 rc = pMedium->i_createDiffStorage(diff,
10848 pMedium->i_getPreferredDiffVariant(),
10849 pMediumLockList,
10850 NULL /* aProgress */,
10851 true /* aWait */,
10852 false /* aNotify */);
10853 alock.acquire();
10854 if (FAILED(rc)) throw rc;
10855
10856 /* actual lock list update is done in Machine::i_commitMedia */
10857
10858 rc = diff->i_addBackReference(mData->mUuid);
10859 AssertComRCThrowRC(rc);
10860
10861 /* add a new attachment */
10862 ComObjPtr<MediumAttachment> attachment;
10863 attachment.createObject();
10864 rc = attachment->init(this,
10865 diff,
10866 pAtt->i_getControllerName(),
10867 pAtt->i_getPort(),
10868 pAtt->i_getDevice(),
10869 DeviceType_HardDisk,
10870 true /* aImplicit */,
10871 false /* aPassthrough */,
10872 false /* aTempEject */,
10873 pAtt->i_getNonRotational(),
10874 pAtt->i_getDiscard(),
10875 pAtt->i_getHotPluggable(),
10876 pAtt->i_getBandwidthGroup());
10877 if (FAILED(rc)) throw rc;
10878
10879 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10880 AssertComRCThrowRC(rc);
10881 mMediumAttachments->push_back(attachment);
10882 }
10883 }
10884 catch (HRESULT aRC) { rc = aRC; }
10885
10886 /* unlock all hard disks we locked when there is no VM */
10887 if (!aOnline)
10888 {
10889 ErrorInfoKeeper eik;
10890
10891 HRESULT rc1 = lockedMediaMap->Clear();
10892 AssertComRC(rc1);
10893 }
10894
10895 return rc;
10896}
10897
10898/**
10899 * Deletes implicit differencing hard disks created either by
10900 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10901 * mMediumAttachments.
10902 *
10903 * Note that to delete hard disks created by #attachDevice() this method is
10904 * called from #i_rollbackMedia() when the changes are rolled back.
10905 *
10906 * @note Locks this object and the media tree for writing.
10907 */
10908HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10909{
10910 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10911
10912 AutoCaller autoCaller(this);
10913 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10914
10915 AutoMultiWriteLock2 alock(this->lockHandle(),
10916 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10917
10918 /* We absolutely must have backed up state. */
10919 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10920
10921 /* Check if there are any implicitly created diff images. */
10922 bool fImplicitDiffs = false;
10923 for (MediumAttachmentList::const_iterator
10924 it = mMediumAttachments->begin();
10925 it != mMediumAttachments->end();
10926 ++it)
10927 {
10928 const ComObjPtr<MediumAttachment> &pAtt = *it;
10929 if (pAtt->i_isImplicit())
10930 {
10931 fImplicitDiffs = true;
10932 break;
10933 }
10934 }
10935 /* If there is nothing to do, leave early. This saves lots of image locking
10936 * effort. It also avoids a MachineStateChanged event without real reason.
10937 * This is important e.g. when loading a VM config, because there should be
10938 * no events. Otherwise API clients can become thoroughly confused for
10939 * inaccessible VMs (the code for loading VM configs uses this method for
10940 * cleanup if the config makes no sense), as they take such events as an
10941 * indication that the VM is alive, and they would force the VM config to
10942 * be reread, leading to an endless loop. */
10943 if (!fImplicitDiffs)
10944 return S_OK;
10945
10946 HRESULT rc = S_OK;
10947 MachineState_T oldState = mData->mMachineState;
10948
10949 /* will release the lock before the potentially lengthy operation,
10950 * so protect with the special state (unless already protected) */
10951 if ( oldState != MachineState_Snapshotting
10952 && oldState != MachineState_OnlineSnapshotting
10953 && oldState != MachineState_LiveSnapshotting
10954 && oldState != MachineState_RestoringSnapshot
10955 && oldState != MachineState_DeletingSnapshot
10956 && oldState != MachineState_DeletingSnapshotOnline
10957 && oldState != MachineState_DeletingSnapshotPaused
10958 )
10959 i_setMachineState(MachineState_SettingUp);
10960
10961 // use appropriate locked media map (online or offline)
10962 MediumLockListMap lockedMediaOffline;
10963 MediumLockListMap *lockedMediaMap;
10964 if (aOnline)
10965 lockedMediaMap = &mData->mSession.mLockedMedia;
10966 else
10967 lockedMediaMap = &lockedMediaOffline;
10968
10969 try
10970 {
10971 if (!aOnline)
10972 {
10973 /* lock all attached hard disks early to detect "in use"
10974 * situations before deleting actual diffs */
10975 for (MediumAttachmentList::const_iterator
10976 it = mMediumAttachments->begin();
10977 it != mMediumAttachments->end();
10978 ++it)
10979 {
10980 MediumAttachment *pAtt = *it;
10981 if (pAtt->i_getType() == DeviceType_HardDisk)
10982 {
10983 Medium *pMedium = pAtt->i_getMedium();
10984 Assert(pMedium);
10985
10986 MediumLockList *pMediumLockList(new MediumLockList());
10987 alock.release();
10988 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10989 NULL /* pToLockWrite */,
10990 false /* fMediumLockWriteAll */,
10991 NULL,
10992 *pMediumLockList);
10993 alock.acquire();
10994
10995 if (FAILED(rc))
10996 {
10997 delete pMediumLockList;
10998 throw rc;
10999 }
11000
11001 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11002 if (FAILED(rc))
11003 throw rc;
11004 }
11005 }
11006
11007 if (FAILED(rc))
11008 throw rc;
11009 } // end of offline
11010
11011 /* Lock lists are now up to date and include implicitly created media */
11012
11013 /* Go through remembered attachments and delete all implicitly created
11014 * diffs and fix up the attachment information */
11015 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11016 MediumAttachmentList implicitAtts;
11017 for (MediumAttachmentList::const_iterator
11018 it = mMediumAttachments->begin();
11019 it != mMediumAttachments->end();
11020 ++it)
11021 {
11022 ComObjPtr<MediumAttachment> pAtt = *it;
11023 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11024 if (pMedium.isNull())
11025 continue;
11026
11027 // Implicit attachments go on the list for deletion and back references are removed.
11028 if (pAtt->i_isImplicit())
11029 {
11030 /* Deassociate and mark for deletion */
11031 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11032 rc = pMedium->i_removeBackReference(mData->mUuid);
11033 if (FAILED(rc))
11034 throw rc;
11035 implicitAtts.push_back(pAtt);
11036 continue;
11037 }
11038
11039 /* Was this medium attached before? */
11040 if (!i_findAttachment(oldAtts, pMedium))
11041 {
11042 /* no: de-associate */
11043 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11044 rc = pMedium->i_removeBackReference(mData->mUuid);
11045 if (FAILED(rc))
11046 throw rc;
11047 continue;
11048 }
11049 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11050 }
11051
11052 /* If there are implicit attachments to delete, throw away the lock
11053 * map contents (which will unlock all media) since the medium
11054 * attachments will be rolled back. Below we need to completely
11055 * recreate the lock map anyway since it is infinitely complex to
11056 * do this incrementally (would need reconstructing each attachment
11057 * change, which would be extremely hairy). */
11058 if (implicitAtts.size() != 0)
11059 {
11060 ErrorInfoKeeper eik;
11061
11062 HRESULT rc1 = lockedMediaMap->Clear();
11063 AssertComRC(rc1);
11064 }
11065
11066 /* rollback hard disk changes */
11067 mMediumAttachments.rollback();
11068
11069 MultiResult mrc(S_OK);
11070
11071 // Delete unused implicit diffs.
11072 if (implicitAtts.size() != 0)
11073 {
11074 alock.release();
11075
11076 for (MediumAttachmentList::const_iterator
11077 it = implicitAtts.begin();
11078 it != implicitAtts.end();
11079 ++it)
11080 {
11081 // Remove medium associated with this attachment.
11082 ComObjPtr<MediumAttachment> pAtt = *it;
11083 Assert(pAtt);
11084 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11085 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11086 Assert(pMedium);
11087
11088 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11089 // continue on delete failure, just collect error messages
11090 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11091 pMedium->i_getLocationFull().c_str() ));
11092 mrc = rc;
11093 }
11094 // Clear the list of deleted implicit attachments now, while not
11095 // holding the lock, as it will ultimately trigger Medium::uninit()
11096 // calls which assume that the media tree lock isn't held.
11097 implicitAtts.clear();
11098
11099 alock.acquire();
11100
11101 /* if there is a VM recreate media lock map as mentioned above,
11102 * otherwise it is a waste of time and we leave things unlocked */
11103 if (aOnline)
11104 {
11105 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11106 /* must never be NULL, but better safe than sorry */
11107 if (!pMachine.isNull())
11108 {
11109 alock.release();
11110 rc = mData->mSession.mMachine->i_lockMedia();
11111 alock.acquire();
11112 if (FAILED(rc))
11113 throw rc;
11114 }
11115 }
11116 }
11117 }
11118 catch (HRESULT aRC) {rc = aRC;}
11119
11120 if (mData->mMachineState == MachineState_SettingUp)
11121 i_setMachineState(oldState);
11122
11123 /* unlock all hard disks we locked when there is no VM */
11124 if (!aOnline)
11125 {
11126 ErrorInfoKeeper eik;
11127
11128 HRESULT rc1 = lockedMediaMap->Clear();
11129 AssertComRC(rc1);
11130 }
11131
11132 return rc;
11133}
11134
11135
11136/**
11137 * Looks through the given list of media attachments for one with the given parameters
11138 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11139 * can be searched as well if needed.
11140 *
11141 * @param ll
11142 * @param aControllerName
11143 * @param aControllerPort
11144 * @param aDevice
11145 * @return
11146 */
11147MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11148 const Utf8Str &aControllerName,
11149 LONG aControllerPort,
11150 LONG aDevice)
11151{
11152 for (MediumAttachmentList::const_iterator
11153 it = ll.begin();
11154 it != ll.end();
11155 ++it)
11156 {
11157 MediumAttachment *pAttach = *it;
11158 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11159 return pAttach;
11160 }
11161
11162 return NULL;
11163}
11164
11165/**
11166 * Looks through the given list of media attachments for one with the given parameters
11167 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11168 * can be searched as well if needed.
11169 *
11170 * @param ll
11171 * @param pMedium
11172 * @return
11173 */
11174MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11175 ComObjPtr<Medium> pMedium)
11176{
11177 for (MediumAttachmentList::const_iterator
11178 it = ll.begin();
11179 it != ll.end();
11180 ++it)
11181 {
11182 MediumAttachment *pAttach = *it;
11183 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11184 if (pMediumThis == pMedium)
11185 return pAttach;
11186 }
11187
11188 return NULL;
11189}
11190
11191/**
11192 * Looks through the given list of media attachments for one with the given parameters
11193 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11194 * can be searched as well if needed.
11195 *
11196 * @param ll
11197 * @param id
11198 * @return
11199 */
11200MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11201 Guid &id)
11202{
11203 for (MediumAttachmentList::const_iterator
11204 it = ll.begin();
11205 it != ll.end();
11206 ++it)
11207 {
11208 MediumAttachment *pAttach = *it;
11209 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11210 if (pMediumThis->i_getId() == id)
11211 return pAttach;
11212 }
11213
11214 return NULL;
11215}
11216
11217/**
11218 * Main implementation for Machine::DetachDevice. This also gets called
11219 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11220 *
11221 * @param pAttach Medium attachment to detach.
11222 * @param writeLock Machine write lock which the caller must have locked once.
11223 * This may be released temporarily in here.
11224 * @param pSnapshot If NULL, then the detachment is for the current machine.
11225 * Otherwise this is for a SnapshotMachine, and this must be
11226 * its snapshot.
11227 * @return
11228 */
11229HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11230 AutoWriteLock &writeLock,
11231 Snapshot *pSnapshot)
11232{
11233 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11234 DeviceType_T mediumType = pAttach->i_getType();
11235
11236 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11237
11238 if (pAttach->i_isImplicit())
11239 {
11240 /* attempt to implicitly delete the implicitly created diff */
11241
11242 /// @todo move the implicit flag from MediumAttachment to Medium
11243 /// and forbid any hard disk operation when it is implicit. Or maybe
11244 /// a special media state for it to make it even more simple.
11245
11246 Assert(mMediumAttachments.isBackedUp());
11247
11248 /* will release the lock before the potentially lengthy operation, so
11249 * protect with the special state */
11250 MachineState_T oldState = mData->mMachineState;
11251 i_setMachineState(MachineState_SettingUp);
11252
11253 writeLock.release();
11254
11255 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11256 true /*aWait*/,
11257 false /*aNotify*/);
11258
11259 writeLock.acquire();
11260
11261 i_setMachineState(oldState);
11262
11263 if (FAILED(rc)) return rc;
11264 }
11265
11266 i_setModified(IsModified_Storage);
11267 mMediumAttachments.backup();
11268 mMediumAttachments->remove(pAttach);
11269
11270 if (!oldmedium.isNull())
11271 {
11272 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11273 if (pSnapshot)
11274 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11275 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11276 else if (mediumType != DeviceType_HardDisk)
11277 oldmedium->i_removeBackReference(mData->mUuid);
11278 }
11279
11280 return S_OK;
11281}
11282
11283/**
11284 * Goes thru all media of the given list and
11285 *
11286 * 1) calls i_detachDevice() on each of them for this machine and
11287 * 2) adds all Medium objects found in the process to the given list,
11288 * depending on cleanupMode.
11289 *
11290 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11291 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11292 * media to the list.
11293 *
11294 * This gets called from Machine::Unregister, both for the actual Machine and
11295 * the SnapshotMachine objects that might be found in the snapshots.
11296 *
11297 * Requires caller and locking. The machine lock must be passed in because it
11298 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11299 *
11300 * @param writeLock Machine lock from top-level caller; this gets passed to
11301 * i_detachDevice.
11302 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11303 * object if called for a SnapshotMachine.
11304 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11305 * added to llMedia; if Full, then all media get added;
11306 * otherwise no media get added.
11307 * @param llMedia Caller's list to receive Medium objects which got detached so
11308 * caller can close() them, depending on cleanupMode.
11309 * @return
11310 */
11311HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11312 Snapshot *pSnapshot,
11313 CleanupMode_T cleanupMode,
11314 MediaList &llMedia)
11315{
11316 Assert(isWriteLockOnCurrentThread());
11317
11318 HRESULT rc;
11319
11320 // make a temporary list because i_detachDevice invalidates iterators into
11321 // mMediumAttachments
11322 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11323
11324 for (MediumAttachmentList::iterator
11325 it = llAttachments2.begin();
11326 it != llAttachments2.end();
11327 ++it)
11328 {
11329 ComObjPtr<MediumAttachment> &pAttach = *it;
11330 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11331
11332 if (!pMedium.isNull())
11333 {
11334 AutoCaller mac(pMedium);
11335 if (FAILED(mac.rc())) return mac.rc();
11336 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11337 DeviceType_T devType = pMedium->i_getDeviceType();
11338 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11339 && devType == DeviceType_HardDisk)
11340 || (cleanupMode == CleanupMode_Full)
11341 )
11342 {
11343 llMedia.push_back(pMedium);
11344 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11345 /* Not allowed to keep this lock as below we need the parent
11346 * medium lock, and the lock order is parent to child. */
11347 lock.release();
11348 /*
11349 * Search for medias which are not attached to any machine, but
11350 * in the chain to an attached disk. Mediums are only consided
11351 * if they are:
11352 * - have only one child
11353 * - no references to any machines
11354 * - are of normal medium type
11355 */
11356 while (!pParent.isNull())
11357 {
11358 AutoCaller mac1(pParent);
11359 if (FAILED(mac1.rc())) return mac1.rc();
11360 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11361 if (pParent->i_getChildren().size() == 1)
11362 {
11363 if ( pParent->i_getMachineBackRefCount() == 0
11364 && pParent->i_getType() == MediumType_Normal
11365 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11366 llMedia.push_back(pParent);
11367 }
11368 else
11369 break;
11370 pParent = pParent->i_getParent();
11371 }
11372 }
11373 }
11374
11375 // real machine: then we need to use the proper method
11376 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11377
11378 if (FAILED(rc))
11379 return rc;
11380 }
11381
11382 return S_OK;
11383}
11384
11385/**
11386 * Perform deferred hard disk detachments.
11387 *
11388 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11389 * changed (not backed up).
11390 *
11391 * If @a aOnline is @c true then this method will also unlock the old hard
11392 * disks for which the new implicit diffs were created and will lock these new
11393 * diffs for writing.
11394 *
11395 * @param aOnline Whether the VM was online prior to this operation.
11396 *
11397 * @note Locks this object for writing!
11398 */
11399void Machine::i_commitMedia(bool aOnline /*= false*/)
11400{
11401 AutoCaller autoCaller(this);
11402 AssertComRCReturnVoid(autoCaller.rc());
11403
11404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11405
11406 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11407
11408 HRESULT rc = S_OK;
11409
11410 /* no attach/detach operations -- nothing to do */
11411 if (!mMediumAttachments.isBackedUp())
11412 return;
11413
11414 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11415 bool fMediaNeedsLocking = false;
11416
11417 /* enumerate new attachments */
11418 for (MediumAttachmentList::const_iterator
11419 it = mMediumAttachments->begin();
11420 it != mMediumAttachments->end();
11421 ++it)
11422 {
11423 MediumAttachment *pAttach = *it;
11424
11425 pAttach->i_commit();
11426
11427 Medium *pMedium = pAttach->i_getMedium();
11428 bool fImplicit = pAttach->i_isImplicit();
11429
11430 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11431 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11432 fImplicit));
11433
11434 /** @todo convert all this Machine-based voodoo to MediumAttachment
11435 * based commit logic. */
11436 if (fImplicit)
11437 {
11438 /* convert implicit attachment to normal */
11439 pAttach->i_setImplicit(false);
11440
11441 if ( aOnline
11442 && pMedium
11443 && pAttach->i_getType() == DeviceType_HardDisk
11444 )
11445 {
11446 /* update the appropriate lock list */
11447 MediumLockList *pMediumLockList;
11448 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11449 AssertComRC(rc);
11450 if (pMediumLockList)
11451 {
11452 /* unlock if there's a need to change the locking */
11453 if (!fMediaNeedsLocking)
11454 {
11455 rc = mData->mSession.mLockedMedia.Unlock();
11456 AssertComRC(rc);
11457 fMediaNeedsLocking = true;
11458 }
11459 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11460 AssertComRC(rc);
11461 rc = pMediumLockList->Append(pMedium, true);
11462 AssertComRC(rc);
11463 }
11464 }
11465
11466 continue;
11467 }
11468
11469 if (pMedium)
11470 {
11471 /* was this medium attached before? */
11472 for (MediumAttachmentList::iterator
11473 oldIt = oldAtts.begin();
11474 oldIt != oldAtts.end();
11475 ++oldIt)
11476 {
11477 MediumAttachment *pOldAttach = *oldIt;
11478 if (pOldAttach->i_getMedium() == pMedium)
11479 {
11480 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11481
11482 /* yes: remove from old to avoid de-association */
11483 oldAtts.erase(oldIt);
11484 break;
11485 }
11486 }
11487 }
11488 }
11489
11490 /* enumerate remaining old attachments and de-associate from the
11491 * current machine state */
11492 for (MediumAttachmentList::const_iterator
11493 it = oldAtts.begin();
11494 it != oldAtts.end();
11495 ++it)
11496 {
11497 MediumAttachment *pAttach = *it;
11498 Medium *pMedium = pAttach->i_getMedium();
11499
11500 /* Detach only hard disks, since DVD/floppy media is detached
11501 * instantly in MountMedium. */
11502 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11503 {
11504 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11505
11506 /* now de-associate from the current machine state */
11507 rc = pMedium->i_removeBackReference(mData->mUuid);
11508 AssertComRC(rc);
11509
11510 if (aOnline)
11511 {
11512 /* unlock since medium is not used anymore */
11513 MediumLockList *pMediumLockList;
11514 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11515 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11516 {
11517 /* this happens for online snapshots, there the attachment
11518 * is changing, but only to a diff image created under
11519 * the old one, so there is no separate lock list */
11520 Assert(!pMediumLockList);
11521 }
11522 else
11523 {
11524 AssertComRC(rc);
11525 if (pMediumLockList)
11526 {
11527 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11528 AssertComRC(rc);
11529 }
11530 }
11531 }
11532 }
11533 }
11534
11535 /* take media locks again so that the locking state is consistent */
11536 if (fMediaNeedsLocking)
11537 {
11538 Assert(aOnline);
11539 rc = mData->mSession.mLockedMedia.Lock();
11540 AssertComRC(rc);
11541 }
11542
11543 /* commit the hard disk changes */
11544 mMediumAttachments.commit();
11545
11546 if (i_isSessionMachine())
11547 {
11548 /*
11549 * Update the parent machine to point to the new owner.
11550 * This is necessary because the stored parent will point to the
11551 * session machine otherwise and cause crashes or errors later
11552 * when the session machine gets invalid.
11553 */
11554 /** @todo Change the MediumAttachment class to behave like any other
11555 * class in this regard by creating peer MediumAttachment
11556 * objects for session machines and share the data with the peer
11557 * machine.
11558 */
11559 for (MediumAttachmentList::const_iterator
11560 it = mMediumAttachments->begin();
11561 it != mMediumAttachments->end();
11562 ++it)
11563 (*it)->i_updateParentMachine(mPeer);
11564
11565 /* attach new data to the primary machine and reshare it */
11566 mPeer->mMediumAttachments.attach(mMediumAttachments);
11567 }
11568
11569 return;
11570}
11571
11572/**
11573 * Perform deferred deletion of implicitly created diffs.
11574 *
11575 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11576 * changed (not backed up).
11577 *
11578 * @note Locks this object for writing!
11579 */
11580void Machine::i_rollbackMedia()
11581{
11582 AutoCaller autoCaller(this);
11583 AssertComRCReturnVoid(autoCaller.rc());
11584
11585 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11586 LogFlowThisFunc(("Entering rollbackMedia\n"));
11587
11588 HRESULT rc = S_OK;
11589
11590 /* no attach/detach operations -- nothing to do */
11591 if (!mMediumAttachments.isBackedUp())
11592 return;
11593
11594 /* enumerate new attachments */
11595 for (MediumAttachmentList::const_iterator
11596 it = mMediumAttachments->begin();
11597 it != mMediumAttachments->end();
11598 ++it)
11599 {
11600 MediumAttachment *pAttach = *it;
11601 /* Fix up the backrefs for DVD/floppy media. */
11602 if (pAttach->i_getType() != DeviceType_HardDisk)
11603 {
11604 Medium *pMedium = pAttach->i_getMedium();
11605 if (pMedium)
11606 {
11607 rc = pMedium->i_removeBackReference(mData->mUuid);
11608 AssertComRC(rc);
11609 }
11610 }
11611
11612 (*it)->i_rollback();
11613
11614 pAttach = *it;
11615 /* Fix up the backrefs for DVD/floppy media. */
11616 if (pAttach->i_getType() != DeviceType_HardDisk)
11617 {
11618 Medium *pMedium = pAttach->i_getMedium();
11619 if (pMedium)
11620 {
11621 rc = pMedium->i_addBackReference(mData->mUuid);
11622 AssertComRC(rc);
11623 }
11624 }
11625 }
11626
11627 /** @todo convert all this Machine-based voodoo to MediumAttachment
11628 * based rollback logic. */
11629 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11630
11631 return;
11632}
11633
11634/**
11635 * Returns true if the settings file is located in the directory named exactly
11636 * as the machine; this means, among other things, that the machine directory
11637 * should be auto-renamed.
11638 *
11639 * @param aSettingsDir if not NULL, the full machine settings file directory
11640 * name will be assigned there.
11641 *
11642 * @note Doesn't lock anything.
11643 * @note Not thread safe (must be called from this object's lock).
11644 */
11645bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11646{
11647 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11648 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11649 if (aSettingsDir)
11650 *aSettingsDir = strMachineDirName;
11651 strMachineDirName.stripPath(); // vmname
11652 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11653 strConfigFileOnly.stripPath() // vmname.vbox
11654 .stripSuffix(); // vmname
11655 /** @todo hack, make somehow use of ComposeMachineFilename */
11656 if (mUserData->s.fDirectoryIncludesUUID)
11657 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11658
11659 AssertReturn(!strMachineDirName.isEmpty(), false);
11660 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11661
11662 return strMachineDirName == strConfigFileOnly;
11663}
11664
11665/**
11666 * Discards all changes to machine settings.
11667 *
11668 * @param aNotify Whether to notify the direct session about changes or not.
11669 *
11670 * @note Locks objects for writing!
11671 */
11672void Machine::i_rollback(bool aNotify)
11673{
11674 AutoCaller autoCaller(this);
11675 AssertComRCReturn(autoCaller.rc(), (void)0);
11676
11677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11678
11679 if (!mStorageControllers.isNull())
11680 {
11681 if (mStorageControllers.isBackedUp())
11682 {
11683 /* unitialize all new devices (absent in the backed up list). */
11684 StorageControllerList *backedList = mStorageControllers.backedUpData();
11685 for (StorageControllerList::const_iterator
11686 it = mStorageControllers->begin();
11687 it != mStorageControllers->end();
11688 ++it)
11689 {
11690 if ( std::find(backedList->begin(), backedList->end(), *it)
11691 == backedList->end()
11692 )
11693 {
11694 (*it)->uninit();
11695 }
11696 }
11697
11698 /* restore the list */
11699 mStorageControllers.rollback();
11700 }
11701
11702 /* rollback any changes to devices after restoring the list */
11703 if (mData->flModifications & IsModified_Storage)
11704 {
11705 for (StorageControllerList::const_iterator
11706 it = mStorageControllers->begin();
11707 it != mStorageControllers->end();
11708 ++it)
11709 {
11710 (*it)->i_rollback();
11711 }
11712 }
11713 }
11714
11715 if (!mUSBControllers.isNull())
11716 {
11717 if (mUSBControllers.isBackedUp())
11718 {
11719 /* unitialize all new devices (absent in the backed up list). */
11720 USBControllerList *backedList = mUSBControllers.backedUpData();
11721 for (USBControllerList::const_iterator
11722 it = mUSBControllers->begin();
11723 it != mUSBControllers->end();
11724 ++it)
11725 {
11726 if ( std::find(backedList->begin(), backedList->end(), *it)
11727 == backedList->end()
11728 )
11729 {
11730 (*it)->uninit();
11731 }
11732 }
11733
11734 /* restore the list */
11735 mUSBControllers.rollback();
11736 }
11737
11738 /* rollback any changes to devices after restoring the list */
11739 if (mData->flModifications & IsModified_USB)
11740 {
11741 for (USBControllerList::const_iterator
11742 it = mUSBControllers->begin();
11743 it != mUSBControllers->end();
11744 ++it)
11745 {
11746 (*it)->i_rollback();
11747 }
11748 }
11749 }
11750
11751 mUserData.rollback();
11752
11753 mHWData.rollback();
11754
11755 if (mData->flModifications & IsModified_Storage)
11756 i_rollbackMedia();
11757
11758 if (mBIOSSettings)
11759 mBIOSSettings->i_rollback();
11760
11761 if (mTrustedPlatformModule)
11762 mTrustedPlatformModule->i_rollback();
11763
11764 if (mNvramStore)
11765 mNvramStore->i_rollback();
11766
11767 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11768 mRecordingSettings->i_rollback();
11769
11770 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11771 mGraphicsAdapter->i_rollback();
11772
11773 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11774 mVRDEServer->i_rollback();
11775
11776 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11777 mAudioAdapter->i_rollback();
11778
11779 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11780 mUSBDeviceFilters->i_rollback();
11781
11782 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11783 mBandwidthControl->i_rollback();
11784
11785 if (!mHWData.isNull())
11786 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11787 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11788 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11789 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11790
11791 if (mData->flModifications & IsModified_NetworkAdapters)
11792 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11793 if ( mNetworkAdapters[slot]
11794 && mNetworkAdapters[slot]->i_isModified())
11795 {
11796 mNetworkAdapters[slot]->i_rollback();
11797 networkAdapters[slot] = mNetworkAdapters[slot];
11798 }
11799
11800 if (mData->flModifications & IsModified_SerialPorts)
11801 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11802 if ( mSerialPorts[slot]
11803 && mSerialPorts[slot]->i_isModified())
11804 {
11805 mSerialPorts[slot]->i_rollback();
11806 serialPorts[slot] = mSerialPorts[slot];
11807 }
11808
11809 if (mData->flModifications & IsModified_ParallelPorts)
11810 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11811 if ( mParallelPorts[slot]
11812 && mParallelPorts[slot]->i_isModified())
11813 {
11814 mParallelPorts[slot]->i_rollback();
11815 parallelPorts[slot] = mParallelPorts[slot];
11816 }
11817
11818 if (aNotify)
11819 {
11820 /* inform the direct session about changes */
11821
11822 ComObjPtr<Machine> that = this;
11823 uint32_t flModifications = mData->flModifications;
11824 alock.release();
11825
11826 if (flModifications & IsModified_SharedFolders)
11827 that->i_onSharedFolderChange();
11828
11829 if (flModifications & IsModified_VRDEServer)
11830 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11831 if (flModifications & IsModified_USB)
11832 that->i_onUSBControllerChange();
11833
11834 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11835 if (networkAdapters[slot])
11836 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11837 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11838 if (serialPorts[slot])
11839 that->i_onSerialPortChange(serialPorts[slot]);
11840 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11841 if (parallelPorts[slot])
11842 that->i_onParallelPortChange(parallelPorts[slot]);
11843
11844 if (flModifications & IsModified_Storage)
11845 {
11846 for (StorageControllerList::const_iterator
11847 it = mStorageControllers->begin();
11848 it != mStorageControllers->end();
11849 ++it)
11850 {
11851 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11852 }
11853 }
11854
11855
11856#if 0
11857 if (flModifications & IsModified_BandwidthControl)
11858 that->onBandwidthControlChange();
11859#endif
11860 }
11861}
11862
11863/**
11864 * Commits all the changes to machine settings.
11865 *
11866 * Note that this operation is supposed to never fail.
11867 *
11868 * @note Locks this object and children for writing.
11869 */
11870void Machine::i_commit()
11871{
11872 AutoCaller autoCaller(this);
11873 AssertComRCReturnVoid(autoCaller.rc());
11874
11875 AutoCaller peerCaller(mPeer);
11876 AssertComRCReturnVoid(peerCaller.rc());
11877
11878 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11879
11880 /*
11881 * use safe commit to ensure Snapshot machines (that share mUserData)
11882 * will still refer to a valid memory location
11883 */
11884 mUserData.commitCopy();
11885
11886 mHWData.commit();
11887
11888 if (mMediumAttachments.isBackedUp())
11889 i_commitMedia(Global::IsOnline(mData->mMachineState));
11890
11891 mBIOSSettings->i_commit();
11892 mTrustedPlatformModule->i_commit();
11893 mNvramStore->i_commit();
11894 mRecordingSettings->i_commit();
11895 mGraphicsAdapter->i_commit();
11896 mVRDEServer->i_commit();
11897 mAudioAdapter->i_commit();
11898 mUSBDeviceFilters->i_commit();
11899 mBandwidthControl->i_commit();
11900
11901 /* Since mNetworkAdapters is a list which might have been changed (resized)
11902 * without using the Backupable<> template we need to handle the copying
11903 * of the list entries manually, including the creation of peers for the
11904 * new objects. */
11905 bool commitNetworkAdapters = false;
11906 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11907 if (mPeer)
11908 {
11909 /* commit everything, even the ones which will go away */
11910 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11911 mNetworkAdapters[slot]->i_commit();
11912 /* copy over the new entries, creating a peer and uninit the original */
11913 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11914 for (size_t slot = 0; slot < newSize; slot++)
11915 {
11916 /* look if this adapter has a peer device */
11917 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11918 if (!peer)
11919 {
11920 /* no peer means the adapter is a newly created one;
11921 * create a peer owning data this data share it with */
11922 peer.createObject();
11923 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11924 }
11925 mPeer->mNetworkAdapters[slot] = peer;
11926 }
11927 /* uninit any no longer needed network adapters */
11928 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11929 mNetworkAdapters[slot]->uninit();
11930 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11931 {
11932 if (mPeer->mNetworkAdapters[slot])
11933 mPeer->mNetworkAdapters[slot]->uninit();
11934 }
11935 /* Keep the original network adapter count until this point, so that
11936 * discarding a chipset type change will not lose settings. */
11937 mNetworkAdapters.resize(newSize);
11938 mPeer->mNetworkAdapters.resize(newSize);
11939 }
11940 else
11941 {
11942 /* we have no peer (our parent is the newly created machine);
11943 * just commit changes to the network adapters */
11944 commitNetworkAdapters = true;
11945 }
11946 if (commitNetworkAdapters)
11947 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11948 mNetworkAdapters[slot]->i_commit();
11949
11950 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11951 mSerialPorts[slot]->i_commit();
11952 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11953 mParallelPorts[slot]->i_commit();
11954
11955 bool commitStorageControllers = false;
11956
11957 if (mStorageControllers.isBackedUp())
11958 {
11959 mStorageControllers.commit();
11960
11961 if (mPeer)
11962 {
11963 /* Commit all changes to new controllers (this will reshare data with
11964 * peers for those who have peers) */
11965 StorageControllerList *newList = new StorageControllerList();
11966 for (StorageControllerList::const_iterator
11967 it = mStorageControllers->begin();
11968 it != mStorageControllers->end();
11969 ++it)
11970 {
11971 (*it)->i_commit();
11972
11973 /* look if this controller has a peer device */
11974 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11975 if (!peer)
11976 {
11977 /* no peer means the device is a newly created one;
11978 * create a peer owning data this device share it with */
11979 peer.createObject();
11980 peer->init(mPeer, *it, true /* aReshare */);
11981 }
11982 else
11983 {
11984 /* remove peer from the old list */
11985 mPeer->mStorageControllers->remove(peer);
11986 }
11987 /* and add it to the new list */
11988 newList->push_back(peer);
11989 }
11990
11991 /* uninit old peer's controllers that are left */
11992 for (StorageControllerList::const_iterator
11993 it = mPeer->mStorageControllers->begin();
11994 it != mPeer->mStorageControllers->end();
11995 ++it)
11996 {
11997 (*it)->uninit();
11998 }
11999
12000 /* attach new list of controllers to our peer */
12001 mPeer->mStorageControllers.attach(newList);
12002 }
12003 else
12004 {
12005 /* we have no peer (our parent is the newly created machine);
12006 * just commit changes to devices */
12007 commitStorageControllers = true;
12008 }
12009 }
12010 else
12011 {
12012 /* the list of controllers itself is not changed,
12013 * just commit changes to controllers themselves */
12014 commitStorageControllers = true;
12015 }
12016
12017 if (commitStorageControllers)
12018 {
12019 for (StorageControllerList::const_iterator
12020 it = mStorageControllers->begin();
12021 it != mStorageControllers->end();
12022 ++it)
12023 {
12024 (*it)->i_commit();
12025 }
12026 }
12027
12028 bool commitUSBControllers = false;
12029
12030 if (mUSBControllers.isBackedUp())
12031 {
12032 mUSBControllers.commit();
12033
12034 if (mPeer)
12035 {
12036 /* Commit all changes to new controllers (this will reshare data with
12037 * peers for those who have peers) */
12038 USBControllerList *newList = new USBControllerList();
12039 for (USBControllerList::const_iterator
12040 it = mUSBControllers->begin();
12041 it != mUSBControllers->end();
12042 ++it)
12043 {
12044 (*it)->i_commit();
12045
12046 /* look if this controller has a peer device */
12047 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12048 if (!peer)
12049 {
12050 /* no peer means the device is a newly created one;
12051 * create a peer owning data this device share it with */
12052 peer.createObject();
12053 peer->init(mPeer, *it, true /* aReshare */);
12054 }
12055 else
12056 {
12057 /* remove peer from the old list */
12058 mPeer->mUSBControllers->remove(peer);
12059 }
12060 /* and add it to the new list */
12061 newList->push_back(peer);
12062 }
12063
12064 /* uninit old peer's controllers that are left */
12065 for (USBControllerList::const_iterator
12066 it = mPeer->mUSBControllers->begin();
12067 it != mPeer->mUSBControllers->end();
12068 ++it)
12069 {
12070 (*it)->uninit();
12071 }
12072
12073 /* attach new list of controllers to our peer */
12074 mPeer->mUSBControllers.attach(newList);
12075 }
12076 else
12077 {
12078 /* we have no peer (our parent is the newly created machine);
12079 * just commit changes to devices */
12080 commitUSBControllers = true;
12081 }
12082 }
12083 else
12084 {
12085 /* the list of controllers itself is not changed,
12086 * just commit changes to controllers themselves */
12087 commitUSBControllers = true;
12088 }
12089
12090 if (commitUSBControllers)
12091 {
12092 for (USBControllerList::const_iterator
12093 it = mUSBControllers->begin();
12094 it != mUSBControllers->end();
12095 ++it)
12096 {
12097 (*it)->i_commit();
12098 }
12099 }
12100
12101 if (i_isSessionMachine())
12102 {
12103 /* attach new data to the primary machine and reshare it */
12104 mPeer->mUserData.attach(mUserData);
12105 mPeer->mHWData.attach(mHWData);
12106 /* mmMediumAttachments is reshared by fixupMedia */
12107 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12108 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12109 }
12110}
12111
12112/**
12113 * Copies all the hardware data from the given machine.
12114 *
12115 * Currently, only called when the VM is being restored from a snapshot. In
12116 * particular, this implies that the VM is not running during this method's
12117 * call.
12118 *
12119 * @note This method must be called from under this object's lock.
12120 *
12121 * @note This method doesn't call #i_commit(), so all data remains backed up and
12122 * unsaved.
12123 */
12124void Machine::i_copyFrom(Machine *aThat)
12125{
12126 AssertReturnVoid(!i_isSnapshotMachine());
12127 AssertReturnVoid(aThat->i_isSnapshotMachine());
12128
12129 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12130
12131 mHWData.assignCopy(aThat->mHWData);
12132
12133 // create copies of all shared folders (mHWData after attaching a copy
12134 // contains just references to original objects)
12135 for (HWData::SharedFolderList::iterator
12136 it = mHWData->mSharedFolders.begin();
12137 it != mHWData->mSharedFolders.end();
12138 ++it)
12139 {
12140 ComObjPtr<SharedFolder> folder;
12141 folder.createObject();
12142 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12143 AssertComRC(rc);
12144 *it = folder;
12145 }
12146
12147 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12148 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12149 mNvramStore->i_copyFrom(aThat->mNvramStore);
12150 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12151 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12152 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12153 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12154 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12155 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12156
12157 /* create private copies of all controllers */
12158 mStorageControllers.backup();
12159 mStorageControllers->clear();
12160 for (StorageControllerList::const_iterator
12161 it = aThat->mStorageControllers->begin();
12162 it != aThat->mStorageControllers->end();
12163 ++it)
12164 {
12165 ComObjPtr<StorageController> ctrl;
12166 ctrl.createObject();
12167 ctrl->initCopy(this, *it);
12168 mStorageControllers->push_back(ctrl);
12169 }
12170
12171 /* create private copies of all USB controllers */
12172 mUSBControllers.backup();
12173 mUSBControllers->clear();
12174 for (USBControllerList::const_iterator
12175 it = aThat->mUSBControllers->begin();
12176 it != aThat->mUSBControllers->end();
12177 ++it)
12178 {
12179 ComObjPtr<USBController> ctrl;
12180 ctrl.createObject();
12181 ctrl->initCopy(this, *it);
12182 mUSBControllers->push_back(ctrl);
12183 }
12184
12185 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12186 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12187 {
12188 if (mNetworkAdapters[slot].isNotNull())
12189 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12190 else
12191 {
12192 unconst(mNetworkAdapters[slot]).createObject();
12193 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12194 }
12195 }
12196 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12197 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12198 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12199 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12200}
12201
12202/**
12203 * Returns whether the given storage controller is hotplug capable.
12204 *
12205 * @returns true if the controller supports hotplugging
12206 * false otherwise.
12207 * @param enmCtrlType The controller type to check for.
12208 */
12209bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12210{
12211 ComPtr<ISystemProperties> systemProperties;
12212 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12213 if (FAILED(rc))
12214 return false;
12215
12216 BOOL aHotplugCapable = FALSE;
12217 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12218
12219 return RT_BOOL(aHotplugCapable);
12220}
12221
12222#ifdef VBOX_WITH_RESOURCE_USAGE_API
12223
12224void Machine::i_getDiskList(MediaList &list)
12225{
12226 for (MediumAttachmentList::const_iterator
12227 it = mMediumAttachments->begin();
12228 it != mMediumAttachments->end();
12229 ++it)
12230 {
12231 MediumAttachment *pAttach = *it;
12232 /* just in case */
12233 AssertContinue(pAttach);
12234
12235 AutoCaller localAutoCallerA(pAttach);
12236 if (FAILED(localAutoCallerA.rc())) continue;
12237
12238 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12239
12240 if (pAttach->i_getType() == DeviceType_HardDisk)
12241 list.push_back(pAttach->i_getMedium());
12242 }
12243}
12244
12245void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12246{
12247 AssertReturnVoid(isWriteLockOnCurrentThread());
12248 AssertPtrReturnVoid(aCollector);
12249
12250 pm::CollectorHAL *hal = aCollector->getHAL();
12251 /* Create sub metrics */
12252 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12253 "Percentage of processor time spent in user mode by the VM process.");
12254 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12255 "Percentage of processor time spent in kernel mode by the VM process.");
12256 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12257 "Size of resident portion of VM process in memory.");
12258 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12259 "Actual size of all VM disks combined.");
12260 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12261 "Network receive rate.");
12262 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12263 "Network transmit rate.");
12264 /* Create and register base metrics */
12265 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12266 cpuLoadUser, cpuLoadKernel);
12267 aCollector->registerBaseMetric(cpuLoad);
12268 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12269 ramUsageUsed);
12270 aCollector->registerBaseMetric(ramUsage);
12271 MediaList disks;
12272 i_getDiskList(disks);
12273 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12274 diskUsageUsed);
12275 aCollector->registerBaseMetric(diskUsage);
12276
12277 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12278 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12279 new pm::AggregateAvg()));
12280 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12281 new pm::AggregateMin()));
12282 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12283 new pm::AggregateMax()));
12284 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12285 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12286 new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12288 new pm::AggregateMin()));
12289 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12290 new pm::AggregateMax()));
12291
12292 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12293 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12294 new pm::AggregateAvg()));
12295 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12296 new pm::AggregateMin()));
12297 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12298 new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12301 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12302 new pm::AggregateAvg()));
12303 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12304 new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12306 new pm::AggregateMax()));
12307
12308
12309 /* Guest metrics collector */
12310 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12311 aCollector->registerGuest(mCollectorGuest);
12312 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12313
12314 /* Create sub metrics */
12315 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12316 "Percentage of processor time spent in user mode as seen by the guest.");
12317 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12318 "Percentage of processor time spent in kernel mode as seen by the guest.");
12319 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12320 "Percentage of processor time spent idling as seen by the guest.");
12321
12322 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12323 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12324 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12325 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12326 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12327 pm::SubMetric *guestMemCache = new pm::SubMetric(
12328 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12329
12330 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12331 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12332
12333 /* Create and register base metrics */
12334 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12335 machineNetRx, machineNetTx);
12336 aCollector->registerBaseMetric(machineNetRate);
12337
12338 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12339 guestLoadUser, guestLoadKernel, guestLoadIdle);
12340 aCollector->registerBaseMetric(guestCpuLoad);
12341
12342 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12343 guestMemTotal, guestMemFree,
12344 guestMemBalloon, guestMemShared,
12345 guestMemCache, guestPagedTotal);
12346 aCollector->registerBaseMetric(guestCpuMem);
12347
12348 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12349 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12350 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12351 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12352
12353 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12354 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12355 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12356 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12357
12358 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12359 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12361 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12362
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12364 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12366 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12367
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12369 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12371 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12372
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12377
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12382
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12387
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12392
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12397
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12400 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12401 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12402}
12403
12404void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12405{
12406 AssertReturnVoid(isWriteLockOnCurrentThread());
12407
12408 if (aCollector)
12409 {
12410 aCollector->unregisterMetricsFor(aMachine);
12411 aCollector->unregisterBaseMetricsFor(aMachine);
12412 }
12413}
12414
12415#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12416
12417
12418////////////////////////////////////////////////////////////////////////////////
12419
12420DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12421
12422HRESULT SessionMachine::FinalConstruct()
12423{
12424 LogFlowThisFunc(("\n"));
12425
12426 mClientToken = NULL;
12427
12428 return BaseFinalConstruct();
12429}
12430
12431void SessionMachine::FinalRelease()
12432{
12433 LogFlowThisFunc(("\n"));
12434
12435 Assert(!mClientToken);
12436 /* paranoia, should not hang around any more */
12437 if (mClientToken)
12438 {
12439 delete mClientToken;
12440 mClientToken = NULL;
12441 }
12442
12443 uninit(Uninit::Unexpected);
12444
12445 BaseFinalRelease();
12446}
12447
12448/**
12449 * @note Must be called only by Machine::LockMachine() from its own write lock.
12450 */
12451HRESULT SessionMachine::init(Machine *aMachine)
12452{
12453 LogFlowThisFuncEnter();
12454 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12455
12456 AssertReturn(aMachine, E_INVALIDARG);
12457
12458 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12459
12460 /* Enclose the state transition NotReady->InInit->Ready */
12461 AutoInitSpan autoInitSpan(this);
12462 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12463
12464 HRESULT rc = S_OK;
12465
12466 RT_ZERO(mAuthLibCtx);
12467
12468 /* create the machine client token */
12469 try
12470 {
12471 mClientToken = new ClientToken(aMachine, this);
12472 if (!mClientToken->isReady())
12473 {
12474 delete mClientToken;
12475 mClientToken = NULL;
12476 rc = E_FAIL;
12477 }
12478 }
12479 catch (std::bad_alloc &)
12480 {
12481 rc = E_OUTOFMEMORY;
12482 }
12483 if (FAILED(rc))
12484 return rc;
12485
12486 /* memorize the peer Machine */
12487 unconst(mPeer) = aMachine;
12488 /* share the parent pointer */
12489 unconst(mParent) = aMachine->mParent;
12490
12491 /* take the pointers to data to share */
12492 mData.share(aMachine->mData);
12493 mSSData.share(aMachine->mSSData);
12494
12495 mUserData.share(aMachine->mUserData);
12496 mHWData.share(aMachine->mHWData);
12497 mMediumAttachments.share(aMachine->mMediumAttachments);
12498
12499 mStorageControllers.allocate();
12500 for (StorageControllerList::const_iterator
12501 it = aMachine->mStorageControllers->begin();
12502 it != aMachine->mStorageControllers->end();
12503 ++it)
12504 {
12505 ComObjPtr<StorageController> ctl;
12506 ctl.createObject();
12507 ctl->init(this, *it);
12508 mStorageControllers->push_back(ctl);
12509 }
12510
12511 mUSBControllers.allocate();
12512 for (USBControllerList::const_iterator
12513 it = aMachine->mUSBControllers->begin();
12514 it != aMachine->mUSBControllers->end();
12515 ++it)
12516 {
12517 ComObjPtr<USBController> ctl;
12518 ctl.createObject();
12519 ctl->init(this, *it);
12520 mUSBControllers->push_back(ctl);
12521 }
12522
12523 unconst(mBIOSSettings).createObject();
12524 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12525
12526 unconst(mTrustedPlatformModule).createObject();
12527 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12528
12529 unconst(mNvramStore).createObject();
12530 mNvramStore->init(this, aMachine->mNvramStore);
12531
12532 unconst(mRecordingSettings).createObject();
12533 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12534 /* create another GraphicsAdapter object that will be mutable */
12535 unconst(mGraphicsAdapter).createObject();
12536 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12537 /* create another VRDEServer object that will be mutable */
12538 unconst(mVRDEServer).createObject();
12539 mVRDEServer->init(this, aMachine->mVRDEServer);
12540 /* create another audio adapter object that will be mutable */
12541 unconst(mAudioAdapter).createObject();
12542 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12543 /* create a list of serial ports that will be mutable */
12544 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12545 {
12546 unconst(mSerialPorts[slot]).createObject();
12547 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12548 }
12549 /* create a list of parallel ports that will be mutable */
12550 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12551 {
12552 unconst(mParallelPorts[slot]).createObject();
12553 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12554 }
12555
12556 /* create another USB device filters object that will be mutable */
12557 unconst(mUSBDeviceFilters).createObject();
12558 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12559
12560 /* create a list of network adapters that will be mutable */
12561 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12562 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12563 {
12564 unconst(mNetworkAdapters[slot]).createObject();
12565 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12566 }
12567
12568 /* create another bandwidth control object that will be mutable */
12569 unconst(mBandwidthControl).createObject();
12570 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12571
12572 /* default is to delete saved state on Saved -> PoweredOff transition */
12573 mRemoveSavedState = true;
12574
12575 /* Confirm a successful initialization when it's the case */
12576 autoInitSpan.setSucceeded();
12577
12578 miNATNetworksStarted = 0;
12579
12580 LogFlowThisFuncLeave();
12581 return rc;
12582}
12583
12584/**
12585 * Uninitializes this session object. If the reason is other than
12586 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12587 * or the client watcher code.
12588 *
12589 * @param aReason uninitialization reason
12590 *
12591 * @note Locks mParent + this object for writing.
12592 */
12593void SessionMachine::uninit(Uninit::Reason aReason)
12594{
12595 LogFlowThisFuncEnter();
12596 LogFlowThisFunc(("reason=%d\n", aReason));
12597
12598 /*
12599 * Strongly reference ourselves to prevent this object deletion after
12600 * mData->mSession.mMachine.setNull() below (which can release the last
12601 * reference and call the destructor). Important: this must be done before
12602 * accessing any members (and before AutoUninitSpan that does it as well).
12603 * This self reference will be released as the very last step on return.
12604 */
12605 ComObjPtr<SessionMachine> selfRef;
12606 if (aReason != Uninit::Unexpected)
12607 selfRef = this;
12608
12609 /* Enclose the state transition Ready->InUninit->NotReady */
12610 AutoUninitSpan autoUninitSpan(this);
12611 if (autoUninitSpan.uninitDone())
12612 {
12613 LogFlowThisFunc(("Already uninitialized\n"));
12614 LogFlowThisFuncLeave();
12615 return;
12616 }
12617
12618 if (autoUninitSpan.initFailed())
12619 {
12620 /* We've been called by init() because it's failed. It's not really
12621 * necessary (nor it's safe) to perform the regular uninit sequence
12622 * below, the following is enough.
12623 */
12624 LogFlowThisFunc(("Initialization failed.\n"));
12625 /* destroy the machine client token */
12626 if (mClientToken)
12627 {
12628 delete mClientToken;
12629 mClientToken = NULL;
12630 }
12631 uninitDataAndChildObjects();
12632 mData.free();
12633 unconst(mParent) = NULL;
12634 unconst(mPeer) = NULL;
12635 LogFlowThisFuncLeave();
12636 return;
12637 }
12638
12639 MachineState_T lastState;
12640 {
12641 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12642 lastState = mData->mMachineState;
12643 }
12644 NOREF(lastState);
12645
12646#ifdef VBOX_WITH_USB
12647 // release all captured USB devices, but do this before requesting the locks below
12648 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12649 {
12650 /* Console::captureUSBDevices() is called in the VM process only after
12651 * setting the machine state to Starting or Restoring.
12652 * Console::detachAllUSBDevices() will be called upon successful
12653 * termination. So, we need to release USB devices only if there was
12654 * an abnormal termination of a running VM.
12655 *
12656 * This is identical to SessionMachine::DetachAllUSBDevices except
12657 * for the aAbnormal argument. */
12658 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12659 AssertComRC(rc);
12660 NOREF(rc);
12661
12662 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12663 if (service)
12664 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12665 }
12666#endif /* VBOX_WITH_USB */
12667
12668 // we need to lock this object in uninit() because the lock is shared
12669 // with mPeer (as well as data we modify below). mParent lock is needed
12670 // by several calls to it.
12671 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12672
12673#ifdef VBOX_WITH_RESOURCE_USAGE_API
12674 /*
12675 * It is safe to call Machine::i_unregisterMetrics() here because
12676 * PerformanceCollector::samplerCallback no longer accesses guest methods
12677 * holding the lock.
12678 */
12679 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12680 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12681 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12682 if (mCollectorGuest)
12683 {
12684 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12685 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12686 mCollectorGuest = NULL;
12687 }
12688#endif
12689
12690 if (aReason == Uninit::Abnormal)
12691 {
12692 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12693
12694 /*
12695 * Move the VM to the 'Aborted' machine state unless we are restoring a
12696 * VM that was in the 'Saved' machine state. In that case, if the VM
12697 * fails before reaching either the 'Restoring' machine state or the
12698 * 'Running' machine state then we set the machine state to
12699 * 'AbortedSaved' in order to preserve the saved state file so that the
12700 * VM can be restored in the future.
12701 */
12702 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12703 i_setMachineState(MachineState_AbortedSaved);
12704 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12705 i_setMachineState(MachineState_Aborted);
12706 }
12707
12708 // any machine settings modified?
12709 if (mData->flModifications)
12710 {
12711 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12712 i_rollback(false /* aNotify */);
12713 }
12714
12715 mData->mSession.mPID = NIL_RTPROCESS;
12716
12717 if (aReason == Uninit::Unexpected)
12718 {
12719 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12720 * client watcher thread to update the set of machines that have open
12721 * sessions. */
12722 mParent->i_updateClientWatcher();
12723 }
12724
12725 /* uninitialize all remote controls */
12726 if (mData->mSession.mRemoteControls.size())
12727 {
12728 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12729 mData->mSession.mRemoteControls.size()));
12730
12731 /* Always restart a the beginning, since the iterator is invalidated
12732 * by using erase(). */
12733 for (Data::Session::RemoteControlList::iterator
12734 it = mData->mSession.mRemoteControls.begin();
12735 it != mData->mSession.mRemoteControls.end();
12736 it = mData->mSession.mRemoteControls.begin())
12737 {
12738 ComPtr<IInternalSessionControl> pControl = *it;
12739 mData->mSession.mRemoteControls.erase(it);
12740 multilock.release();
12741 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12742 HRESULT rc = pControl->Uninitialize();
12743 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12744 if (FAILED(rc))
12745 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12746 multilock.acquire();
12747 }
12748 mData->mSession.mRemoteControls.clear();
12749 }
12750
12751 /* Remove all references to the NAT network service. The service will stop
12752 * if all references (also from other VMs) are removed. */
12753 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12754 {
12755 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12756 {
12757 BOOL enabled;
12758 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12759 if ( FAILED(hrc)
12760 || !enabled)
12761 continue;
12762
12763 NetworkAttachmentType_T type;
12764 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12765 if ( SUCCEEDED(hrc)
12766 && type == NetworkAttachmentType_NATNetwork)
12767 {
12768 Bstr name;
12769 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12770 if (SUCCEEDED(hrc))
12771 {
12772 multilock.release();
12773 Utf8Str strName(name);
12774 LogRel(("VM '%s' stops using NAT network '%s'\n",
12775 mUserData->s.strName.c_str(), strName.c_str()));
12776 mParent->i_natNetworkRefDec(strName);
12777 multilock.acquire();
12778 }
12779 }
12780 }
12781 }
12782
12783 /*
12784 * An expected uninitialization can come only from #i_checkForDeath().
12785 * Otherwise it means that something's gone really wrong (for example,
12786 * the Session implementation has released the VirtualBox reference
12787 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12788 * etc). However, it's also possible, that the client releases the IPC
12789 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12790 * but the VirtualBox release event comes first to the server process.
12791 * This case is practically possible, so we should not assert on an
12792 * unexpected uninit, just log a warning.
12793 */
12794
12795 if (aReason == Uninit::Unexpected)
12796 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12797
12798 if (aReason != Uninit::Normal)
12799 {
12800 mData->mSession.mDirectControl.setNull();
12801 }
12802 else
12803 {
12804 /* this must be null here (see #OnSessionEnd()) */
12805 Assert(mData->mSession.mDirectControl.isNull());
12806 Assert(mData->mSession.mState == SessionState_Unlocking);
12807 Assert(!mData->mSession.mProgress.isNull());
12808 }
12809 if (mData->mSession.mProgress)
12810 {
12811 if (aReason == Uninit::Normal)
12812 mData->mSession.mProgress->i_notifyComplete(S_OK);
12813 else
12814 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12815 COM_IIDOF(ISession),
12816 getComponentName(),
12817 tr("The VM session was aborted"));
12818 mData->mSession.mProgress.setNull();
12819 }
12820
12821 if (mConsoleTaskData.mProgress)
12822 {
12823 Assert(aReason == Uninit::Abnormal);
12824 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12825 COM_IIDOF(ISession),
12826 getComponentName(),
12827 tr("The VM session was aborted"));
12828 mConsoleTaskData.mProgress.setNull();
12829 }
12830
12831 /* remove the association between the peer machine and this session machine */
12832 Assert( (SessionMachine*)mData->mSession.mMachine == this
12833 || aReason == Uninit::Unexpected);
12834
12835 /* reset the rest of session data */
12836 mData->mSession.mLockType = LockType_Null;
12837 mData->mSession.mMachine.setNull();
12838 mData->mSession.mState = SessionState_Unlocked;
12839 mData->mSession.mName.setNull();
12840
12841 /* destroy the machine client token before leaving the exclusive lock */
12842 if (mClientToken)
12843 {
12844 delete mClientToken;
12845 mClientToken = NULL;
12846 }
12847
12848 /* fire an event */
12849 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12850
12851 uninitDataAndChildObjects();
12852
12853 /* free the essential data structure last */
12854 mData.free();
12855
12856 /* release the exclusive lock before setting the below two to NULL */
12857 multilock.release();
12858
12859 unconst(mParent) = NULL;
12860 unconst(mPeer) = NULL;
12861
12862 AuthLibUnload(&mAuthLibCtx);
12863
12864 LogFlowThisFuncLeave();
12865}
12866
12867// util::Lockable interface
12868////////////////////////////////////////////////////////////////////////////////
12869
12870/**
12871 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12872 * with the primary Machine instance (mPeer).
12873 */
12874RWLockHandle *SessionMachine::lockHandle() const
12875{
12876 AssertReturn(mPeer != NULL, NULL);
12877 return mPeer->lockHandle();
12878}
12879
12880// IInternalMachineControl methods
12881////////////////////////////////////////////////////////////////////////////////
12882
12883/**
12884 * Passes collected guest statistics to performance collector object
12885 */
12886HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12887 ULONG aCpuKernel, ULONG aCpuIdle,
12888 ULONG aMemTotal, ULONG aMemFree,
12889 ULONG aMemBalloon, ULONG aMemShared,
12890 ULONG aMemCache, ULONG aPageTotal,
12891 ULONG aAllocVMM, ULONG aFreeVMM,
12892 ULONG aBalloonedVMM, ULONG aSharedVMM,
12893 ULONG aVmNetRx, ULONG aVmNetTx)
12894{
12895#ifdef VBOX_WITH_RESOURCE_USAGE_API
12896 if (mCollectorGuest)
12897 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12898 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12899 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12900 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12901
12902 return S_OK;
12903#else
12904 NOREF(aValidStats);
12905 NOREF(aCpuUser);
12906 NOREF(aCpuKernel);
12907 NOREF(aCpuIdle);
12908 NOREF(aMemTotal);
12909 NOREF(aMemFree);
12910 NOREF(aMemBalloon);
12911 NOREF(aMemShared);
12912 NOREF(aMemCache);
12913 NOREF(aPageTotal);
12914 NOREF(aAllocVMM);
12915 NOREF(aFreeVMM);
12916 NOREF(aBalloonedVMM);
12917 NOREF(aSharedVMM);
12918 NOREF(aVmNetRx);
12919 NOREF(aVmNetTx);
12920 return E_NOTIMPL;
12921#endif
12922}
12923
12924////////////////////////////////////////////////////////////////////////////////
12925//
12926// SessionMachine task records
12927//
12928////////////////////////////////////////////////////////////////////////////////
12929
12930/**
12931 * Task record for saving the machine state.
12932 */
12933class SessionMachine::SaveStateTask
12934 : public Machine::Task
12935{
12936public:
12937 SaveStateTask(SessionMachine *m,
12938 Progress *p,
12939 const Utf8Str &t,
12940 Reason_T enmReason,
12941 const Utf8Str &strStateFilePath)
12942 : Task(m, p, t),
12943 m_enmReason(enmReason),
12944 m_strStateFilePath(strStateFilePath)
12945 {}
12946
12947private:
12948 void handler()
12949 {
12950 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12951 }
12952
12953 Reason_T m_enmReason;
12954 Utf8Str m_strStateFilePath;
12955
12956 friend class SessionMachine;
12957};
12958
12959/**
12960 * Task thread implementation for SessionMachine::SaveState(), called from
12961 * SessionMachine::taskHandler().
12962 *
12963 * @note Locks this object for writing.
12964 *
12965 * @param task
12966 * @return
12967 */
12968void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12969{
12970 LogFlowThisFuncEnter();
12971
12972 AutoCaller autoCaller(this);
12973 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12974 if (FAILED(autoCaller.rc()))
12975 {
12976 /* we might have been uninitialized because the session was accidentally
12977 * closed by the client, so don't assert */
12978 HRESULT rc = setError(E_FAIL,
12979 tr("The session has been accidentally closed"));
12980 task.m_pProgress->i_notifyComplete(rc);
12981 LogFlowThisFuncLeave();
12982 return;
12983 }
12984
12985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12986
12987 HRESULT rc = S_OK;
12988
12989 try
12990 {
12991 ComPtr<IInternalSessionControl> directControl;
12992 if (mData->mSession.mLockType == LockType_VM)
12993 directControl = mData->mSession.mDirectControl;
12994 if (directControl.isNull())
12995 throw setError(VBOX_E_INVALID_VM_STATE,
12996 tr("Trying to save state without a running VM"));
12997 alock.release();
12998 BOOL fSuspendedBySave;
12999 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13000 Assert(!fSuspendedBySave);
13001 alock.acquire();
13002
13003 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13004 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13005 throw E_FAIL);
13006
13007 if (SUCCEEDED(rc))
13008 {
13009 mSSData->strStateFilePath = task.m_strStateFilePath;
13010
13011 /* save all VM settings */
13012 rc = i_saveSettings(NULL, alock);
13013 // no need to check whether VirtualBox.xml needs saving also since
13014 // we can't have a name change pending at this point
13015 }
13016 else
13017 {
13018 // On failure, set the state to the state we had at the beginning.
13019 i_setMachineState(task.m_machineStateBackup);
13020 i_updateMachineStateOnClient();
13021
13022 // Delete the saved state file (might have been already created).
13023 // No need to check whether this is shared with a snapshot here
13024 // because we certainly created a fresh saved state file here.
13025 RTFileDelete(task.m_strStateFilePath.c_str());
13026 }
13027 }
13028 catch (HRESULT aRC) { rc = aRC; }
13029
13030 task.m_pProgress->i_notifyComplete(rc);
13031
13032 LogFlowThisFuncLeave();
13033}
13034
13035/**
13036 * @note Locks this object for writing.
13037 */
13038HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13039{
13040 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13041}
13042
13043HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13044{
13045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13046
13047 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13048 if (FAILED(rc)) return rc;
13049
13050 if ( mData->mMachineState != MachineState_Running
13051 && mData->mMachineState != MachineState_Paused
13052 )
13053 return setError(VBOX_E_INVALID_VM_STATE,
13054 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13055 Global::stringifyMachineState(mData->mMachineState));
13056
13057 ComObjPtr<Progress> pProgress;
13058 pProgress.createObject();
13059 rc = pProgress->init(i_getVirtualBox(),
13060 static_cast<IMachine *>(this) /* aInitiator */,
13061 tr("Saving the execution state of the virtual machine"),
13062 FALSE /* aCancelable */);
13063 if (FAILED(rc))
13064 return rc;
13065
13066 Utf8Str strStateFilePath;
13067 i_composeSavedStateFilename(strStateFilePath);
13068
13069 /* create and start the task on a separate thread (note that it will not
13070 * start working until we release alock) */
13071 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13072 rc = pTask->createThread();
13073 if (FAILED(rc))
13074 return rc;
13075
13076 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13077 i_setMachineState(MachineState_Saving);
13078 i_updateMachineStateOnClient();
13079
13080 pProgress.queryInterfaceTo(aProgress.asOutParam());
13081
13082 return S_OK;
13083}
13084
13085/**
13086 * @note Locks this object for writing.
13087 */
13088HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13089{
13090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13091
13092 HRESULT rc = i_checkStateDependency(MutableStateDep);
13093 if (FAILED(rc)) return rc;
13094
13095 if ( mData->mMachineState != MachineState_PoweredOff
13096 && mData->mMachineState != MachineState_Teleported
13097 && mData->mMachineState != MachineState_Aborted
13098 )
13099 return setError(VBOX_E_INVALID_VM_STATE,
13100 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13101 Global::stringifyMachineState(mData->mMachineState));
13102
13103 com::Utf8Str stateFilePathFull;
13104 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13105 if (RT_FAILURE(vrc))
13106 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13107 tr("Invalid saved state file path '%s' (%Rrc)"),
13108 aSavedStateFile.c_str(),
13109 vrc);
13110
13111 mSSData->strStateFilePath = stateFilePathFull;
13112
13113 /* The below i_setMachineState() will detect the state transition and will
13114 * update the settings file */
13115
13116 return i_setMachineState(MachineState_Saved);
13117}
13118
13119/**
13120 * @note Locks this object for writing.
13121 */
13122HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13123{
13124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13125
13126 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13127 if (FAILED(rc)) return rc;
13128
13129 if ( mData->mMachineState != MachineState_Saved
13130 && mData->mMachineState != MachineState_AbortedSaved)
13131 return setError(VBOX_E_INVALID_VM_STATE,
13132 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13133 Global::stringifyMachineState(mData->mMachineState));
13134
13135 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13136
13137 /*
13138 * Saved -> PoweredOff transition will be detected in the SessionMachine
13139 * and properly handled.
13140 */
13141 rc = i_setMachineState(MachineState_PoweredOff);
13142 return rc;
13143}
13144
13145
13146/**
13147 * @note Locks the same as #i_setMachineState() does.
13148 */
13149HRESULT SessionMachine::updateState(MachineState_T aState)
13150{
13151 return i_setMachineState(aState);
13152}
13153
13154/**
13155 * @note Locks this object for writing.
13156 */
13157HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13158{
13159 IProgress *pProgress(aProgress);
13160
13161 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13162
13163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13164
13165 if (mData->mSession.mState != SessionState_Locked)
13166 return VBOX_E_INVALID_OBJECT_STATE;
13167
13168 if (!mData->mSession.mProgress.isNull())
13169 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13170
13171 /* If we didn't reference the NAT network service yet, add a reference to
13172 * force a start */
13173 if (miNATNetworksStarted < 1)
13174 {
13175 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13176 {
13177 BOOL enabled;
13178 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13179 if ( FAILED(hrc)
13180 || !enabled)
13181 continue;
13182
13183 NetworkAttachmentType_T type;
13184 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13185 if ( SUCCEEDED(hrc)
13186 && type == NetworkAttachmentType_NATNetwork)
13187 {
13188 Bstr name;
13189 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13190 if (SUCCEEDED(hrc))
13191 {
13192 Utf8Str strName(name);
13193 LogRel(("VM '%s' starts using NAT network '%s'\n",
13194 mUserData->s.strName.c_str(), strName.c_str()));
13195 mPeer->lockHandle()->unlockWrite();
13196 mParent->i_natNetworkRefInc(strName);
13197#ifdef RT_LOCK_STRICT
13198 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13199#else
13200 mPeer->lockHandle()->lockWrite();
13201#endif
13202 }
13203 }
13204 }
13205 miNATNetworksStarted++;
13206 }
13207
13208 LogFlowThisFunc(("returns S_OK.\n"));
13209 return S_OK;
13210}
13211
13212/**
13213 * @note Locks this object for writing.
13214 */
13215HRESULT SessionMachine::endPowerUp(LONG aResult)
13216{
13217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13218
13219 if (mData->mSession.mState != SessionState_Locked)
13220 return VBOX_E_INVALID_OBJECT_STATE;
13221
13222 /* Finalize the LaunchVMProcess progress object. */
13223 if (mData->mSession.mProgress)
13224 {
13225 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13226 mData->mSession.mProgress.setNull();
13227 }
13228
13229 if (SUCCEEDED((HRESULT)aResult))
13230 {
13231#ifdef VBOX_WITH_RESOURCE_USAGE_API
13232 /* The VM has been powered up successfully, so it makes sense
13233 * now to offer the performance metrics for a running machine
13234 * object. Doing it earlier wouldn't be safe. */
13235 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13236 mData->mSession.mPID);
13237#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13238 }
13239
13240 return S_OK;
13241}
13242
13243/**
13244 * @note Locks this object for writing.
13245 */
13246HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13247{
13248 LogFlowThisFuncEnter();
13249
13250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13251
13252 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13253 E_FAIL);
13254
13255 /* create a progress object to track operation completion */
13256 ComObjPtr<Progress> pProgress;
13257 pProgress.createObject();
13258 pProgress->init(i_getVirtualBox(),
13259 static_cast<IMachine *>(this) /* aInitiator */,
13260 tr("Stopping the virtual machine"),
13261 FALSE /* aCancelable */);
13262
13263 /* fill in the console task data */
13264 mConsoleTaskData.mLastState = mData->mMachineState;
13265 mConsoleTaskData.mProgress = pProgress;
13266
13267 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13268 i_setMachineState(MachineState_Stopping);
13269
13270 pProgress.queryInterfaceTo(aProgress.asOutParam());
13271
13272 return S_OK;
13273}
13274
13275/**
13276 * @note Locks this object for writing.
13277 */
13278HRESULT SessionMachine::endPoweringDown(LONG aResult,
13279 const com::Utf8Str &aErrMsg)
13280{
13281 HRESULT const hrcResult = (HRESULT)aResult;
13282 LogFlowThisFuncEnter();
13283
13284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13285
13286 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13287 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13288 && mConsoleTaskData.mLastState != MachineState_Null,
13289 E_FAIL);
13290
13291 /*
13292 * On failure, set the state to the state we had when BeginPoweringDown()
13293 * was called (this is expected by Console::PowerDown() and the associated
13294 * task). On success the VM process already changed the state to
13295 * MachineState_PoweredOff, so no need to do anything.
13296 */
13297 if (FAILED(hrcResult))
13298 i_setMachineState(mConsoleTaskData.mLastState);
13299
13300 /* notify the progress object about operation completion */
13301 Assert(mConsoleTaskData.mProgress);
13302 if (SUCCEEDED(hrcResult))
13303 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13304 else
13305 {
13306 if (aErrMsg.length())
13307 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13308 COM_IIDOF(ISession),
13309 getComponentName(),
13310 aErrMsg.c_str());
13311 else
13312 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13313 }
13314
13315 /* clear out the temporary saved state data */
13316 mConsoleTaskData.mLastState = MachineState_Null;
13317 mConsoleTaskData.mProgress.setNull();
13318
13319 LogFlowThisFuncLeave();
13320 return S_OK;
13321}
13322
13323
13324/**
13325 * Goes through the USB filters of the given machine to see if the given
13326 * device matches any filter or not.
13327 *
13328 * @note Locks the same as USBController::hasMatchingFilter() does.
13329 */
13330HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13331 BOOL *aMatched,
13332 ULONG *aMaskedInterfaces)
13333{
13334 LogFlowThisFunc(("\n"));
13335
13336#ifdef VBOX_WITH_USB
13337 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13338#else
13339 NOREF(aDevice);
13340 NOREF(aMaskedInterfaces);
13341 *aMatched = FALSE;
13342#endif
13343
13344 return S_OK;
13345}
13346
13347/**
13348 * @note Locks the same as Host::captureUSBDevice() does.
13349 */
13350HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13351{
13352 LogFlowThisFunc(("\n"));
13353
13354#ifdef VBOX_WITH_USB
13355 /* if captureDeviceForVM() fails, it must have set extended error info */
13356 clearError();
13357 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13358 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13359 return rc;
13360
13361 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13362 AssertReturn(service, E_FAIL);
13363 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13364#else
13365 RT_NOREF(aId, aCaptureFilename);
13366 return E_NOTIMPL;
13367#endif
13368}
13369
13370/**
13371 * @note Locks the same as Host::detachUSBDevice() does.
13372 */
13373HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13374 BOOL aDone)
13375{
13376 LogFlowThisFunc(("\n"));
13377
13378#ifdef VBOX_WITH_USB
13379 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13380 AssertReturn(service, E_FAIL);
13381 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13382#else
13383 NOREF(aId);
13384 NOREF(aDone);
13385 return E_NOTIMPL;
13386#endif
13387}
13388
13389/**
13390 * Inserts all machine filters to the USB proxy service and then calls
13391 * Host::autoCaptureUSBDevices().
13392 *
13393 * Called by Console from the VM process upon VM startup.
13394 *
13395 * @note Locks what called methods lock.
13396 */
13397HRESULT SessionMachine::autoCaptureUSBDevices()
13398{
13399 LogFlowThisFunc(("\n"));
13400
13401#ifdef VBOX_WITH_USB
13402 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13403 AssertComRC(rc);
13404 NOREF(rc);
13405
13406 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13407 AssertReturn(service, E_FAIL);
13408 return service->autoCaptureDevicesForVM(this);
13409#else
13410 return S_OK;
13411#endif
13412}
13413
13414/**
13415 * Removes all machine filters from the USB proxy service and then calls
13416 * Host::detachAllUSBDevices().
13417 *
13418 * Called by Console from the VM process upon normal VM termination or by
13419 * SessionMachine::uninit() upon abnormal VM termination (from under the
13420 * Machine/SessionMachine lock).
13421 *
13422 * @note Locks what called methods lock.
13423 */
13424HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13425{
13426 LogFlowThisFunc(("\n"));
13427
13428#ifdef VBOX_WITH_USB
13429 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13430 AssertComRC(rc);
13431 NOREF(rc);
13432
13433 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13434 AssertReturn(service, E_FAIL);
13435 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13436#else
13437 NOREF(aDone);
13438 return S_OK;
13439#endif
13440}
13441
13442/**
13443 * @note Locks this object for writing.
13444 */
13445HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13446 ComPtr<IProgress> &aProgress)
13447{
13448 LogFlowThisFuncEnter();
13449
13450 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13451 /*
13452 * We don't assert below because it might happen that a non-direct session
13453 * informs us it is closed right after we've been uninitialized -- it's ok.
13454 */
13455
13456 /* get IInternalSessionControl interface */
13457 ComPtr<IInternalSessionControl> control(aSession);
13458
13459 ComAssertRet(!control.isNull(), E_INVALIDARG);
13460
13461 /* Creating a Progress object requires the VirtualBox lock, and
13462 * thus locking it here is required by the lock order rules. */
13463 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13464
13465 if (control == mData->mSession.mDirectControl)
13466 {
13467 /* The direct session is being normally closed by the client process
13468 * ----------------------------------------------------------------- */
13469
13470 /* go to the closing state (essential for all open*Session() calls and
13471 * for #i_checkForDeath()) */
13472 Assert(mData->mSession.mState == SessionState_Locked);
13473 mData->mSession.mState = SessionState_Unlocking;
13474
13475 /* set direct control to NULL to release the remote instance */
13476 mData->mSession.mDirectControl.setNull();
13477 LogFlowThisFunc(("Direct control is set to NULL\n"));
13478
13479 if (mData->mSession.mProgress)
13480 {
13481 /* finalize the progress, someone might wait if a frontend
13482 * closes the session before powering on the VM. */
13483 mData->mSession.mProgress->notifyComplete(E_FAIL,
13484 COM_IIDOF(ISession),
13485 getComponentName(),
13486 tr("The VM session was closed before any attempt to power it on"));
13487 mData->mSession.mProgress.setNull();
13488 }
13489
13490 /* Create the progress object the client will use to wait until
13491 * #i_checkForDeath() is called to uninitialize this session object after
13492 * it releases the IPC semaphore.
13493 * Note! Because we're "reusing" mProgress here, this must be a proxy
13494 * object just like for LaunchVMProcess. */
13495 Assert(mData->mSession.mProgress.isNull());
13496 ComObjPtr<ProgressProxy> progress;
13497 progress.createObject();
13498 ComPtr<IUnknown> pPeer(mPeer);
13499 progress->init(mParent, pPeer,
13500 Bstr(tr("Closing session")).raw(),
13501 FALSE /* aCancelable */);
13502 progress.queryInterfaceTo(aProgress.asOutParam());
13503 mData->mSession.mProgress = progress;
13504 }
13505 else
13506 {
13507 /* the remote session is being normally closed */
13508 bool found = false;
13509 for (Data::Session::RemoteControlList::iterator
13510 it = mData->mSession.mRemoteControls.begin();
13511 it != mData->mSession.mRemoteControls.end();
13512 ++it)
13513 {
13514 if (control == *it)
13515 {
13516 found = true;
13517 // This MUST be erase(it), not remove(*it) as the latter
13518 // triggers a very nasty use after free due to the place where
13519 // the value "lives".
13520 mData->mSession.mRemoteControls.erase(it);
13521 break;
13522 }
13523 }
13524 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13525 E_INVALIDARG);
13526 }
13527
13528 /* signal the client watcher thread, because the client is going away */
13529 mParent->i_updateClientWatcher();
13530
13531 LogFlowThisFuncLeave();
13532 return S_OK;
13533}
13534
13535HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13536 std::vector<com::Utf8Str> &aValues,
13537 std::vector<LONG64> &aTimestamps,
13538 std::vector<com::Utf8Str> &aFlags)
13539{
13540 LogFlowThisFunc(("\n"));
13541
13542#ifdef VBOX_WITH_GUEST_PROPS
13543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13544
13545 size_t cEntries = mHWData->mGuestProperties.size();
13546 aNames.resize(cEntries);
13547 aValues.resize(cEntries);
13548 aTimestamps.resize(cEntries);
13549 aFlags.resize(cEntries);
13550
13551 size_t i = 0;
13552 for (HWData::GuestPropertyMap::const_iterator
13553 it = mHWData->mGuestProperties.begin();
13554 it != mHWData->mGuestProperties.end();
13555 ++it, ++i)
13556 {
13557 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13558 aNames[i] = it->first;
13559 aValues[i] = it->second.strValue;
13560 aTimestamps[i] = it->second.mTimestamp;
13561
13562 /* If it is NULL, keep it NULL. */
13563 if (it->second.mFlags)
13564 {
13565 GuestPropWriteFlags(it->second.mFlags, szFlags);
13566 aFlags[i] = szFlags;
13567 }
13568 else
13569 aFlags[i] = "";
13570 }
13571 return S_OK;
13572#else
13573 ReturnComNotImplemented();
13574#endif
13575}
13576
13577HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13578 const com::Utf8Str &aValue,
13579 LONG64 aTimestamp,
13580 const com::Utf8Str &aFlags)
13581{
13582 LogFlowThisFunc(("\n"));
13583
13584#ifdef VBOX_WITH_GUEST_PROPS
13585 try
13586 {
13587 /*
13588 * Convert input up front.
13589 */
13590 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13591 if (aFlags.length())
13592 {
13593 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13594 AssertRCReturn(vrc, E_INVALIDARG);
13595 }
13596
13597 /*
13598 * Now grab the object lock, validate the state and do the update.
13599 */
13600
13601 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13602
13603 if (!Global::IsOnline(mData->mMachineState))
13604 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13605
13606 i_setModified(IsModified_MachineData);
13607 mHWData.backup();
13608
13609 bool fDelete = !aValue.length();
13610 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13611 if (it != mHWData->mGuestProperties.end())
13612 {
13613 if (!fDelete)
13614 {
13615 it->second.strValue = aValue;
13616 it->second.mTimestamp = aTimestamp;
13617 it->second.mFlags = fFlags;
13618 }
13619 else
13620 mHWData->mGuestProperties.erase(it);
13621
13622 mData->mGuestPropertiesModified = TRUE;
13623 }
13624 else if (!fDelete)
13625 {
13626 HWData::GuestProperty prop;
13627 prop.strValue = aValue;
13628 prop.mTimestamp = aTimestamp;
13629 prop.mFlags = fFlags;
13630
13631 mHWData->mGuestProperties[aName] = prop;
13632 mData->mGuestPropertiesModified = TRUE;
13633 }
13634
13635 alock.release();
13636
13637 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13638 }
13639 catch (...)
13640 {
13641 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13642 }
13643 return S_OK;
13644#else
13645 ReturnComNotImplemented();
13646#endif
13647}
13648
13649
13650HRESULT SessionMachine::lockMedia()
13651{
13652 AutoMultiWriteLock2 alock(this->lockHandle(),
13653 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13654
13655 AssertReturn( mData->mMachineState == MachineState_Starting
13656 || mData->mMachineState == MachineState_Restoring
13657 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13658
13659 clearError();
13660 alock.release();
13661 return i_lockMedia();
13662}
13663
13664HRESULT SessionMachine::unlockMedia()
13665{
13666 HRESULT hrc = i_unlockMedia();
13667 return hrc;
13668}
13669
13670HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13671 ComPtr<IMediumAttachment> &aNewAttachment)
13672{
13673 // request the host lock first, since might be calling Host methods for getting host drives;
13674 // next, protect the media tree all the while we're in here, as well as our member variables
13675 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13676 this->lockHandle(),
13677 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13678
13679 IMediumAttachment *iAttach = aAttachment;
13680 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13681
13682 Utf8Str ctrlName;
13683 LONG lPort;
13684 LONG lDevice;
13685 bool fTempEject;
13686 {
13687 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13688
13689 /* Need to query the details first, as the IMediumAttachment reference
13690 * might be to the original settings, which we are going to change. */
13691 ctrlName = pAttach->i_getControllerName();
13692 lPort = pAttach->i_getPort();
13693 lDevice = pAttach->i_getDevice();
13694 fTempEject = pAttach->i_getTempEject();
13695 }
13696
13697 if (!fTempEject)
13698 {
13699 /* Remember previously mounted medium. The medium before taking the
13700 * backup is not necessarily the same thing. */
13701 ComObjPtr<Medium> oldmedium;
13702 oldmedium = pAttach->i_getMedium();
13703
13704 i_setModified(IsModified_Storage);
13705 mMediumAttachments.backup();
13706
13707 // The backup operation makes the pAttach reference point to the
13708 // old settings. Re-get the correct reference.
13709 pAttach = i_findAttachment(*mMediumAttachments.data(),
13710 ctrlName,
13711 lPort,
13712 lDevice);
13713
13714 {
13715 AutoCaller autoAttachCaller(this);
13716 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13717
13718 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13719 if (!oldmedium.isNull())
13720 oldmedium->i_removeBackReference(mData->mUuid);
13721
13722 pAttach->i_updateMedium(NULL);
13723 pAttach->i_updateEjected();
13724 }
13725
13726 i_setModified(IsModified_Storage);
13727 }
13728 else
13729 {
13730 {
13731 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13732 pAttach->i_updateEjected();
13733 }
13734 }
13735
13736 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13737
13738 return S_OK;
13739}
13740
13741HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13742 com::Utf8Str &aResult)
13743{
13744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13745
13746 HRESULT hr = S_OK;
13747
13748 if (!mAuthLibCtx.hAuthLibrary)
13749 {
13750 /* Load the external authentication library. */
13751 Bstr authLibrary;
13752 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13753
13754 Utf8Str filename = authLibrary;
13755
13756 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13757 if (RT_FAILURE(vrc))
13758 hr = setErrorBoth(E_FAIL, vrc,
13759 tr("Could not load the external authentication library '%s' (%Rrc)"),
13760 filename.c_str(), vrc);
13761 }
13762
13763 /* The auth library might need the machine lock. */
13764 alock.release();
13765
13766 if (FAILED(hr))
13767 return hr;
13768
13769 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13770 {
13771 enum VRDEAuthParams
13772 {
13773 parmUuid = 1,
13774 parmGuestJudgement,
13775 parmUser,
13776 parmPassword,
13777 parmDomain,
13778 parmClientId
13779 };
13780
13781 AuthResult result = AuthResultAccessDenied;
13782
13783 Guid uuid(aAuthParams[parmUuid]);
13784 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13785 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13786
13787 result = AuthLibAuthenticate(&mAuthLibCtx,
13788 uuid.raw(), guestJudgement,
13789 aAuthParams[parmUser].c_str(),
13790 aAuthParams[parmPassword].c_str(),
13791 aAuthParams[parmDomain].c_str(),
13792 u32ClientId);
13793
13794 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13795 size_t cbPassword = aAuthParams[parmPassword].length();
13796 if (cbPassword)
13797 {
13798 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13799 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13800 }
13801
13802 if (result == AuthResultAccessGranted)
13803 aResult = "granted";
13804 else
13805 aResult = "denied";
13806
13807 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13808 aAuthParams[parmUser].c_str(), aResult.c_str()));
13809 }
13810 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13811 {
13812 enum VRDEAuthDisconnectParams
13813 {
13814 parmUuid = 1,
13815 parmClientId
13816 };
13817
13818 Guid uuid(aAuthParams[parmUuid]);
13819 uint32_t u32ClientId = 0;
13820 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13821 }
13822 else
13823 {
13824 hr = E_INVALIDARG;
13825 }
13826
13827 return hr;
13828}
13829
13830// public methods only for internal purposes
13831/////////////////////////////////////////////////////////////////////////////
13832
13833#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13834/**
13835 * Called from the client watcher thread to check for expected or unexpected
13836 * death of the client process that has a direct session to this machine.
13837 *
13838 * On Win32 and on OS/2, this method is called only when we've got the
13839 * mutex (i.e. the client has either died or terminated normally) so it always
13840 * returns @c true (the client is terminated, the session machine is
13841 * uninitialized).
13842 *
13843 * On other platforms, the method returns @c true if the client process has
13844 * terminated normally or abnormally and the session machine was uninitialized,
13845 * and @c false if the client process is still alive.
13846 *
13847 * @note Locks this object for writing.
13848 */
13849bool SessionMachine::i_checkForDeath()
13850{
13851 Uninit::Reason reason;
13852 bool terminated = false;
13853
13854 /* Enclose autoCaller with a block because calling uninit() from under it
13855 * will deadlock. */
13856 {
13857 AutoCaller autoCaller(this);
13858 if (!autoCaller.isOk())
13859 {
13860 /* return true if not ready, to cause the client watcher to exclude
13861 * the corresponding session from watching */
13862 LogFlowThisFunc(("Already uninitialized!\n"));
13863 return true;
13864 }
13865
13866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13867
13868 /* Determine the reason of death: if the session state is Closing here,
13869 * everything is fine. Otherwise it means that the client did not call
13870 * OnSessionEnd() before it released the IPC semaphore. This may happen
13871 * either because the client process has abnormally terminated, or
13872 * because it simply forgot to call ISession::Close() before exiting. We
13873 * threat the latter also as an abnormal termination (see
13874 * Session::uninit() for details). */
13875 reason = mData->mSession.mState == SessionState_Unlocking ?
13876 Uninit::Normal :
13877 Uninit::Abnormal;
13878
13879 if (mClientToken)
13880 terminated = mClientToken->release();
13881 } /* AutoCaller block */
13882
13883 if (terminated)
13884 uninit(reason);
13885
13886 return terminated;
13887}
13888
13889void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13890{
13891 LogFlowThisFunc(("\n"));
13892
13893 strTokenId.setNull();
13894
13895 AutoCaller autoCaller(this);
13896 AssertComRCReturnVoid(autoCaller.rc());
13897
13898 Assert(mClientToken);
13899 if (mClientToken)
13900 mClientToken->getId(strTokenId);
13901}
13902#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13903IToken *SessionMachine::i_getToken()
13904{
13905 LogFlowThisFunc(("\n"));
13906
13907 AutoCaller autoCaller(this);
13908 AssertComRCReturn(autoCaller.rc(), NULL);
13909
13910 Assert(mClientToken);
13911 if (mClientToken)
13912 return mClientToken->getToken();
13913 else
13914 return NULL;
13915}
13916#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13917
13918Machine::ClientToken *SessionMachine::i_getClientToken()
13919{
13920 LogFlowThisFunc(("\n"));
13921
13922 AutoCaller autoCaller(this);
13923 AssertComRCReturn(autoCaller.rc(), NULL);
13924
13925 return mClientToken;
13926}
13927
13928
13929/**
13930 * @note Locks this object for reading.
13931 */
13932HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13933{
13934 LogFlowThisFunc(("\n"));
13935
13936 AutoCaller autoCaller(this);
13937 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13938
13939 ComPtr<IInternalSessionControl> directControl;
13940 {
13941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13942 if (mData->mSession.mLockType == LockType_VM)
13943 directControl = mData->mSession.mDirectControl;
13944 }
13945
13946 /* ignore notifications sent after #OnSessionEnd() is called */
13947 if (!directControl)
13948 return S_OK;
13949
13950 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13951}
13952
13953/**
13954 * @note Locks this object for reading.
13955 */
13956HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13957 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13958 const Utf8Str &aGuestIp, LONG aGuestPort)
13959{
13960 LogFlowThisFunc(("\n"));
13961
13962 AutoCaller autoCaller(this);
13963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13964
13965 ComPtr<IInternalSessionControl> directControl;
13966 {
13967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13968 if (mData->mSession.mLockType == LockType_VM)
13969 directControl = mData->mSession.mDirectControl;
13970 }
13971
13972 /* ignore notifications sent after #OnSessionEnd() is called */
13973 if (!directControl)
13974 return S_OK;
13975 /*
13976 * instead acting like callback we ask IVirtualBox deliver corresponding event
13977 */
13978
13979 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13980 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13981 return S_OK;
13982}
13983
13984/**
13985 * @note Locks this object for reading.
13986 */
13987HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13988{
13989 LogFlowThisFunc(("\n"));
13990
13991 AutoCaller autoCaller(this);
13992 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13993
13994 ComPtr<IInternalSessionControl> directControl;
13995 {
13996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13997 if (mData->mSession.mLockType == LockType_VM)
13998 directControl = mData->mSession.mDirectControl;
13999 }
14000
14001 /* ignore notifications sent after #OnSessionEnd() is called */
14002 if (!directControl)
14003 return S_OK;
14004
14005 return directControl->OnAudioAdapterChange(audioAdapter);
14006}
14007
14008/**
14009 * @note Locks this object for reading.
14010 */
14011HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14012{
14013 LogFlowThisFunc(("\n"));
14014
14015 AutoCaller autoCaller(this);
14016 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14017
14018 ComPtr<IInternalSessionControl> directControl;
14019 {
14020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14021 if (mData->mSession.mLockType == LockType_VM)
14022 directControl = mData->mSession.mDirectControl;
14023 }
14024
14025 /* ignore notifications sent after #OnSessionEnd() is called */
14026 if (!directControl)
14027 return S_OK;
14028
14029 return directControl->OnSerialPortChange(serialPort);
14030}
14031
14032/**
14033 * @note Locks this object for reading.
14034 */
14035HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14036{
14037 LogFlowThisFunc(("\n"));
14038
14039 AutoCaller autoCaller(this);
14040 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14041
14042 ComPtr<IInternalSessionControl> directControl;
14043 {
14044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14045 if (mData->mSession.mLockType == LockType_VM)
14046 directControl = mData->mSession.mDirectControl;
14047 }
14048
14049 /* ignore notifications sent after #OnSessionEnd() is called */
14050 if (!directControl)
14051 return S_OK;
14052
14053 return directControl->OnParallelPortChange(parallelPort);
14054}
14055
14056/**
14057 * @note Locks this object for reading.
14058 */
14059HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14060{
14061 LogFlowThisFunc(("\n"));
14062
14063 AutoCaller autoCaller(this);
14064 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14065
14066 ComPtr<IInternalSessionControl> directControl;
14067 {
14068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14069 if (mData->mSession.mLockType == LockType_VM)
14070 directControl = mData->mSession.mDirectControl;
14071 }
14072
14073 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14074
14075 /* ignore notifications sent after #OnSessionEnd() is called */
14076 if (!directControl)
14077 return S_OK;
14078
14079 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14080}
14081
14082/**
14083 * @note Locks this object for reading.
14084 */
14085HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14086{
14087 LogFlowThisFunc(("\n"));
14088
14089 AutoCaller autoCaller(this);
14090 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14091
14092 ComPtr<IInternalSessionControl> directControl;
14093 {
14094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14095 if (mData->mSession.mLockType == LockType_VM)
14096 directControl = mData->mSession.mDirectControl;
14097 }
14098
14099 mParent->i_onMediumChanged(aAttachment);
14100
14101 /* ignore notifications sent after #OnSessionEnd() is called */
14102 if (!directControl)
14103 return S_OK;
14104
14105 return directControl->OnMediumChange(aAttachment, aForce);
14106}
14107
14108HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14109{
14110 LogFlowThisFunc(("\n"));
14111
14112 AutoCaller autoCaller(this);
14113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14114
14115 ComPtr<IInternalSessionControl> directControl;
14116 {
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118 if (mData->mSession.mLockType == LockType_VM)
14119 directControl = mData->mSession.mDirectControl;
14120 }
14121
14122 /* ignore notifications sent after #OnSessionEnd() is called */
14123 if (!directControl)
14124 return S_OK;
14125
14126 return directControl->OnVMProcessPriorityChange(aPriority);
14127}
14128
14129/**
14130 * @note Locks this object for reading.
14131 */
14132HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14133{
14134 LogFlowThisFunc(("\n"));
14135
14136 AutoCaller autoCaller(this);
14137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14138
14139 ComPtr<IInternalSessionControl> directControl;
14140 {
14141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14142 if (mData->mSession.mLockType == LockType_VM)
14143 directControl = mData->mSession.mDirectControl;
14144 }
14145
14146 /* ignore notifications sent after #OnSessionEnd() is called */
14147 if (!directControl)
14148 return S_OK;
14149
14150 return directControl->OnCPUChange(aCPU, aRemove);
14151}
14152
14153HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14154{
14155 LogFlowThisFunc(("\n"));
14156
14157 AutoCaller autoCaller(this);
14158 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14159
14160 ComPtr<IInternalSessionControl> directControl;
14161 {
14162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14163 if (mData->mSession.mLockType == LockType_VM)
14164 directControl = mData->mSession.mDirectControl;
14165 }
14166
14167 /* ignore notifications sent after #OnSessionEnd() is called */
14168 if (!directControl)
14169 return S_OK;
14170
14171 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14172}
14173
14174/**
14175 * @note Locks this object for reading.
14176 */
14177HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14178{
14179 LogFlowThisFunc(("\n"));
14180
14181 AutoCaller autoCaller(this);
14182 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14183
14184 ComPtr<IInternalSessionControl> directControl;
14185 {
14186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14187 if (mData->mSession.mLockType == LockType_VM)
14188 directControl = mData->mSession.mDirectControl;
14189 }
14190
14191 /* ignore notifications sent after #OnSessionEnd() is called */
14192 if (!directControl)
14193 return S_OK;
14194
14195 return directControl->OnVRDEServerChange(aRestart);
14196}
14197
14198/**
14199 * @note Locks this object for reading.
14200 */
14201HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14202{
14203 LogFlowThisFunc(("\n"));
14204
14205 AutoCaller autoCaller(this);
14206 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14207
14208 ComPtr<IInternalSessionControl> directControl;
14209 {
14210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14211 if (mData->mSession.mLockType == LockType_VM)
14212 directControl = mData->mSession.mDirectControl;
14213 }
14214
14215 /* ignore notifications sent after #OnSessionEnd() is called */
14216 if (!directControl)
14217 return S_OK;
14218
14219 return directControl->OnRecordingChange(aEnable);
14220}
14221
14222/**
14223 * @note Locks this object for reading.
14224 */
14225HRESULT SessionMachine::i_onUSBControllerChange()
14226{
14227 LogFlowThisFunc(("\n"));
14228
14229 AutoCaller autoCaller(this);
14230 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14231
14232 ComPtr<IInternalSessionControl> directControl;
14233 {
14234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14235 if (mData->mSession.mLockType == LockType_VM)
14236 directControl = mData->mSession.mDirectControl;
14237 }
14238
14239 /* ignore notifications sent after #OnSessionEnd() is called */
14240 if (!directControl)
14241 return S_OK;
14242
14243 return directControl->OnUSBControllerChange();
14244}
14245
14246/**
14247 * @note Locks this object for reading.
14248 */
14249HRESULT SessionMachine::i_onSharedFolderChange()
14250{
14251 LogFlowThisFunc(("\n"));
14252
14253 AutoCaller autoCaller(this);
14254 AssertComRCReturnRC(autoCaller.rc());
14255
14256 ComPtr<IInternalSessionControl> directControl;
14257 {
14258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14259 if (mData->mSession.mLockType == LockType_VM)
14260 directControl = mData->mSession.mDirectControl;
14261 }
14262
14263 /* ignore notifications sent after #OnSessionEnd() is called */
14264 if (!directControl)
14265 return S_OK;
14266
14267 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14268}
14269
14270/**
14271 * @note Locks this object for reading.
14272 */
14273HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14274{
14275 LogFlowThisFunc(("\n"));
14276
14277 AutoCaller autoCaller(this);
14278 AssertComRCReturnRC(autoCaller.rc());
14279
14280 ComPtr<IInternalSessionControl> directControl;
14281 {
14282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14283 if (mData->mSession.mLockType == LockType_VM)
14284 directControl = mData->mSession.mDirectControl;
14285 }
14286
14287 /* ignore notifications sent after #OnSessionEnd() is called */
14288 if (!directControl)
14289 return S_OK;
14290
14291 return directControl->OnClipboardModeChange(aClipboardMode);
14292}
14293
14294/**
14295 * @note Locks this object for reading.
14296 */
14297HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14298{
14299 LogFlowThisFunc(("\n"));
14300
14301 AutoCaller autoCaller(this);
14302 AssertComRCReturnRC(autoCaller.rc());
14303
14304 ComPtr<IInternalSessionControl> directControl;
14305 {
14306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14307 if (mData->mSession.mLockType == LockType_VM)
14308 directControl = mData->mSession.mDirectControl;
14309 }
14310
14311 /* ignore notifications sent after #OnSessionEnd() is called */
14312 if (!directControl)
14313 return S_OK;
14314
14315 return directControl->OnClipboardFileTransferModeChange(aEnable);
14316}
14317
14318/**
14319 * @note Locks this object for reading.
14320 */
14321HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14322{
14323 LogFlowThisFunc(("\n"));
14324
14325 AutoCaller autoCaller(this);
14326 AssertComRCReturnRC(autoCaller.rc());
14327
14328 ComPtr<IInternalSessionControl> directControl;
14329 {
14330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14331 if (mData->mSession.mLockType == LockType_VM)
14332 directControl = mData->mSession.mDirectControl;
14333 }
14334
14335 /* ignore notifications sent after #OnSessionEnd() is called */
14336 if (!directControl)
14337 return S_OK;
14338
14339 return directControl->OnDnDModeChange(aDnDMode);
14340}
14341
14342/**
14343 * @note Locks this object for reading.
14344 */
14345HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14346{
14347 LogFlowThisFunc(("\n"));
14348
14349 AutoCaller autoCaller(this);
14350 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14351
14352 ComPtr<IInternalSessionControl> directControl;
14353 {
14354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14355 if (mData->mSession.mLockType == LockType_VM)
14356 directControl = mData->mSession.mDirectControl;
14357 }
14358
14359 /* ignore notifications sent after #OnSessionEnd() is called */
14360 if (!directControl)
14361 return S_OK;
14362
14363 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14364}
14365
14366/**
14367 * @note Locks this object for reading.
14368 */
14369HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14370{
14371 LogFlowThisFunc(("\n"));
14372
14373 AutoCaller autoCaller(this);
14374 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14375
14376 ComPtr<IInternalSessionControl> directControl;
14377 {
14378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14379 if (mData->mSession.mLockType == LockType_VM)
14380 directControl = mData->mSession.mDirectControl;
14381 }
14382
14383 /* ignore notifications sent after #OnSessionEnd() is called */
14384 if (!directControl)
14385 return S_OK;
14386
14387 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14388}
14389
14390/**
14391 * Returns @c true if this machine's USB controller reports it has a matching
14392 * filter for the given USB device and @c false otherwise.
14393 *
14394 * @note locks this object for reading.
14395 */
14396bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14397{
14398 AutoCaller autoCaller(this);
14399 /* silently return if not ready -- this method may be called after the
14400 * direct machine session has been called */
14401 if (!autoCaller.isOk())
14402 return false;
14403
14404#ifdef VBOX_WITH_USB
14405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14406
14407 switch (mData->mMachineState)
14408 {
14409 case MachineState_Starting:
14410 case MachineState_Restoring:
14411 case MachineState_TeleportingIn:
14412 case MachineState_Paused:
14413 case MachineState_Running:
14414 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14415 * elsewhere... */
14416 alock.release();
14417 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14418 default: break;
14419 }
14420#else
14421 NOREF(aDevice);
14422 NOREF(aMaskedIfs);
14423#endif
14424 return false;
14425}
14426
14427/**
14428 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14429 */
14430HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14431 IVirtualBoxErrorInfo *aError,
14432 ULONG aMaskedIfs,
14433 const com::Utf8Str &aCaptureFilename)
14434{
14435 LogFlowThisFunc(("\n"));
14436
14437 AutoCaller autoCaller(this);
14438
14439 /* This notification may happen after the machine object has been
14440 * uninitialized (the session was closed), so don't assert. */
14441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14442
14443 ComPtr<IInternalSessionControl> directControl;
14444 {
14445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14446 if (mData->mSession.mLockType == LockType_VM)
14447 directControl = mData->mSession.mDirectControl;
14448 }
14449
14450 /* fail on notifications sent after #OnSessionEnd() is called, it is
14451 * expected by the caller */
14452 if (!directControl)
14453 return E_FAIL;
14454
14455 /* No locks should be held at this point. */
14456 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14457 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14458
14459 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14460}
14461
14462/**
14463 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14464 */
14465HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14466 IVirtualBoxErrorInfo *aError)
14467{
14468 LogFlowThisFunc(("\n"));
14469
14470 AutoCaller autoCaller(this);
14471
14472 /* This notification may happen after the machine object has been
14473 * uninitialized (the session was closed), so don't assert. */
14474 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14475
14476 ComPtr<IInternalSessionControl> directControl;
14477 {
14478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14479 if (mData->mSession.mLockType == LockType_VM)
14480 directControl = mData->mSession.mDirectControl;
14481 }
14482
14483 /* fail on notifications sent after #OnSessionEnd() is called, it is
14484 * expected by the caller */
14485 if (!directControl)
14486 return E_FAIL;
14487
14488 /* No locks should be held at this point. */
14489 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14490 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14491
14492 return directControl->OnUSBDeviceDetach(aId, aError);
14493}
14494
14495// protected methods
14496/////////////////////////////////////////////////////////////////////////////
14497
14498/**
14499 * Deletes the given file if it is no longer in use by either the current machine state
14500 * (if the machine is "saved") or any of the machine's snapshots.
14501 *
14502 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14503 * but is different for each SnapshotMachine. When calling this, the order of calling this
14504 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14505 * is therefore critical. I know, it's all rather messy.
14506 *
14507 * @param strStateFile
14508 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14509 * the test for whether the saved state file is in use.
14510 */
14511void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14512 Snapshot *pSnapshotToIgnore)
14513{
14514 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14515 if ( (strStateFile.isNotEmpty())
14516 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14517 )
14518 // ... and it must also not be shared with other snapshots
14519 if ( !mData->mFirstSnapshot
14520 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14521 // this checks the SnapshotMachine's state file paths
14522 )
14523 RTFileDelete(strStateFile.c_str());
14524}
14525
14526/**
14527 * Locks the attached media.
14528 *
14529 * All attached hard disks are locked for writing and DVD/floppy are locked for
14530 * reading. Parents of attached hard disks (if any) are locked for reading.
14531 *
14532 * This method also performs accessibility check of all media it locks: if some
14533 * media is inaccessible, the method will return a failure and a bunch of
14534 * extended error info objects per each inaccessible medium.
14535 *
14536 * Note that this method is atomic: if it returns a success, all media are
14537 * locked as described above; on failure no media is locked at all (all
14538 * succeeded individual locks will be undone).
14539 *
14540 * The caller is responsible for doing the necessary state sanity checks.
14541 *
14542 * The locks made by this method must be undone by calling #unlockMedia() when
14543 * no more needed.
14544 */
14545HRESULT SessionMachine::i_lockMedia()
14546{
14547 AutoCaller autoCaller(this);
14548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14549
14550 AutoMultiWriteLock2 alock(this->lockHandle(),
14551 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14552
14553 /* bail out if trying to lock things with already set up locking */
14554 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14555
14556 MultiResult mrc(S_OK);
14557
14558 /* Collect locking information for all medium objects attached to the VM. */
14559 for (MediumAttachmentList::const_iterator
14560 it = mMediumAttachments->begin();
14561 it != mMediumAttachments->end();
14562 ++it)
14563 {
14564 MediumAttachment *pAtt = *it;
14565 DeviceType_T devType = pAtt->i_getType();
14566 Medium *pMedium = pAtt->i_getMedium();
14567
14568 MediumLockList *pMediumLockList(new MediumLockList());
14569 // There can be attachments without a medium (floppy/dvd), and thus
14570 // it's impossible to create a medium lock list. It still makes sense
14571 // to have the empty medium lock list in the map in case a medium is
14572 // attached later.
14573 if (pMedium != NULL)
14574 {
14575 MediumType_T mediumType = pMedium->i_getType();
14576 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14577 || mediumType == MediumType_Shareable;
14578 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14579
14580 alock.release();
14581 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14582 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14583 false /* fMediumLockWriteAll */,
14584 NULL,
14585 *pMediumLockList);
14586 alock.acquire();
14587 if (FAILED(mrc))
14588 {
14589 delete pMediumLockList;
14590 mData->mSession.mLockedMedia.Clear();
14591 break;
14592 }
14593 }
14594
14595 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14596 if (FAILED(rc))
14597 {
14598 mData->mSession.mLockedMedia.Clear();
14599 mrc = setError(rc,
14600 tr("Collecting locking information for all attached media failed"));
14601 break;
14602 }
14603 }
14604
14605 if (SUCCEEDED(mrc))
14606 {
14607 /* Now lock all media. If this fails, nothing is locked. */
14608 alock.release();
14609 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14610 alock.acquire();
14611 if (FAILED(rc))
14612 {
14613 mrc = setError(rc,
14614 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14615 }
14616 }
14617
14618 return mrc;
14619}
14620
14621/**
14622 * Undoes the locks made by by #lockMedia().
14623 */
14624HRESULT SessionMachine::i_unlockMedia()
14625{
14626 AutoCaller autoCaller(this);
14627 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14628
14629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14630
14631 /* we may be holding important error info on the current thread;
14632 * preserve it */
14633 ErrorInfoKeeper eik;
14634
14635 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14636 AssertComRC(rc);
14637 return rc;
14638}
14639
14640/**
14641 * Helper to change the machine state (reimplementation).
14642 *
14643 * @note Locks this object for writing.
14644 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14645 * it can cause crashes in random places due to unexpectedly committing
14646 * the current settings. The caller is responsible for that. The call
14647 * to saveStateSettings is fine, because this method does not commit.
14648 */
14649HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14650{
14651 LogFlowThisFuncEnter();
14652
14653 AutoCaller autoCaller(this);
14654 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14655
14656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14657
14658 MachineState_T oldMachineState = mData->mMachineState;
14659
14660 AssertMsgReturn(oldMachineState != aMachineState,
14661 ("oldMachineState=%s, aMachineState=%s\n",
14662 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14663 E_FAIL);
14664
14665 HRESULT rc = S_OK;
14666
14667 int stsFlags = 0;
14668 bool deleteSavedState = false;
14669
14670 /* detect some state transitions */
14671
14672 if ( ( ( oldMachineState == MachineState_Saved
14673 || oldMachineState == MachineState_AbortedSaved
14674 )
14675 && aMachineState == MachineState_Restoring
14676 )
14677 || ( ( oldMachineState == MachineState_PoweredOff
14678 || oldMachineState == MachineState_Teleported
14679 || oldMachineState == MachineState_Aborted
14680 )
14681 && ( aMachineState == MachineState_TeleportingIn
14682 || aMachineState == MachineState_Starting
14683 )
14684 )
14685 )
14686 {
14687 /* The EMT thread is about to start */
14688
14689 /* Nothing to do here for now... */
14690
14691 /// @todo NEWMEDIA don't let mDVDDrive and other children
14692 /// change anything when in the Starting/Restoring state
14693 }
14694 else if ( ( oldMachineState == MachineState_Running
14695 || oldMachineState == MachineState_Paused
14696 || oldMachineState == MachineState_Teleporting
14697 || oldMachineState == MachineState_OnlineSnapshotting
14698 || oldMachineState == MachineState_LiveSnapshotting
14699 || oldMachineState == MachineState_Stuck
14700 || oldMachineState == MachineState_Starting
14701 || oldMachineState == MachineState_Stopping
14702 || oldMachineState == MachineState_Saving
14703 || oldMachineState == MachineState_Restoring
14704 || oldMachineState == MachineState_TeleportingPausedVM
14705 || oldMachineState == MachineState_TeleportingIn
14706 )
14707 && ( aMachineState == MachineState_PoweredOff
14708 || aMachineState == MachineState_Saved
14709 || aMachineState == MachineState_Teleported
14710 || aMachineState == MachineState_Aborted
14711 || aMachineState == MachineState_AbortedSaved
14712 )
14713 )
14714 {
14715 /* The EMT thread has just stopped, unlock attached media. Note that as
14716 * opposed to locking that is done from Console, we do unlocking here
14717 * because the VM process may have aborted before having a chance to
14718 * properly unlock all media it locked. */
14719
14720 unlockMedia();
14721 }
14722
14723 if (oldMachineState == MachineState_Restoring)
14724 {
14725 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14726 {
14727 /*
14728 * delete the saved state file once the machine has finished
14729 * restoring from it (note that Console sets the state from
14730 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14731 * to give the user an ability to fix an error and retry --
14732 * we keep the saved state file in this case)
14733 */
14734 deleteSavedState = true;
14735 }
14736 }
14737 else if ( oldMachineState == MachineState_Saved
14738 && ( aMachineState == MachineState_PoweredOff
14739 || aMachineState == MachineState_Teleported
14740 )
14741 )
14742 {
14743 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14744 deleteSavedState = true;
14745 mData->mCurrentStateModified = TRUE;
14746 stsFlags |= SaveSTS_CurStateModified;
14747 }
14748 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14749 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14750
14751 if ( aMachineState == MachineState_Starting
14752 || aMachineState == MachineState_Restoring
14753 || aMachineState == MachineState_TeleportingIn
14754 )
14755 {
14756 /* set the current state modified flag to indicate that the current
14757 * state is no more identical to the state in the
14758 * current snapshot */
14759 if (!mData->mCurrentSnapshot.isNull())
14760 {
14761 mData->mCurrentStateModified = TRUE;
14762 stsFlags |= SaveSTS_CurStateModified;
14763 }
14764 }
14765
14766 if (deleteSavedState)
14767 {
14768 if (mRemoveSavedState)
14769 {
14770 Assert(!mSSData->strStateFilePath.isEmpty());
14771
14772 // it is safe to delete the saved state file if ...
14773 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14774 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14775 // ... none of the snapshots share the saved state file
14776 )
14777 RTFileDelete(mSSData->strStateFilePath.c_str());
14778 }
14779
14780 mSSData->strStateFilePath.setNull();
14781 stsFlags |= SaveSTS_StateFilePath;
14782 }
14783
14784 /* redirect to the underlying peer machine */
14785 mPeer->i_setMachineState(aMachineState);
14786
14787 if ( oldMachineState != MachineState_RestoringSnapshot
14788 && ( aMachineState == MachineState_PoweredOff
14789 || aMachineState == MachineState_Teleported
14790 || aMachineState == MachineState_Aborted
14791 || aMachineState == MachineState_AbortedSaved
14792 || aMachineState == MachineState_Saved))
14793 {
14794 /* the machine has stopped execution
14795 * (or the saved state file was adopted) */
14796 stsFlags |= SaveSTS_StateTimeStamp;
14797 }
14798
14799 if ( ( oldMachineState == MachineState_PoweredOff
14800 || oldMachineState == MachineState_Aborted
14801 || oldMachineState == MachineState_Teleported
14802 )
14803 && aMachineState == MachineState_Saved)
14804 {
14805 /* the saved state file was adopted */
14806 Assert(!mSSData->strStateFilePath.isEmpty());
14807 stsFlags |= SaveSTS_StateFilePath;
14808 }
14809
14810#ifdef VBOX_WITH_GUEST_PROPS
14811 if ( aMachineState == MachineState_PoweredOff
14812 || aMachineState == MachineState_Aborted
14813 || aMachineState == MachineState_Teleported)
14814 {
14815 /* Make sure any transient guest properties get removed from the
14816 * property store on shutdown. */
14817 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14818
14819 /* remove it from the settings representation */
14820 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14821 for (settings::GuestPropertiesList::iterator
14822 it = llGuestProperties.begin();
14823 it != llGuestProperties.end();
14824 /*nothing*/)
14825 {
14826 const settings::GuestProperty &prop = *it;
14827 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14828 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14829 {
14830 it = llGuestProperties.erase(it);
14831 fNeedsSaving = true;
14832 }
14833 else
14834 {
14835 ++it;
14836 }
14837 }
14838
14839 /* Additionally remove it from the HWData representation. Required to
14840 * keep everything in sync, as this is what the API keeps using. */
14841 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14842 for (HWData::GuestPropertyMap::iterator
14843 it = llHWGuestProperties.begin();
14844 it != llHWGuestProperties.end();
14845 /*nothing*/)
14846 {
14847 uint32_t fFlags = it->second.mFlags;
14848 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14849 {
14850 /* iterator where we need to continue after the erase call
14851 * (C++03 is a fact still, and it doesn't return the iterator
14852 * which would allow continuing) */
14853 HWData::GuestPropertyMap::iterator it2 = it;
14854 ++it2;
14855 llHWGuestProperties.erase(it);
14856 it = it2;
14857 fNeedsSaving = true;
14858 }
14859 else
14860 {
14861 ++it;
14862 }
14863 }
14864
14865 if (fNeedsSaving)
14866 {
14867 mData->mCurrentStateModified = TRUE;
14868 stsFlags |= SaveSTS_CurStateModified;
14869 }
14870 }
14871#endif /* VBOX_WITH_GUEST_PROPS */
14872
14873 rc = i_saveStateSettings(stsFlags);
14874
14875 if ( ( oldMachineState != MachineState_PoweredOff
14876 && oldMachineState != MachineState_Aborted
14877 && oldMachineState != MachineState_Teleported
14878 )
14879 && ( aMachineState == MachineState_PoweredOff
14880 || aMachineState == MachineState_Aborted
14881 || aMachineState == MachineState_Teleported
14882 )
14883 )
14884 {
14885 /* we've been shut down for any reason */
14886 /* no special action so far */
14887 }
14888
14889 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
14890 LogFlowThisFuncLeave();
14891 return rc;
14892}
14893
14894/**
14895 * Sends the current machine state value to the VM process.
14896 *
14897 * @note Locks this object for reading, then calls a client process.
14898 */
14899HRESULT SessionMachine::i_updateMachineStateOnClient()
14900{
14901 AutoCaller autoCaller(this);
14902 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14903
14904 ComPtr<IInternalSessionControl> directControl;
14905 {
14906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14907 AssertReturn(!!mData, E_FAIL);
14908 if (mData->mSession.mLockType == LockType_VM)
14909 directControl = mData->mSession.mDirectControl;
14910
14911 /* directControl may be already set to NULL here in #OnSessionEnd()
14912 * called too early by the direct session process while there is still
14913 * some operation (like deleting the snapshot) in progress. The client
14914 * process in this case is waiting inside Session::close() for the
14915 * "end session" process object to complete, while #uninit() called by
14916 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14917 * operation to complete. For now, we accept this inconsistent behavior
14918 * and simply do nothing here. */
14919
14920 if (mData->mSession.mState == SessionState_Unlocking)
14921 return S_OK;
14922 }
14923
14924 /* ignore notifications sent after #OnSessionEnd() is called */
14925 if (!directControl)
14926 return S_OK;
14927
14928 return directControl->UpdateMachineState(mData->mMachineState);
14929}
14930
14931
14932/*static*/
14933HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14934{
14935 va_list args;
14936 va_start(args, pcszMsg);
14937 HRESULT rc = setErrorInternalV(aResultCode,
14938 getStaticClassIID(),
14939 getStaticComponentName(),
14940 pcszMsg, args,
14941 false /* aWarning */,
14942 true /* aLogIt */);
14943 va_end(args);
14944 return rc;
14945}
14946
14947
14948HRESULT Machine::updateState(MachineState_T aState)
14949{
14950 NOREF(aState);
14951 ReturnComNotImplemented();
14952}
14953
14954HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14955{
14956 NOREF(aProgress);
14957 ReturnComNotImplemented();
14958}
14959
14960HRESULT Machine::endPowerUp(LONG aResult)
14961{
14962 NOREF(aResult);
14963 ReturnComNotImplemented();
14964}
14965
14966HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14967{
14968 NOREF(aProgress);
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::endPoweringDown(LONG aResult,
14973 const com::Utf8Str &aErrMsg)
14974{
14975 NOREF(aResult);
14976 NOREF(aErrMsg);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14981 BOOL *aMatched,
14982 ULONG *aMaskedInterfaces)
14983{
14984 NOREF(aDevice);
14985 NOREF(aMatched);
14986 NOREF(aMaskedInterfaces);
14987 ReturnComNotImplemented();
14988
14989}
14990
14991HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14992{
14993 NOREF(aId); NOREF(aCaptureFilename);
14994 ReturnComNotImplemented();
14995}
14996
14997HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14998 BOOL aDone)
14999{
15000 NOREF(aId);
15001 NOREF(aDone);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::autoCaptureUSBDevices()
15006{
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15011{
15012 NOREF(aDone);
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15017 ComPtr<IProgress> &aProgress)
15018{
15019 NOREF(aSession);
15020 NOREF(aProgress);
15021 ReturnComNotImplemented();
15022}
15023
15024HRESULT Machine::finishOnlineMergeMedium()
15025{
15026 ReturnComNotImplemented();
15027}
15028
15029HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15030 std::vector<com::Utf8Str> &aValues,
15031 std::vector<LONG64> &aTimestamps,
15032 std::vector<com::Utf8Str> &aFlags)
15033{
15034 NOREF(aNames);
15035 NOREF(aValues);
15036 NOREF(aTimestamps);
15037 NOREF(aFlags);
15038 ReturnComNotImplemented();
15039}
15040
15041HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15042 const com::Utf8Str &aValue,
15043 LONG64 aTimestamp,
15044 const com::Utf8Str &aFlags)
15045{
15046 NOREF(aName);
15047 NOREF(aValue);
15048 NOREF(aTimestamp);
15049 NOREF(aFlags);
15050 ReturnComNotImplemented();
15051}
15052
15053HRESULT Machine::lockMedia()
15054{
15055 ReturnComNotImplemented();
15056}
15057
15058HRESULT Machine::unlockMedia()
15059{
15060 ReturnComNotImplemented();
15061}
15062
15063HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15064 ComPtr<IMediumAttachment> &aNewAttachment)
15065{
15066 NOREF(aAttachment);
15067 NOREF(aNewAttachment);
15068 ReturnComNotImplemented();
15069}
15070
15071HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15072 ULONG aCpuUser,
15073 ULONG aCpuKernel,
15074 ULONG aCpuIdle,
15075 ULONG aMemTotal,
15076 ULONG aMemFree,
15077 ULONG aMemBalloon,
15078 ULONG aMemShared,
15079 ULONG aMemCache,
15080 ULONG aPagedTotal,
15081 ULONG aMemAllocTotal,
15082 ULONG aMemFreeTotal,
15083 ULONG aMemBalloonTotal,
15084 ULONG aMemSharedTotal,
15085 ULONG aVmNetRx,
15086 ULONG aVmNetTx)
15087{
15088 NOREF(aValidStats);
15089 NOREF(aCpuUser);
15090 NOREF(aCpuKernel);
15091 NOREF(aCpuIdle);
15092 NOREF(aMemTotal);
15093 NOREF(aMemFree);
15094 NOREF(aMemBalloon);
15095 NOREF(aMemShared);
15096 NOREF(aMemCache);
15097 NOREF(aPagedTotal);
15098 NOREF(aMemAllocTotal);
15099 NOREF(aMemFreeTotal);
15100 NOREF(aMemBalloonTotal);
15101 NOREF(aMemSharedTotal);
15102 NOREF(aVmNetRx);
15103 NOREF(aVmNetTx);
15104 ReturnComNotImplemented();
15105}
15106
15107HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15108 com::Utf8Str &aResult)
15109{
15110 NOREF(aAuthParams);
15111 NOREF(aResult);
15112 ReturnComNotImplemented();
15113}
15114
15115com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15116{
15117 com::Utf8Str strControllerName = "Unknown";
15118 switch (aBusType)
15119 {
15120 case StorageBus_IDE:
15121 {
15122 strControllerName = "IDE";
15123 break;
15124 }
15125 case StorageBus_SATA:
15126 {
15127 strControllerName = "SATA";
15128 break;
15129 }
15130 case StorageBus_SCSI:
15131 {
15132 strControllerName = "SCSI";
15133 break;
15134 }
15135 case StorageBus_Floppy:
15136 {
15137 strControllerName = "Floppy";
15138 break;
15139 }
15140 case StorageBus_SAS:
15141 {
15142 strControllerName = "SAS";
15143 break;
15144 }
15145 case StorageBus_USB:
15146 {
15147 strControllerName = "USB";
15148 break;
15149 }
15150 default:
15151 break;
15152 }
15153 return strControllerName;
15154}
15155
15156HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15157{
15158 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15159
15160 AutoCaller autoCaller(this);
15161 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15162
15163 HRESULT rc = S_OK;
15164
15165 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15166 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15167 rc = getUSBDeviceFilters(usbDeviceFilters);
15168 if (FAILED(rc)) return rc;
15169
15170 NOREF(aFlags);
15171 com::Utf8Str osTypeId;
15172 ComObjPtr<GuestOSType> osType = NULL;
15173
15174 /* Get the guest os type as a string from the VB. */
15175 rc = getOSTypeId(osTypeId);
15176 if (FAILED(rc)) return rc;
15177
15178 /* Get the os type obj that coresponds, can be used to get
15179 * the defaults for this guest OS. */
15180 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15181 if (FAILED(rc)) return rc;
15182
15183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15184
15185 /* Let the OS type select 64-bit ness. */
15186 mHWData->mLongMode = osType->i_is64Bit()
15187 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15188
15189 /* Let the OS type enable the X2APIC */
15190 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15191
15192 /* This one covers IOAPICEnabled. */
15193 mBIOSSettings->i_applyDefaults(osType);
15194
15195 /* Initialize default record settings. */
15196 mRecordingSettings->i_applyDefaults();
15197
15198 /* Initialize default BIOS settings here */
15199 /* Hardware virtualization must be ON by default */
15200 mHWData->mAPIC = true;
15201 mHWData->mHWVirtExEnabled = true;
15202
15203 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15204 if (FAILED(rc)) return rc;
15205
15206 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15207 if (FAILED(rc)) return rc;
15208
15209 /* Graphics stuff. */
15210 GraphicsControllerType_T graphicsController;
15211 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15212 if (FAILED(rc)) return rc;
15213
15214 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15215 if (FAILED(rc)) return rc;
15216
15217 ULONG vramSize;
15218 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15219 if (FAILED(rc)) return rc;
15220
15221 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15222 if (FAILED(rc)) return rc;
15223
15224 BOOL fAccelerate2DVideoEnabled;
15225 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15226 if (FAILED(rc)) return rc;
15227
15228 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15229 if (FAILED(rc)) return rc;
15230
15231 BOOL fAccelerate3DEnabled;
15232 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15233 if (FAILED(rc)) return rc;
15234
15235 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15236 if (FAILED(rc)) return rc;
15237
15238 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15239 if (FAILED(rc)) return rc;
15240
15241 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15242 if (FAILED(rc)) return rc;
15243
15244 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15245 if (FAILED(rc)) return rc;
15246
15247 BOOL mRTCUseUTC;
15248 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15249 if (FAILED(rc)) return rc;
15250
15251 setRTCUseUTC(mRTCUseUTC);
15252 if (FAILED(rc)) return rc;
15253
15254 /* the setter does more than just the assignment, so use it */
15255 ChipsetType_T enmChipsetType;
15256 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15257 if (FAILED(rc)) return rc;
15258
15259 rc = COMSETTER(ChipsetType)(enmChipsetType);
15260 if (FAILED(rc)) return rc;
15261
15262 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15263 if (FAILED(rc)) return rc;
15264
15265 /* Apply IOMMU defaults. */
15266 IommuType_T enmIommuType;
15267 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15268 if (FAILED(rc)) return rc;
15269
15270 rc = COMSETTER(IommuType)(enmIommuType);
15271 if (FAILED(rc)) return rc;
15272
15273 /* Apply network adapters defaults */
15274 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15275 mNetworkAdapters[slot]->i_applyDefaults(osType);
15276
15277 /* Apply serial port defaults */
15278 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15279 mSerialPorts[slot]->i_applyDefaults(osType);
15280
15281 /* Apply parallel port defaults - not OS dependent*/
15282 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15283 mParallelPorts[slot]->i_applyDefaults();
15284
15285 /* This one covers the TPM type. */
15286 mTrustedPlatformModule->i_applyDefaults(osType);
15287
15288 /* This one covers secure boot. */
15289 rc = mNvramStore->i_applyDefaults(osType);
15290 if (FAILED(rc)) return rc;
15291
15292 /* Audio stuff. */
15293 AudioControllerType_T audioController;
15294 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15295 if (FAILED(rc)) return rc;
15296
15297 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15298 if (FAILED(rc)) return rc;
15299
15300 AudioCodecType_T audioCodec;
15301 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15302 if (FAILED(rc)) return rc;
15303
15304 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15305 if (FAILED(rc)) return rc;
15306
15307 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15308 if (FAILED(rc)) return rc;
15309
15310 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15311 if (FAILED(rc)) return rc;
15312
15313 /* Storage Controllers */
15314 StorageControllerType_T hdStorageControllerType;
15315 StorageBus_T hdStorageBusType;
15316 StorageControllerType_T dvdStorageControllerType;
15317 StorageBus_T dvdStorageBusType;
15318 BOOL recommendedFloppy;
15319 ComPtr<IStorageController> floppyController;
15320 ComPtr<IStorageController> hdController;
15321 ComPtr<IStorageController> dvdController;
15322 Utf8Str strFloppyName, strDVDName, strHDName;
15323
15324 /* GUI auto generates controller names using bus type. Do the same*/
15325 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15326
15327 /* Floppy recommended? add one. */
15328 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15329 if (FAILED(rc)) return rc;
15330 if (recommendedFloppy)
15331 {
15332 rc = addStorageController(strFloppyName,
15333 StorageBus_Floppy,
15334 floppyController);
15335 if (FAILED(rc)) return rc;
15336 }
15337
15338 /* Setup one DVD storage controller. */
15339 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15340 if (FAILED(rc)) return rc;
15341
15342 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15343 if (FAILED(rc)) return rc;
15344
15345 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15346
15347 rc = addStorageController(strDVDName,
15348 dvdStorageBusType,
15349 dvdController);
15350 if (FAILED(rc)) return rc;
15351
15352 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15353 if (FAILED(rc)) return rc;
15354
15355 /* Setup one HDD storage controller. */
15356 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15357 if (FAILED(rc)) return rc;
15358
15359 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15360 if (FAILED(rc)) return rc;
15361
15362 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15363
15364 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15365 {
15366 rc = addStorageController(strHDName,
15367 hdStorageBusType,
15368 hdController);
15369 if (FAILED(rc)) return rc;
15370
15371 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15372 if (FAILED(rc)) return rc;
15373 }
15374 else
15375 {
15376 /* The HD controller is the same as DVD: */
15377 hdController = dvdController;
15378 }
15379
15380 /* Limit the AHCI port count if it's used because windows has trouble with
15381 * too many ports and other guest (OS X in particular) may take extra long
15382 * boot: */
15383
15384 // pParent = static_cast<Medium*>(aP)
15385 IStorageController *temp = hdController;
15386 ComObjPtr<StorageController> storageController;
15387 storageController = static_cast<StorageController *>(temp);
15388
15389 // tempHDController = aHDController;
15390 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15391 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15392 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15393 storageController->COMSETTER(PortCount)(1);
15394
15395 /* USB stuff */
15396
15397 bool ohciEnabled = false;
15398
15399 ComPtr<IUSBController> usbController;
15400 BOOL recommendedUSB3;
15401 BOOL recommendedUSB;
15402 BOOL usbProxyAvailable;
15403
15404 getUSBProxyAvailable(&usbProxyAvailable);
15405 if (FAILED(rc)) return rc;
15406
15407 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15408 if (FAILED(rc)) return rc;
15409 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15410 if (FAILED(rc)) return rc;
15411
15412 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15413 {
15414#ifdef VBOX_WITH_EXTPACK
15415 /* USB 3.0 is only available if the proper ExtPack is installed. */
15416 ExtPackManager *aManager = mParent->i_getExtPackManager();
15417 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15418 {
15419 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15420 if (FAILED(rc)) return rc;
15421
15422 /* xHci includes OHCI */
15423 ohciEnabled = true;
15424 }
15425#endif
15426 }
15427 if ( !ohciEnabled
15428 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15429 {
15430 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15431 if (FAILED(rc)) return rc;
15432 ohciEnabled = true;
15433
15434#ifdef VBOX_WITH_EXTPACK
15435 /* USB 2.0 is only available if the proper ExtPack is installed.
15436 * Note. Configuring EHCI here and providing messages about
15437 * the missing extpack isn't exactly clean, but it is a
15438 * necessary evil to patch over legacy compatability issues
15439 * introduced by the new distribution model. */
15440 ExtPackManager *manager = mParent->i_getExtPackManager();
15441 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15442 {
15443 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15444 if (FAILED(rc)) return rc;
15445 }
15446#endif
15447 }
15448
15449 /* Set recommended human interface device types: */
15450 BOOL recommendedUSBHID;
15451 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15452 if (FAILED(rc)) return rc;
15453
15454 if (recommendedUSBHID)
15455 {
15456 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15457 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15458 if (!ohciEnabled && !usbDeviceFilters.isNull())
15459 {
15460 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15461 if (FAILED(rc)) return rc;
15462 }
15463 }
15464
15465 BOOL recommendedUSBTablet;
15466 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15467 if (FAILED(rc)) return rc;
15468
15469 if (recommendedUSBTablet)
15470 {
15471 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15472 if (!ohciEnabled && !usbDeviceFilters.isNull())
15473 {
15474 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15475 if (FAILED(rc)) return rc;
15476 }
15477 }
15478
15479 /* Enable the VMMDev testing feature for bootsector VMs: */
15480 if (osTypeId == "VBoxBS_64")
15481 {
15482 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15483 if (FAILED(rc))
15484 return rc;
15485 }
15486
15487 return S_OK;
15488}
15489
15490/* This isn't handled entirely by the wrapper generator yet. */
15491#ifdef VBOX_WITH_XPCOM
15492NS_DECL_CLASSINFO(SessionMachine)
15493NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15494
15495NS_DECL_CLASSINFO(SnapshotMachine)
15496NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15497#endif
Note: See TracBrowser for help on using the repository browser.

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