VirtualBox

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

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

Main/Machine: Additional IMachine methods need machine state check
enforcement. bugref:5490

All IMachine methods and attribute setters which modify a VM's settings
or configuration need to detect and reject attempts to use an immutable
Machine object rather than a mutable session object from
ISession::machine(). The following routines: setCPUExecutionCap(),
setMemoryBalloonSize(), setPageFusionEnabled(), setClipboardMode(),
setClipboardFileTransfersEnabled(), setDnDMode(), hotPlugCPU(), and
hotUnplugCPU() need updating to include a call to i_checkStateDependency()
to enforce this.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 543.6 KB
Line 
1/* $Id: MachineImpl.cpp 94092 2022-03-04 22:09:02Z 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 rc = i_checkStateDependency(MutableOrRunningStateDep);
1603 if (FAILED(rc)) return rc;
1604
1605 alock.release();
1606 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1607 alock.acquire();
1608 if (FAILED(rc)) return rc;
1609
1610 i_setModified(IsModified_MachineData);
1611 mHWData.backup();
1612 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1613
1614 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1615 if (Global::IsOnline(mData->mMachineState))
1616 i_saveSettings(NULL, alock);
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1622{
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1626
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1631{
1632 HRESULT rc = S_OK;
1633
1634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 rc = i_checkStateDependency(MutableStateDep);
1637 if (FAILED(rc)) return rc;
1638
1639 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1640 {
1641 if (aCPUHotPlugEnabled)
1642 {
1643 i_setModified(IsModified_MachineData);
1644 mHWData.backup();
1645
1646 /* Add the amount of CPUs currently attached */
1647 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1648 mHWData->mCPUAttached[i] = true;
1649 }
1650 else
1651 {
1652 /*
1653 * We can disable hotplug only if the amount of maximum CPUs is equal
1654 * to the amount of attached CPUs
1655 */
1656 unsigned cCpusAttached = 0;
1657 unsigned iHighestId = 0;
1658
1659 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1660 {
1661 if (mHWData->mCPUAttached[i])
1662 {
1663 cCpusAttached++;
1664 iHighestId = i;
1665 }
1666 }
1667
1668 if ( (cCpusAttached != mHWData->mCPUCount)
1669 || (iHighestId >= mHWData->mCPUCount))
1670 return setError(E_INVALIDARG,
1671 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1672
1673 i_setModified(IsModified_MachineData);
1674 mHWData.backup();
1675 }
1676 }
1677
1678 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1679
1680 return rc;
1681}
1682
1683HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1684{
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1688
1689 return S_OK;
1690}
1691
1692HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1693{
1694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1695
1696 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1697 if (SUCCEEDED(hrc))
1698 {
1699 i_setModified(IsModified_MachineData);
1700 mHWData.backup();
1701 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1702 }
1703 return hrc;
1704}
1705
1706HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1707{
1708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1709 aCPUProfile = mHWData->mCpuProfile;
1710 return S_OK;
1711}
1712
1713HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1714{
1715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1716 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1717 if (SUCCEEDED(hrc))
1718 {
1719 i_setModified(IsModified_MachineData);
1720 mHWData.backup();
1721 /* Empty equals 'host'. */
1722 if (aCPUProfile.isNotEmpty())
1723 mHWData->mCpuProfile = aCPUProfile;
1724 else
1725 mHWData->mCpuProfile = "host";
1726 }
1727 return hrc;
1728}
1729
1730HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1731{
1732#ifdef VBOX_WITH_USB_CARDREADER
1733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1734
1735 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1736
1737 return S_OK;
1738#else
1739 NOREF(aEmulatedUSBCardReaderEnabled);
1740 return E_NOTIMPL;
1741#endif
1742}
1743
1744HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1745{
1746#ifdef VBOX_WITH_USB_CARDREADER
1747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1750 if (FAILED(rc)) return rc;
1751
1752 i_setModified(IsModified_MachineData);
1753 mHWData.backup();
1754 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1755
1756 return S_OK;
1757#else
1758 NOREF(aEmulatedUSBCardReaderEnabled);
1759 return E_NOTIMPL;
1760#endif
1761}
1762
1763HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766
1767 *aHPETEnabled = mHWData->mHPETEnabled;
1768
1769 return S_OK;
1770}
1771
1772HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1773{
1774 HRESULT rc = S_OK;
1775
1776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1777
1778 rc = i_checkStateDependency(MutableStateDep);
1779 if (FAILED(rc)) return rc;
1780
1781 i_setModified(IsModified_MachineData);
1782 mHWData.backup();
1783
1784 mHWData->mHPETEnabled = aHPETEnabled;
1785
1786 return rc;
1787}
1788
1789/** @todo this method should not be public */
1790HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1791{
1792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1793
1794 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1795
1796 return S_OK;
1797}
1798
1799/**
1800 * Set the memory balloon size.
1801 *
1802 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1803 * we have to make sure that we never call IGuest from here.
1804 */
1805HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1806{
1807 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1808#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1809 /* check limits */
1810 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1811 return setError(E_INVALIDARG,
1812 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1813 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1814
1815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1816
1817 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
1818 if (FAILED(rc)) return rc;
1819
1820 i_setModified(IsModified_MachineData);
1821 mHWData.backup();
1822 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1823
1824 return S_OK;
1825#else
1826 NOREF(aMemoryBalloonSize);
1827 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1828#endif
1829}
1830
1831HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1832{
1833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1834
1835 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1836 return S_OK;
1837}
1838
1839HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1840{
1841#ifdef VBOX_WITH_PAGE_SHARING
1842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 HRESULT rc = i_checkStateDependency(MutableStateDep);
1845 if (FAILED(rc)) return rc;
1846
1847 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1848 i_setModified(IsModified_MachineData);
1849 mHWData.backup();
1850 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1851 return S_OK;
1852#else
1853 NOREF(aPageFusionEnabled);
1854 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1855#endif
1856}
1857
1858HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1859{
1860 /* mBIOSSettings is constant during life time, no need to lock */
1861 aBIOSSettings = mBIOSSettings;
1862
1863 return S_OK;
1864}
1865
1866HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1867{
1868 /* mTrustedPlatformModule is constant during life time, no need to lock */
1869 aTrustedPlatformModule = mTrustedPlatformModule;
1870
1871 return S_OK;
1872}
1873
1874HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1875{
1876 /* mNvramStore is constant during life time, no need to lock */
1877 aNvramStore = mNvramStore;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 aRecordingSettings = mRecordingSettings;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1892{
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 aGraphicsAdapter = mGraphicsAdapter;
1896
1897 return S_OK;
1898}
1899
1900HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1901{
1902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1903
1904 switch (aProperty)
1905 {
1906 case CPUPropertyType_PAE:
1907 *aValue = mHWData->mPAEEnabled;
1908 break;
1909
1910 case CPUPropertyType_LongMode:
1911 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1912 *aValue = TRUE;
1913 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1914 *aValue = FALSE;
1915#if HC_ARCH_BITS == 64
1916 else
1917 *aValue = TRUE;
1918#else
1919 else
1920 {
1921 *aValue = FALSE;
1922
1923 ComObjPtr<GuestOSType> pGuestOSType;
1924 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1925 pGuestOSType);
1926 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1927 {
1928 if (pGuestOSType->i_is64Bit())
1929 {
1930 ComObjPtr<Host> pHost = mParent->i_host();
1931 alock.release();
1932
1933 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1934 if (FAILED(hrc2))
1935 *aValue = FALSE;
1936 }
1937 }
1938 }
1939#endif
1940 break;
1941
1942 case CPUPropertyType_TripleFaultReset:
1943 *aValue = mHWData->mTripleFaultReset;
1944 break;
1945
1946 case CPUPropertyType_APIC:
1947 *aValue = mHWData->mAPIC;
1948 break;
1949
1950 case CPUPropertyType_X2APIC:
1951 *aValue = mHWData->mX2APIC;
1952 break;
1953
1954 case CPUPropertyType_IBPBOnVMExit:
1955 *aValue = mHWData->mIBPBOnVMExit;
1956 break;
1957
1958 case CPUPropertyType_IBPBOnVMEntry:
1959 *aValue = mHWData->mIBPBOnVMEntry;
1960 break;
1961
1962 case CPUPropertyType_SpecCtrl:
1963 *aValue = mHWData->mSpecCtrl;
1964 break;
1965
1966 case CPUPropertyType_SpecCtrlByHost:
1967 *aValue = mHWData->mSpecCtrlByHost;
1968 break;
1969
1970 case CPUPropertyType_HWVirt:
1971 *aValue = mHWData->mNestedHWVirt;
1972 break;
1973
1974 case CPUPropertyType_L1DFlushOnEMTScheduling:
1975 *aValue = mHWData->mL1DFlushOnSched;
1976 break;
1977
1978 case CPUPropertyType_L1DFlushOnVMEntry:
1979 *aValue = mHWData->mL1DFlushOnVMEntry;
1980 break;
1981
1982 case CPUPropertyType_MDSClearOnEMTScheduling:
1983 *aValue = mHWData->mMDSClearOnSched;
1984 break;
1985
1986 case CPUPropertyType_MDSClearOnVMEntry:
1987 *aValue = mHWData->mMDSClearOnVMEntry;
1988 break;
1989
1990 default:
1991 return E_INVALIDARG;
1992 }
1993 return S_OK;
1994}
1995
1996HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1997{
1998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1999
2000 HRESULT rc = i_checkStateDependency(MutableStateDep);
2001 if (FAILED(rc)) return rc;
2002
2003 switch (aProperty)
2004 {
2005 case CPUPropertyType_PAE:
2006 i_setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mPAEEnabled = !!aValue;
2009 break;
2010
2011 case CPUPropertyType_LongMode:
2012 i_setModified(IsModified_MachineData);
2013 mHWData.backup();
2014 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2015 break;
2016
2017 case CPUPropertyType_TripleFaultReset:
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mTripleFaultReset = !!aValue;
2021 break;
2022
2023 case CPUPropertyType_APIC:
2024 if (mHWData->mX2APIC)
2025 aValue = TRUE;
2026 i_setModified(IsModified_MachineData);
2027 mHWData.backup();
2028 mHWData->mAPIC = !!aValue;
2029 break;
2030
2031 case CPUPropertyType_X2APIC:
2032 i_setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mX2APIC = !!aValue;
2035 if (aValue)
2036 mHWData->mAPIC = !!aValue;
2037 break;
2038
2039 case CPUPropertyType_IBPBOnVMExit:
2040 i_setModified(IsModified_MachineData);
2041 mHWData.backup();
2042 mHWData->mIBPBOnVMExit = !!aValue;
2043 break;
2044
2045 case CPUPropertyType_IBPBOnVMEntry:
2046 i_setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mIBPBOnVMEntry = !!aValue;
2049 break;
2050
2051 case CPUPropertyType_SpecCtrl:
2052 i_setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mSpecCtrl = !!aValue;
2055 break;
2056
2057 case CPUPropertyType_SpecCtrlByHost:
2058 i_setModified(IsModified_MachineData);
2059 mHWData.backup();
2060 mHWData->mSpecCtrlByHost = !!aValue;
2061 break;
2062
2063 case CPUPropertyType_HWVirt:
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mNestedHWVirt = !!aValue;
2067 break;
2068
2069 case CPUPropertyType_L1DFlushOnEMTScheduling:
2070 i_setModified(IsModified_MachineData);
2071 mHWData.backup();
2072 mHWData->mL1DFlushOnSched = !!aValue;
2073 break;
2074
2075 case CPUPropertyType_L1DFlushOnVMEntry:
2076 i_setModified(IsModified_MachineData);
2077 mHWData.backup();
2078 mHWData->mL1DFlushOnVMEntry = !!aValue;
2079 break;
2080
2081 case CPUPropertyType_MDSClearOnEMTScheduling:
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mMDSClearOnSched = !!aValue;
2085 break;
2086
2087 case CPUPropertyType_MDSClearOnVMEntry:
2088 i_setModified(IsModified_MachineData);
2089 mHWData.backup();
2090 mHWData->mMDSClearOnVMEntry = !!aValue;
2091 break;
2092
2093 default:
2094 return E_INVALIDARG;
2095 }
2096 return S_OK;
2097}
2098
2099HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2100 ULONG *aValEcx, ULONG *aValEdx)
2101{
2102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2103 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2104 {
2105 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2106 it != mHWData->mCpuIdLeafList.end();
2107 ++it)
2108 {
2109 if (aOrdinal == 0)
2110 {
2111 const settings::CpuIdLeaf &rLeaf= *it;
2112 *aIdx = rLeaf.idx;
2113 *aSubIdx = rLeaf.idxSub;
2114 *aValEax = rLeaf.uEax;
2115 *aValEbx = rLeaf.uEbx;
2116 *aValEcx = rLeaf.uEcx;
2117 *aValEdx = rLeaf.uEdx;
2118 return S_OK;
2119 }
2120 aOrdinal--;
2121 }
2122 }
2123 return E_INVALIDARG;
2124}
2125
2126HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2127{
2128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 /*
2131 * Search the list.
2132 */
2133 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2134 {
2135 const settings::CpuIdLeaf &rLeaf= *it;
2136 if ( rLeaf.idx == aIdx
2137 && ( aSubIdx == UINT32_MAX
2138 || rLeaf.idxSub == aSubIdx) )
2139 {
2140 *aValEax = rLeaf.uEax;
2141 *aValEbx = rLeaf.uEbx;
2142 *aValEcx = rLeaf.uEcx;
2143 *aValEdx = rLeaf.uEdx;
2144 return S_OK;
2145 }
2146 }
2147
2148 return E_INVALIDARG;
2149}
2150
2151
2152HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2153{
2154 /*
2155 * Validate input before taking locks and checking state.
2156 */
2157 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2158 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2159 if ( aIdx >= UINT32_C(0x20)
2160 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2161 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2162 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2163
2164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2165 HRESULT rc = i_checkStateDependency(MutableStateDep);
2166 if (FAILED(rc)) return rc;
2167
2168 /*
2169 * Impose a maximum number of leaves.
2170 */
2171 if (mHWData->mCpuIdLeafList.size() > 256)
2172 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2173
2174 /*
2175 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2176 */
2177 i_setModified(IsModified_MachineData);
2178 mHWData.backup();
2179
2180 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2181 {
2182 settings::CpuIdLeaf &rLeaf= *it;
2183 if ( rLeaf.idx == aIdx
2184 && ( aSubIdx == UINT32_MAX
2185 || rLeaf.idxSub == aSubIdx) )
2186 it = mHWData->mCpuIdLeafList.erase(it);
2187 else
2188 ++it;
2189 }
2190
2191 settings::CpuIdLeaf NewLeaf;
2192 NewLeaf.idx = aIdx;
2193 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2194 NewLeaf.uEax = aValEax;
2195 NewLeaf.uEbx = aValEbx;
2196 NewLeaf.uEcx = aValEcx;
2197 NewLeaf.uEdx = aValEdx;
2198 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2199 return S_OK;
2200}
2201
2202HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2203{
2204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2205
2206 HRESULT rc = i_checkStateDependency(MutableStateDep);
2207 if (FAILED(rc)) return rc;
2208
2209 /*
2210 * Do the removal.
2211 */
2212 bool fModified = mHWData.isBackedUp();
2213 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2214 {
2215 settings::CpuIdLeaf &rLeaf= *it;
2216 if ( rLeaf.idx == aIdx
2217 && ( aSubIdx == UINT32_MAX
2218 || rLeaf.idxSub == aSubIdx) )
2219 {
2220 if (!fModified)
2221 {
2222 fModified = true;
2223 i_setModified(IsModified_MachineData);
2224 mHWData.backup();
2225 // Start from the beginning, since mHWData.backup() creates
2226 // a new list, causing iterator mixup. This makes sure that
2227 // the settings are not unnecessarily marked as modified,
2228 // at the price of extra list walking.
2229 it = mHWData->mCpuIdLeafList.begin();
2230 }
2231 else
2232 it = mHWData->mCpuIdLeafList.erase(it);
2233 }
2234 else
2235 ++it;
2236 }
2237
2238 return S_OK;
2239}
2240
2241HRESULT Machine::removeAllCPUIDLeaves()
2242{
2243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2244
2245 HRESULT rc = i_checkStateDependency(MutableStateDep);
2246 if (FAILED(rc)) return rc;
2247
2248 if (mHWData->mCpuIdLeafList.size() > 0)
2249 {
2250 i_setModified(IsModified_MachineData);
2251 mHWData.backup();
2252
2253 mHWData->mCpuIdLeafList.clear();
2254 }
2255
2256 return S_OK;
2257}
2258HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2259{
2260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2261
2262 switch(aProperty)
2263 {
2264 case HWVirtExPropertyType_Enabled:
2265 *aValue = mHWData->mHWVirtExEnabled;
2266 break;
2267
2268 case HWVirtExPropertyType_VPID:
2269 *aValue = mHWData->mHWVirtExVPIDEnabled;
2270 break;
2271
2272 case HWVirtExPropertyType_NestedPaging:
2273 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2274 break;
2275
2276 case HWVirtExPropertyType_UnrestrictedExecution:
2277 *aValue = mHWData->mHWVirtExUXEnabled;
2278 break;
2279
2280 case HWVirtExPropertyType_LargePages:
2281 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2282 break;
2283
2284 case HWVirtExPropertyType_Force:
2285 *aValue = mHWData->mHWVirtExForceEnabled;
2286 break;
2287
2288 case HWVirtExPropertyType_UseNativeApi:
2289 *aValue = mHWData->mHWVirtExUseNativeApi;
2290 break;
2291
2292 case HWVirtExPropertyType_VirtVmsaveVmload:
2293 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2294 break;
2295
2296 default:
2297 return E_INVALIDARG;
2298 }
2299 return S_OK;
2300}
2301
2302HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2303{
2304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2305
2306 HRESULT rc = i_checkStateDependency(MutableStateDep);
2307 if (FAILED(rc)) return rc;
2308
2309 switch (aProperty)
2310 {
2311 case HWVirtExPropertyType_Enabled:
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mHWVirtExEnabled = !!aValue;
2315 break;
2316
2317 case HWVirtExPropertyType_VPID:
2318 i_setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2321 break;
2322
2323 case HWVirtExPropertyType_NestedPaging:
2324 i_setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2327 break;
2328
2329 case HWVirtExPropertyType_UnrestrictedExecution:
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mHWVirtExUXEnabled = !!aValue;
2333 break;
2334
2335 case HWVirtExPropertyType_LargePages:
2336 i_setModified(IsModified_MachineData);
2337 mHWData.backup();
2338 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2339 break;
2340
2341 case HWVirtExPropertyType_Force:
2342 i_setModified(IsModified_MachineData);
2343 mHWData.backup();
2344 mHWData->mHWVirtExForceEnabled = !!aValue;
2345 break;
2346
2347 case HWVirtExPropertyType_UseNativeApi:
2348 i_setModified(IsModified_MachineData);
2349 mHWData.backup();
2350 mHWData->mHWVirtExUseNativeApi = !!aValue;
2351 break;
2352
2353 case HWVirtExPropertyType_VirtVmsaveVmload:
2354 i_setModified(IsModified_MachineData);
2355 mHWData.backup();
2356 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2357 break;
2358
2359 default:
2360 return E_INVALIDARG;
2361 }
2362
2363 return S_OK;
2364}
2365
2366HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2367{
2368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2369
2370 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2371
2372 return S_OK;
2373}
2374
2375HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2376{
2377 /** @todo (r=dmik):
2378 * 1. Allow to change the name of the snapshot folder containing snapshots
2379 * 2. Rename the folder on disk instead of just changing the property
2380 * value (to be smart and not to leave garbage). Note that it cannot be
2381 * done here because the change may be rolled back. Thus, the right
2382 * place is #saveSettings().
2383 */
2384
2385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2386
2387 HRESULT rc = i_checkStateDependency(MutableStateDep);
2388 if (FAILED(rc)) return rc;
2389
2390 if (!mData->mCurrentSnapshot.isNull())
2391 return setError(E_FAIL,
2392 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2393
2394 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2395
2396 if (strSnapshotFolder.isEmpty())
2397 strSnapshotFolder = "Snapshots";
2398 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2399 if (RT_FAILURE(vrc))
2400 return setErrorBoth(E_FAIL, vrc,
2401 tr("Invalid snapshot folder '%s' (%Rrc)"),
2402 strSnapshotFolder.c_str(), vrc);
2403
2404 i_setModified(IsModified_MachineData);
2405 mUserData.backup();
2406
2407 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2408
2409 return S_OK;
2410}
2411
2412HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2413{
2414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2415
2416 aMediumAttachments.resize(mMediumAttachments->size());
2417 size_t i = 0;
2418 for (MediumAttachmentList::const_iterator
2419 it = mMediumAttachments->begin();
2420 it != mMediumAttachments->end();
2421 ++it, ++i)
2422 aMediumAttachments[i] = *it;
2423
2424 return S_OK;
2425}
2426
2427HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2428{
2429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2430
2431 Assert(!!mVRDEServer);
2432
2433 aVRDEServer = mVRDEServer;
2434
2435 return S_OK;
2436}
2437
2438HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2439{
2440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2441
2442 aAudioAdapter = mAudioAdapter;
2443
2444 return S_OK;
2445}
2446
2447HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2448{
2449#ifdef VBOX_WITH_VUSB
2450 clearError();
2451 MultiResult rc(S_OK);
2452
2453# ifdef VBOX_WITH_USB
2454 rc = mParent->i_host()->i_checkUSBProxyService();
2455 if (FAILED(rc)) return rc;
2456# endif
2457
2458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 aUSBControllers.resize(mUSBControllers->size());
2461 size_t i = 0;
2462 for (USBControllerList::const_iterator
2463 it = mUSBControllers->begin();
2464 it != mUSBControllers->end();
2465 ++it, ++i)
2466 aUSBControllers[i] = *it;
2467
2468 return S_OK;
2469#else
2470 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2471 * extended error info to indicate that USB is simply not available
2472 * (w/o treating it as a failure), for example, as in OSE */
2473 NOREF(aUSBControllers);
2474 ReturnComNotImplemented();
2475#endif /* VBOX_WITH_VUSB */
2476}
2477
2478HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2479{
2480#ifdef VBOX_WITH_VUSB
2481 clearError();
2482 MultiResult rc(S_OK);
2483
2484# ifdef VBOX_WITH_USB
2485 rc = mParent->i_host()->i_checkUSBProxyService();
2486 if (FAILED(rc)) return rc;
2487# endif
2488
2489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 aUSBDeviceFilters = mUSBDeviceFilters;
2492 return rc;
2493#else
2494 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2495 * extended error info to indicate that USB is simply not available
2496 * (w/o treating it as a failure), for example, as in OSE */
2497 NOREF(aUSBDeviceFilters);
2498 ReturnComNotImplemented();
2499#endif /* VBOX_WITH_VUSB */
2500}
2501
2502HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2503{
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 aSettingsFilePath = mData->m_strConfigFileFull;
2507
2508 return S_OK;
2509}
2510
2511HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2512{
2513 RT_NOREF(aSettingsFilePath);
2514 ReturnComNotImplemented();
2515}
2516
2517HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2518{
2519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2522 if (FAILED(rc)) return rc;
2523
2524 if (!mData->pMachineConfigFile->fileExists())
2525 // this is a new machine, and no config file exists yet:
2526 *aSettingsModified = TRUE;
2527 else
2528 *aSettingsModified = (mData->flModifications != 0);
2529
2530 return S_OK;
2531}
2532
2533HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2534{
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 *aSessionState = mData->mSession.mState;
2538
2539 return S_OK;
2540}
2541
2542HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2543{
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 aSessionName = mData->mSession.mName;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2552{
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555 *aSessionPID = mData->mSession.mPID;
2556
2557 return S_OK;
2558}
2559
2560HRESULT Machine::getState(MachineState_T *aState)
2561{
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 *aState = mData->mMachineState;
2565 Assert(mData->mMachineState != MachineState_Null);
2566
2567 return S_OK;
2568}
2569
2570HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 aStateFilePath = mSSData->strStateFilePath;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 i_getLogFolder(aLogFolder);
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2598{
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 aCurrentSnapshot = mData->mCurrentSnapshot;
2602
2603 return S_OK;
2604}
2605
2606HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2607{
2608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2609
2610 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2611 ? 0
2612 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2613
2614 return S_OK;
2615}
2616
2617HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2618{
2619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2620
2621 /* Note: for machines with no snapshots, we always return FALSE
2622 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2623 * reasons :) */
2624
2625 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2626 ? FALSE
2627 : mData->mCurrentStateModified;
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 aSharedFolders.resize(mHWData->mSharedFolders.size());
2637 size_t i = 0;
2638 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2639 it = mHWData->mSharedFolders.begin();
2640 it != mHWData->mSharedFolders.end();
2641 ++it, ++i)
2642 aSharedFolders[i] = *it;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 *aClipboardMode = mHWData->mClipboardMode;
2652
2653 return S_OK;
2654}
2655
2656HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2657{
2658 HRESULT rc = S_OK;
2659
2660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 rc = i_checkStateDependency(MutableOrRunningStateDep);
2663 if (FAILED(rc)) return rc;
2664
2665 alock.release();
2666 rc = i_onClipboardModeChange(aClipboardMode);
2667 alock.acquire();
2668 if (FAILED(rc)) return rc;
2669
2670 i_setModified(IsModified_MachineData);
2671 mHWData.backup();
2672 mHWData->mClipboardMode = aClipboardMode;
2673
2674 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2675 if (Global::IsOnline(mData->mMachineState))
2676 i_saveSettings(NULL, alock);
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2682{
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2686
2687 return S_OK;
2688}
2689
2690HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2691{
2692 HRESULT rc = S_OK;
2693
2694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 rc = i_checkStateDependency(MutableOrRunningStateDep);
2697 if (FAILED(rc)) return rc;
2698
2699 alock.release();
2700 rc = i_onClipboardFileTransferModeChange(aEnabled);
2701 alock.acquire();
2702 if (FAILED(rc)) return rc;
2703
2704 i_setModified(IsModified_MachineData);
2705 mHWData.backup();
2706 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2707
2708 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2709 if (Global::IsOnline(mData->mMachineState))
2710 i_saveSettings(NULL, alock);
2711
2712 return S_OK;
2713}
2714
2715HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2716{
2717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2718
2719 *aDnDMode = mHWData->mDnDMode;
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2725{
2726 HRESULT rc = S_OK;
2727
2728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 rc = i_checkStateDependency(MutableOrRunningStateDep);
2731 if (FAILED(rc)) return rc;
2732
2733 alock.release();
2734 rc = i_onDnDModeChange(aDnDMode);
2735
2736 alock.acquire();
2737 if (FAILED(rc)) return rc;
2738
2739 i_setModified(IsModified_MachineData);
2740 mHWData.backup();
2741 mHWData->mDnDMode = aDnDMode;
2742
2743 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2744 if (Global::IsOnline(mData->mMachineState))
2745 i_saveSettings(NULL, alock);
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2751{
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 aStorageControllers.resize(mStorageControllers->size());
2755 size_t i = 0;
2756 for (StorageControllerList::const_iterator
2757 it = mStorageControllers->begin();
2758 it != mStorageControllers->end();
2759 ++it, ++i)
2760 aStorageControllers[i] = *it;
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 *aEnabled = mUserData->s.fTeleporterEnabled;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2775{
2776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 /* Only allow it to be set to true when PoweredOff or Aborted.
2779 (Clearing it is always permitted.) */
2780 if ( aTeleporterEnabled
2781 && mData->mRegistered
2782 && ( !i_isSessionMachine()
2783 || ( mData->mMachineState != MachineState_PoweredOff
2784 && mData->mMachineState != MachineState_Teleported
2785 && mData->mMachineState != MachineState_Aborted
2786 )
2787 )
2788 )
2789 return setError(VBOX_E_INVALID_VM_STATE,
2790 tr("The machine is not powered off (state is %s)"),
2791 Global::stringifyMachineState(mData->mMachineState));
2792
2793 i_setModified(IsModified_MachineData);
2794 mUserData.backup();
2795 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2796
2797 return S_OK;
2798}
2799
2800HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2810{
2811 if (aTeleporterPort >= _64K)
2812 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2813
2814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2815
2816 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2817 if (FAILED(rc)) return rc;
2818
2819 i_setModified(IsModified_MachineData);
2820 mUserData.backup();
2821 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2827{
2828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2829
2830 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2831
2832 return S_OK;
2833}
2834
2835HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2836{
2837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2840 if (FAILED(rc)) return rc;
2841
2842 i_setModified(IsModified_MachineData);
2843 mUserData.backup();
2844 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2858{
2859 /*
2860 * Hash the password first.
2861 */
2862 com::Utf8Str aT = aTeleporterPassword;
2863
2864 if (!aT.isEmpty())
2865 {
2866 if (VBoxIsPasswordHashed(&aT))
2867 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2868 VBoxHashPassword(&aT);
2869 }
2870
2871 /*
2872 * Do the update.
2873 */
2874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2875 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2876 if (SUCCEEDED(hrc))
2877 {
2878 i_setModified(IsModified_MachineData);
2879 mUserData.backup();
2880 mUserData->s.strTeleporterPassword = aT;
2881 }
2882
2883 return hrc;
2884}
2885
2886HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2887{
2888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2889
2890 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2891
2892 return S_OK;
2893}
2894
2895HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2896{
2897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 /* Only allow it to be set to true when PoweredOff or Aborted.
2900 (Clearing it is always permitted.) */
2901 if ( aRTCUseUTC
2902 && mData->mRegistered
2903 && ( !i_isSessionMachine()
2904 || ( mData->mMachineState != MachineState_PoweredOff
2905 && mData->mMachineState != MachineState_Teleported
2906 && mData->mMachineState != MachineState_Aborted
2907 )
2908 )
2909 )
2910 return setError(VBOX_E_INVALID_VM_STATE,
2911 tr("The machine is not powered off (state is %s)"),
2912 Global::stringifyMachineState(mData->mMachineState));
2913
2914 i_setModified(IsModified_MachineData);
2915 mUserData.backup();
2916 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2917
2918 return S_OK;
2919}
2920
2921HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2922{
2923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2924
2925 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2926
2927 return S_OK;
2928}
2929
2930HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2931{
2932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2933
2934 HRESULT rc = i_checkStateDependency(MutableStateDep);
2935 if (FAILED(rc)) return rc;
2936
2937 i_setModified(IsModified_MachineData);
2938 mHWData.backup();
2939 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2940
2941 return S_OK;
2942}
2943
2944HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2945{
2946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2947
2948 *aIOCacheSize = mHWData->mIOCacheSize;
2949
2950 return S_OK;
2951}
2952
2953HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2954{
2955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2956
2957 HRESULT rc = i_checkStateDependency(MutableStateDep);
2958 if (FAILED(rc)) return rc;
2959
2960 i_setModified(IsModified_MachineData);
2961 mHWData.backup();
2962 mHWData->mIOCacheSize = aIOCacheSize;
2963
2964 return S_OK;
2965}
2966
2967
2968/**
2969 * @note Locks objects!
2970 */
2971HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2972 LockType_T aLockType)
2973{
2974 /* check the session state */
2975 SessionState_T state;
2976 HRESULT rc = aSession->COMGETTER(State)(&state);
2977 if (FAILED(rc)) return rc;
2978
2979 if (state != SessionState_Unlocked)
2980 return setError(VBOX_E_INVALID_OBJECT_STATE,
2981 tr("The given session is busy"));
2982
2983 // get the client's IInternalSessionControl interface
2984 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2985 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2986 E_INVALIDARG);
2987
2988 // session name (only used in some code paths)
2989 Utf8Str strSessionName;
2990
2991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2992
2993 if (!mData->mRegistered)
2994 return setError(E_UNEXPECTED,
2995 tr("The machine '%s' is not registered"),
2996 mUserData->s.strName.c_str());
2997
2998 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2999
3000 SessionState_T oldState = mData->mSession.mState;
3001 /* Hack: in case the session is closing and there is a progress object
3002 * which allows waiting for the session to be closed, take the opportunity
3003 * and do a limited wait (max. 1 second). This helps a lot when the system
3004 * is busy and thus session closing can take a little while. */
3005 if ( mData->mSession.mState == SessionState_Unlocking
3006 && mData->mSession.mProgress)
3007 {
3008 alock.release();
3009 mData->mSession.mProgress->WaitForCompletion(1000);
3010 alock.acquire();
3011 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3012 }
3013
3014 // try again now
3015 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3016 // (i.e. session machine exists)
3017 && (aLockType == LockType_Shared) // caller wants a shared link to the
3018 // existing session that holds the write lock:
3019 )
3020 {
3021 // OK, share the session... we are now dealing with three processes:
3022 // 1) VBoxSVC (where this code runs);
3023 // 2) process C: the caller's client process (who wants a shared session);
3024 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3025
3026 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3027 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3028 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3029 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3030 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3031
3032 /*
3033 * Release the lock before calling the client process. It's safe here
3034 * since the only thing to do after we get the lock again is to add
3035 * the remote control to the list (which doesn't directly influence
3036 * anything).
3037 */
3038 alock.release();
3039
3040 // get the console of the session holding the write lock (this is a remote call)
3041 ComPtr<IConsole> pConsoleW;
3042 if (mData->mSession.mLockType == LockType_VM)
3043 {
3044 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3045 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3046 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3047 if (FAILED(rc))
3048 // the failure may occur w/o any error info (from RPC), so provide one
3049 return setError(VBOX_E_VM_ERROR,
3050 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3051 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3052 }
3053
3054 // share the session machine and W's console with the caller's session
3055 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3056 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3057 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3058
3059 if (FAILED(rc))
3060 // the failure may occur w/o any error info (from RPC), so provide one
3061 return setError(VBOX_E_VM_ERROR,
3062 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3063 alock.acquire();
3064
3065 // need to revalidate the state after acquiring the lock again
3066 if (mData->mSession.mState != SessionState_Locked)
3067 {
3068 pSessionControl->Uninitialize();
3069 return setError(VBOX_E_INVALID_SESSION_STATE,
3070 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3071 mUserData->s.strName.c_str());
3072 }
3073
3074 // add the caller's session to the list
3075 mData->mSession.mRemoteControls.push_back(pSessionControl);
3076 }
3077 else if ( mData->mSession.mState == SessionState_Locked
3078 || mData->mSession.mState == SessionState_Unlocking
3079 )
3080 {
3081 // sharing not permitted, or machine still unlocking:
3082 return setError(VBOX_E_INVALID_OBJECT_STATE,
3083 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3084 mUserData->s.strName.c_str());
3085 }
3086 else
3087 {
3088 // machine is not locked: then write-lock the machine (create the session machine)
3089
3090 // must not be busy
3091 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3092
3093 // get the caller's session PID
3094 RTPROCESS pid = NIL_RTPROCESS;
3095 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3096 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3097 Assert(pid != NIL_RTPROCESS);
3098
3099 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3100
3101 if (fLaunchingVMProcess)
3102 {
3103 if (mData->mSession.mPID == NIL_RTPROCESS)
3104 {
3105 // two or more clients racing for a lock, the one which set the
3106 // session state to Spawning will win, the others will get an
3107 // error as we can't decide here if waiting a little would help
3108 // (only for shared locks this would avoid an error)
3109 return setError(VBOX_E_INVALID_OBJECT_STATE,
3110 tr("The machine '%s' already has a lock request pending"),
3111 mUserData->s.strName.c_str());
3112 }
3113
3114 // this machine is awaiting for a spawning session to be opened:
3115 // then the calling process must be the one that got started by
3116 // LaunchVMProcess()
3117
3118 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3119 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3120
3121#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3122 /* Hardened windows builds spawns three processes when a VM is
3123 launched, the 3rd one is the one that will end up here. */
3124 RTPROCESS pidParent;
3125 int vrc = RTProcQueryParent(pid, &pidParent);
3126 if (RT_SUCCESS(vrc))
3127 vrc = RTProcQueryParent(pidParent, &pidParent);
3128 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3129 || vrc == VERR_ACCESS_DENIED)
3130 {
3131 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3132 mData->mSession.mPID = pid;
3133 }
3134#endif
3135
3136 if (mData->mSession.mPID != pid)
3137 return setError(E_ACCESSDENIED,
3138 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3139 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3140 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3141 }
3142
3143 // create the mutable SessionMachine from the current machine
3144 ComObjPtr<SessionMachine> sessionMachine;
3145 sessionMachine.createObject();
3146 rc = sessionMachine->init(this);
3147 AssertComRC(rc);
3148
3149 /* NOTE: doing return from this function after this point but
3150 * before the end is forbidden since it may call SessionMachine::uninit()
3151 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3152 * lock while still holding the Machine lock in alock so that a deadlock
3153 * is possible due to the wrong lock order. */
3154
3155 if (SUCCEEDED(rc))
3156 {
3157 /*
3158 * Set the session state to Spawning to protect against subsequent
3159 * attempts to open a session and to unregister the machine after
3160 * we release the lock.
3161 */
3162 SessionState_T origState = mData->mSession.mState;
3163 mData->mSession.mState = SessionState_Spawning;
3164
3165#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3166 /* Get the client token ID to be passed to the client process */
3167 Utf8Str strTokenId;
3168 sessionMachine->i_getTokenId(strTokenId);
3169 Assert(!strTokenId.isEmpty());
3170#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3171 /* Get the client token to be passed to the client process */
3172 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3173 /* The token is now "owned" by pToken, fix refcount */
3174 if (!pToken.isNull())
3175 pToken->Release();
3176#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3177
3178 /*
3179 * Release the lock before calling the client process -- it will call
3180 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3181 * because the state is Spawning, so that LaunchVMProcess() and
3182 * LockMachine() calls will fail. This method, called before we
3183 * acquire the lock again, will fail because of the wrong PID.
3184 *
3185 * Note that mData->mSession.mRemoteControls accessed outside
3186 * the lock may not be modified when state is Spawning, so it's safe.
3187 */
3188 alock.release();
3189
3190 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3191#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3192 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3193#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3194 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3195 /* Now the token is owned by the client process. */
3196 pToken.setNull();
3197#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3198 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3199
3200 /* The failure may occur w/o any error info (from RPC), so provide one */
3201 if (FAILED(rc))
3202 setError(VBOX_E_VM_ERROR,
3203 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3204
3205 // get session name, either to remember or to compare against
3206 // the already known session name.
3207 {
3208 Bstr bstrSessionName;
3209 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3210 if (SUCCEEDED(rc2))
3211 strSessionName = bstrSessionName;
3212 }
3213
3214 if ( SUCCEEDED(rc)
3215 && fLaunchingVMProcess
3216 )
3217 {
3218 /* complete the remote session initialization */
3219
3220 /* get the console from the direct session */
3221 ComPtr<IConsole> console;
3222 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3223 ComAssertComRC(rc);
3224
3225 if (SUCCEEDED(rc) && !console)
3226 {
3227 ComAssert(!!console);
3228 rc = E_FAIL;
3229 }
3230
3231 /* assign machine & console to the remote session */
3232 if (SUCCEEDED(rc))
3233 {
3234 /*
3235 * after LaunchVMProcess(), the first and the only
3236 * entry in remoteControls is that remote session
3237 */
3238 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3239 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3240 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3241
3242 /* The failure may occur w/o any error info (from RPC), so provide one */
3243 if (FAILED(rc))
3244 setError(VBOX_E_VM_ERROR,
3245 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3246 }
3247
3248 if (FAILED(rc))
3249 pSessionControl->Uninitialize();
3250 }
3251
3252 /* acquire the lock again */
3253 alock.acquire();
3254
3255 /* Restore the session state */
3256 mData->mSession.mState = origState;
3257 }
3258
3259 // finalize spawning anyway (this is why we don't return on errors above)
3260 if (fLaunchingVMProcess)
3261 {
3262 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3263 /* Note that the progress object is finalized later */
3264 /** @todo Consider checking mData->mSession.mProgress for cancellation
3265 * around here. */
3266
3267 /* We don't reset mSession.mPID here because it is necessary for
3268 * SessionMachine::uninit() to reap the child process later. */
3269
3270 if (FAILED(rc))
3271 {
3272 /* Close the remote session, remove the remote control from the list
3273 * and reset session state to Closed (@note keep the code in sync
3274 * with the relevant part in checkForSpawnFailure()). */
3275
3276 Assert(mData->mSession.mRemoteControls.size() == 1);
3277 if (mData->mSession.mRemoteControls.size() == 1)
3278 {
3279 ErrorInfoKeeper eik;
3280 mData->mSession.mRemoteControls.front()->Uninitialize();
3281 }
3282
3283 mData->mSession.mRemoteControls.clear();
3284 mData->mSession.mState = SessionState_Unlocked;
3285 }
3286 }
3287 else
3288 {
3289 /* memorize PID of the directly opened session */
3290 if (SUCCEEDED(rc))
3291 mData->mSession.mPID = pid;
3292 }
3293
3294 if (SUCCEEDED(rc))
3295 {
3296 mData->mSession.mLockType = aLockType;
3297 /* memorize the direct session control and cache IUnknown for it */
3298 mData->mSession.mDirectControl = pSessionControl;
3299 mData->mSession.mState = SessionState_Locked;
3300 if (!fLaunchingVMProcess)
3301 mData->mSession.mName = strSessionName;
3302 /* associate the SessionMachine with this Machine */
3303 mData->mSession.mMachine = sessionMachine;
3304
3305 /* request an IUnknown pointer early from the remote party for later
3306 * identity checks (it will be internally cached within mDirectControl
3307 * at least on XPCOM) */
3308 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3309 NOREF(unk);
3310 }
3311
3312 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3313 * would break the lock order */
3314 alock.release();
3315
3316 /* uninitialize the created session machine on failure */
3317 if (FAILED(rc))
3318 sessionMachine->uninit();
3319 }
3320
3321 if (SUCCEEDED(rc))
3322 {
3323 /*
3324 * tell the client watcher thread to update the set of
3325 * machines that have open sessions
3326 */
3327 mParent->i_updateClientWatcher();
3328
3329 if (oldState != SessionState_Locked)
3330 /* fire an event */
3331 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3332 }
3333
3334 return rc;
3335}
3336
3337/**
3338 * @note Locks objects!
3339 */
3340HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3341 const com::Utf8Str &aName,
3342 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3343 ComPtr<IProgress> &aProgress)
3344{
3345 Utf8Str strFrontend(aName);
3346 /* "emergencystop" doesn't need the session, so skip the checks/interface
3347 * retrieval. This code doesn't quite fit in here, but introducing a
3348 * special API method would be even more effort, and would require explicit
3349 * support by every API client. It's better to hide the feature a bit. */
3350 if (strFrontend != "emergencystop")
3351 CheckComArgNotNull(aSession);
3352
3353 HRESULT rc = S_OK;
3354 if (strFrontend.isEmpty())
3355 {
3356 Bstr bstrFrontend;
3357 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3358 if (FAILED(rc))
3359 return rc;
3360 strFrontend = bstrFrontend;
3361 if (strFrontend.isEmpty())
3362 {
3363 ComPtr<ISystemProperties> systemProperties;
3364 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3365 if (FAILED(rc))
3366 return rc;
3367 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3368 if (FAILED(rc))
3369 return rc;
3370 strFrontend = bstrFrontend;
3371 }
3372 /* paranoia - emergencystop is not a valid default */
3373 if (strFrontend == "emergencystop")
3374 strFrontend = Utf8Str::Empty;
3375 }
3376 /* default frontend: Qt GUI */
3377 if (strFrontend.isEmpty())
3378 strFrontend = "GUI/Qt";
3379
3380 if (strFrontend != "emergencystop")
3381 {
3382 /* check the session state */
3383 SessionState_T state;
3384 rc = aSession->COMGETTER(State)(&state);
3385 if (FAILED(rc))
3386 return rc;
3387
3388 if (state != SessionState_Unlocked)
3389 return setError(VBOX_E_INVALID_OBJECT_STATE,
3390 tr("The given session is busy"));
3391
3392 /* get the IInternalSessionControl interface */
3393 ComPtr<IInternalSessionControl> control(aSession);
3394 ComAssertMsgRet(!control.isNull(),
3395 ("No IInternalSessionControl interface"),
3396 E_INVALIDARG);
3397
3398 /* get the teleporter enable state for the progress object init. */
3399 BOOL fTeleporterEnabled;
3400 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3401 if (FAILED(rc))
3402 return rc;
3403
3404 /* create a progress object */
3405 ComObjPtr<ProgressProxy> progress;
3406 progress.createObject();
3407 rc = progress->init(mParent,
3408 static_cast<IMachine*>(this),
3409 Bstr(tr("Starting VM")).raw(),
3410 TRUE /* aCancelable */,
3411 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3412 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3413 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3414 2 /* uFirstOperationWeight */,
3415 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3416
3417 if (SUCCEEDED(rc))
3418 {
3419 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3420 if (SUCCEEDED(rc))
3421 {
3422 aProgress = progress;
3423
3424 /* signal the client watcher thread */
3425 mParent->i_updateClientWatcher();
3426
3427 /* fire an event */
3428 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3429 }
3430 }
3431 }
3432 else
3433 {
3434 /* no progress object - either instant success or failure */
3435 aProgress = NULL;
3436
3437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3438
3439 if (mData->mSession.mState != SessionState_Locked)
3440 return setError(VBOX_E_INVALID_OBJECT_STATE,
3441 tr("The machine '%s' is not locked by a session"),
3442 mUserData->s.strName.c_str());
3443
3444 /* must have a VM process associated - do not kill normal API clients
3445 * with an open session */
3446 if (!Global::IsOnline(mData->mMachineState))
3447 return setError(VBOX_E_INVALID_OBJECT_STATE,
3448 tr("The machine '%s' does not have a VM process"),
3449 mUserData->s.strName.c_str());
3450
3451 /* forcibly terminate the VM process */
3452 if (mData->mSession.mPID != NIL_RTPROCESS)
3453 RTProcTerminate(mData->mSession.mPID);
3454
3455 /* signal the client watcher thread, as most likely the client has
3456 * been terminated */
3457 mParent->i_updateClientWatcher();
3458 }
3459
3460 return rc;
3461}
3462
3463HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3464{
3465 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3466 return setError(E_INVALIDARG,
3467 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3468 aPosition, SchemaDefs::MaxBootPosition);
3469
3470 if (aDevice == DeviceType_USB)
3471 return setError(E_NOTIMPL,
3472 tr("Booting from USB device is currently not supported"));
3473
3474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3475
3476 HRESULT rc = i_checkStateDependency(MutableStateDep);
3477 if (FAILED(rc)) return rc;
3478
3479 i_setModified(IsModified_MachineData);
3480 mHWData.backup();
3481 mHWData->mBootOrder[aPosition - 1] = aDevice;
3482
3483 return S_OK;
3484}
3485
3486HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3487{
3488 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3489 return setError(E_INVALIDARG,
3490 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3491 aPosition, SchemaDefs::MaxBootPosition);
3492
3493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3494
3495 *aDevice = mHWData->mBootOrder[aPosition - 1];
3496
3497 return S_OK;
3498}
3499
3500HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3501 LONG aControllerPort,
3502 LONG aDevice,
3503 DeviceType_T aType,
3504 const ComPtr<IMedium> &aMedium)
3505{
3506 IMedium *aM = aMedium;
3507 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3508 aName.c_str(), aControllerPort, aDevice, aType, aM));
3509
3510 // request the host lock first, since might be calling Host methods for getting host drives;
3511 // next, protect the media tree all the while we're in here, as well as our member variables
3512 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3513 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3514
3515 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3516 if (FAILED(rc)) return rc;
3517
3518 /// @todo NEWMEDIA implicit machine registration
3519 if (!mData->mRegistered)
3520 return setError(VBOX_E_INVALID_OBJECT_STATE,
3521 tr("Cannot attach storage devices to an unregistered machine"));
3522
3523 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3524
3525 /* Check for an existing controller. */
3526 ComObjPtr<StorageController> ctl;
3527 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3528 if (FAILED(rc)) return rc;
3529
3530 StorageControllerType_T ctrlType;
3531 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3532 if (FAILED(rc))
3533 return setError(E_FAIL,
3534 tr("Could not get type of controller '%s'"),
3535 aName.c_str());
3536
3537 bool fSilent = false;
3538 Utf8Str strReconfig;
3539
3540 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3541 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3542 if ( mData->mMachineState == MachineState_Paused
3543 && strReconfig == "1")
3544 fSilent = true;
3545
3546 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3547 bool fHotplug = false;
3548 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3549 fHotplug = true;
3550
3551 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3552 return setError(VBOX_E_INVALID_VM_STATE,
3553 tr("Controller '%s' does not support hotplugging"),
3554 aName.c_str());
3555
3556 // check that the port and device are not out of range
3557 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3558 if (FAILED(rc)) return rc;
3559
3560 /* check if the device slot is already busy */
3561 MediumAttachment *pAttachTemp;
3562 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3563 aName,
3564 aControllerPort,
3565 aDevice)))
3566 {
3567 Medium *pMedium = pAttachTemp->i_getMedium();
3568 if (pMedium)
3569 {
3570 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3571 return setError(VBOX_E_OBJECT_IN_USE,
3572 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3573 pMedium->i_getLocationFull().c_str(),
3574 aControllerPort,
3575 aDevice,
3576 aName.c_str());
3577 }
3578 else
3579 return setError(VBOX_E_OBJECT_IN_USE,
3580 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3581 aControllerPort, aDevice, aName.c_str());
3582 }
3583
3584 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3585 if (aMedium && medium.isNull())
3586 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3587
3588 AutoCaller mediumCaller(medium);
3589 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3590
3591 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3592
3593 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3594 && !medium.isNull()
3595 && ( medium->i_getType() != MediumType_Readonly
3596 || medium->i_getDeviceType() != DeviceType_DVD)
3597 )
3598 return setError(VBOX_E_OBJECT_IN_USE,
3599 tr("Medium '%s' is already attached to this virtual machine"),
3600 medium->i_getLocationFull().c_str());
3601
3602 if (!medium.isNull())
3603 {
3604 MediumType_T mtype = medium->i_getType();
3605 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3606 // For DVDs it's not written to the config file, so needs no global config
3607 // version bump. For floppies it's a new attribute "type", which is ignored
3608 // by older VirtualBox version, so needs no global config version bump either.
3609 // For hard disks this type is not accepted.
3610 if (mtype == MediumType_MultiAttach)
3611 {
3612 // This type is new with VirtualBox 4.0 and therefore requires settings
3613 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3614 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3615 // two reasons: The medium type is a property of the media registry tree, which
3616 // can reside in the global config file (for pre-4.0 media); we would therefore
3617 // possibly need to bump the global config version. We don't want to do that though
3618 // because that might make downgrading to pre-4.0 impossible.
3619 // As a result, we can only use these two new types if the medium is NOT in the
3620 // global registry:
3621 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3622 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3623 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3624 )
3625 return setError(VBOX_E_INVALID_OBJECT_STATE,
3626 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3627 "to machines that were created with VirtualBox 4.0 or later"),
3628 medium->i_getLocationFull().c_str());
3629 }
3630 }
3631
3632 bool fIndirect = false;
3633 if (!medium.isNull())
3634 fIndirect = medium->i_isReadOnly();
3635 bool associate = true;
3636
3637 do
3638 {
3639 if ( aType == DeviceType_HardDisk
3640 && mMediumAttachments.isBackedUp())
3641 {
3642 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3643
3644 /* check if the medium was attached to the VM before we started
3645 * changing attachments in which case the attachment just needs to
3646 * be restored */
3647 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3648 {
3649 AssertReturn(!fIndirect, E_FAIL);
3650
3651 /* see if it's the same bus/channel/device */
3652 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3653 {
3654 /* the simplest case: restore the whole attachment
3655 * and return, nothing else to do */
3656 mMediumAttachments->push_back(pAttachTemp);
3657
3658 /* Reattach the medium to the VM. */
3659 if (fHotplug || fSilent)
3660 {
3661 mediumLock.release();
3662 treeLock.release();
3663 alock.release();
3664
3665 MediumLockList *pMediumLockList(new MediumLockList());
3666
3667 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3668 medium /* pToLockWrite */,
3669 false /* fMediumLockWriteAll */,
3670 NULL,
3671 *pMediumLockList);
3672 alock.acquire();
3673 if (FAILED(rc))
3674 delete pMediumLockList;
3675 else
3676 {
3677 mData->mSession.mLockedMedia.Unlock();
3678 alock.release();
3679 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3680 mData->mSession.mLockedMedia.Lock();
3681 alock.acquire();
3682 }
3683 alock.release();
3684
3685 if (SUCCEEDED(rc))
3686 {
3687 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3688 /* Remove lock list in case of error. */
3689 if (FAILED(rc))
3690 {
3691 mData->mSession.mLockedMedia.Unlock();
3692 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3693 mData->mSession.mLockedMedia.Lock();
3694 }
3695 }
3696 }
3697
3698 return S_OK;
3699 }
3700
3701 /* bus/channel/device differ; we need a new attachment object,
3702 * but don't try to associate it again */
3703 associate = false;
3704 break;
3705 }
3706 }
3707
3708 /* go further only if the attachment is to be indirect */
3709 if (!fIndirect)
3710 break;
3711
3712 /* perform the so called smart attachment logic for indirect
3713 * attachments. Note that smart attachment is only applicable to base
3714 * hard disks. */
3715
3716 if (medium->i_getParent().isNull())
3717 {
3718 /* first, investigate the backup copy of the current hard disk
3719 * attachments to make it possible to re-attach existing diffs to
3720 * another device slot w/o losing their contents */
3721 if (mMediumAttachments.isBackedUp())
3722 {
3723 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3724
3725 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3726 uint32_t foundLevel = 0;
3727
3728 for (MediumAttachmentList::const_iterator
3729 it = oldAtts.begin();
3730 it != oldAtts.end();
3731 ++it)
3732 {
3733 uint32_t level = 0;
3734 MediumAttachment *pAttach = *it;
3735 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3736 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3737 if (pMedium.isNull())
3738 continue;
3739
3740 if (pMedium->i_getBase(&level) == medium)
3741 {
3742 /* skip the hard disk if its currently attached (we
3743 * cannot attach the same hard disk twice) */
3744 if (i_findAttachment(*mMediumAttachments.data(),
3745 pMedium))
3746 continue;
3747
3748 /* matched device, channel and bus (i.e. attached to the
3749 * same place) will win and immediately stop the search;
3750 * otherwise the attachment that has the youngest
3751 * descendant of medium will be used
3752 */
3753 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3754 {
3755 /* the simplest case: restore the whole attachment
3756 * and return, nothing else to do */
3757 mMediumAttachments->push_back(*it);
3758
3759 /* Reattach the medium to the VM. */
3760 if (fHotplug || fSilent)
3761 {
3762 mediumLock.release();
3763 treeLock.release();
3764 alock.release();
3765
3766 MediumLockList *pMediumLockList(new MediumLockList());
3767
3768 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3769 medium /* pToLockWrite */,
3770 false /* fMediumLockWriteAll */,
3771 NULL,
3772 *pMediumLockList);
3773 alock.acquire();
3774 if (FAILED(rc))
3775 delete pMediumLockList;
3776 else
3777 {
3778 mData->mSession.mLockedMedia.Unlock();
3779 alock.release();
3780 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3781 mData->mSession.mLockedMedia.Lock();
3782 alock.acquire();
3783 }
3784 alock.release();
3785
3786 if (SUCCEEDED(rc))
3787 {
3788 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3789 /* Remove lock list in case of error. */
3790 if (FAILED(rc))
3791 {
3792 mData->mSession.mLockedMedia.Unlock();
3793 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3794 mData->mSession.mLockedMedia.Lock();
3795 }
3796 }
3797 }
3798
3799 return S_OK;
3800 }
3801 else if ( foundIt == oldAtts.end()
3802 || level > foundLevel /* prefer younger */
3803 )
3804 {
3805 foundIt = it;
3806 foundLevel = level;
3807 }
3808 }
3809 }
3810
3811 if (foundIt != oldAtts.end())
3812 {
3813 /* use the previously attached hard disk */
3814 medium = (*foundIt)->i_getMedium();
3815 mediumCaller.attach(medium);
3816 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3817 mediumLock.attach(medium);
3818 /* not implicit, doesn't require association with this VM */
3819 fIndirect = false;
3820 associate = false;
3821 /* go right to the MediumAttachment creation */
3822 break;
3823 }
3824 }
3825
3826 /* must give up the medium lock and medium tree lock as below we
3827 * go over snapshots, which needs a lock with higher lock order. */
3828 mediumLock.release();
3829 treeLock.release();
3830
3831 /* then, search through snapshots for the best diff in the given
3832 * hard disk's chain to base the new diff on */
3833
3834 ComObjPtr<Medium> base;
3835 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3836 while (snap)
3837 {
3838 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3839
3840 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3841
3842 MediumAttachment *pAttachFound = NULL;
3843 uint32_t foundLevel = 0;
3844
3845 for (MediumAttachmentList::const_iterator
3846 it = snapAtts.begin();
3847 it != snapAtts.end();
3848 ++it)
3849 {
3850 MediumAttachment *pAttach = *it;
3851 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3852 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3853 if (pMedium.isNull())
3854 continue;
3855
3856 uint32_t level = 0;
3857 if (pMedium->i_getBase(&level) == medium)
3858 {
3859 /* matched device, channel and bus (i.e. attached to the
3860 * same place) will win and immediately stop the search;
3861 * otherwise the attachment that has the youngest
3862 * descendant of medium will be used
3863 */
3864 if ( pAttach->i_getDevice() == aDevice
3865 && pAttach->i_getPort() == aControllerPort
3866 && pAttach->i_getControllerName() == aName
3867 )
3868 {
3869 pAttachFound = pAttach;
3870 break;
3871 }
3872 else if ( !pAttachFound
3873 || level > foundLevel /* prefer younger */
3874 )
3875 {
3876 pAttachFound = pAttach;
3877 foundLevel = level;
3878 }
3879 }
3880 }
3881
3882 if (pAttachFound)
3883 {
3884 base = pAttachFound->i_getMedium();
3885 break;
3886 }
3887
3888 snap = snap->i_getParent();
3889 }
3890
3891 /* re-lock medium tree and the medium, as we need it below */
3892 treeLock.acquire();
3893 mediumLock.acquire();
3894
3895 /* found a suitable diff, use it as a base */
3896 if (!base.isNull())
3897 {
3898 medium = base;
3899 mediumCaller.attach(medium);
3900 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3901 mediumLock.attach(medium);
3902 }
3903 }
3904
3905 Utf8Str strFullSnapshotFolder;
3906 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3907
3908 ComObjPtr<Medium> diff;
3909 diff.createObject();
3910 // store this diff in the same registry as the parent
3911 Guid uuidRegistryParent;
3912 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3913 {
3914 // parent image has no registry: this can happen if we're attaching a new immutable
3915 // image that has not yet been attached (medium then points to the base and we're
3916 // creating the diff image for the immutable, and the parent is not yet registered);
3917 // put the parent in the machine registry then
3918 mediumLock.release();
3919 treeLock.release();
3920 alock.release();
3921 i_addMediumToRegistry(medium);
3922 alock.acquire();
3923 treeLock.acquire();
3924 mediumLock.acquire();
3925 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3926 }
3927 rc = diff->init(mParent,
3928 medium->i_getPreferredDiffFormat(),
3929 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3930 uuidRegistryParent,
3931 DeviceType_HardDisk);
3932 if (FAILED(rc)) return rc;
3933
3934 /* Apply the normal locking logic to the entire chain. */
3935 MediumLockList *pMediumLockList(new MediumLockList());
3936 mediumLock.release();
3937 treeLock.release();
3938 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3939 diff /* pToLockWrite */,
3940 false /* fMediumLockWriteAll */,
3941 medium,
3942 *pMediumLockList);
3943 treeLock.acquire();
3944 mediumLock.acquire();
3945 if (SUCCEEDED(rc))
3946 {
3947 mediumLock.release();
3948 treeLock.release();
3949 rc = pMediumLockList->Lock();
3950 treeLock.acquire();
3951 mediumLock.acquire();
3952 if (FAILED(rc))
3953 setError(rc,
3954 tr("Could not lock medium when creating diff '%s'"),
3955 diff->i_getLocationFull().c_str());
3956 else
3957 {
3958 /* will release the lock before the potentially lengthy
3959 * operation, so protect with the special state */
3960 MachineState_T oldState = mData->mMachineState;
3961 i_setMachineState(MachineState_SettingUp);
3962
3963 mediumLock.release();
3964 treeLock.release();
3965 alock.release();
3966
3967 rc = medium->i_createDiffStorage(diff,
3968 medium->i_getPreferredDiffVariant(),
3969 pMediumLockList,
3970 NULL /* aProgress */,
3971 true /* aWait */,
3972 false /* aNotify */);
3973
3974 alock.acquire();
3975 treeLock.acquire();
3976 mediumLock.acquire();
3977
3978 i_setMachineState(oldState);
3979 }
3980 }
3981
3982 /* Unlock the media and free the associated memory. */
3983 delete pMediumLockList;
3984
3985 if (FAILED(rc)) return rc;
3986
3987 /* use the created diff for the actual attachment */
3988 medium = diff;
3989 mediumCaller.attach(medium);
3990 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3991 mediumLock.attach(medium);
3992 }
3993 while (0);
3994
3995 ComObjPtr<MediumAttachment> attachment;
3996 attachment.createObject();
3997 rc = attachment->init(this,
3998 medium,
3999 aName,
4000 aControllerPort,
4001 aDevice,
4002 aType,
4003 fIndirect,
4004 false /* fPassthrough */,
4005 false /* fTempEject */,
4006 false /* fNonRotational */,
4007 false /* fDiscard */,
4008 fHotplug /* fHotPluggable */,
4009 Utf8Str::Empty);
4010 if (FAILED(rc)) return rc;
4011
4012 if (associate && !medium.isNull())
4013 {
4014 // as the last step, associate the medium to the VM
4015 rc = medium->i_addBackReference(mData->mUuid);
4016 // here we can fail because of Deleting, or being in process of creating a Diff
4017 if (FAILED(rc)) return rc;
4018
4019 mediumLock.release();
4020 treeLock.release();
4021 alock.release();
4022 i_addMediumToRegistry(medium);
4023 alock.acquire();
4024 treeLock.acquire();
4025 mediumLock.acquire();
4026 }
4027
4028 /* success: finally remember the attachment */
4029 i_setModified(IsModified_Storage);
4030 mMediumAttachments.backup();
4031 mMediumAttachments->push_back(attachment);
4032
4033 mediumLock.release();
4034 treeLock.release();
4035 alock.release();
4036
4037 if (fHotplug || fSilent)
4038 {
4039 if (!medium.isNull())
4040 {
4041 MediumLockList *pMediumLockList(new MediumLockList());
4042
4043 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4044 medium /* pToLockWrite */,
4045 false /* fMediumLockWriteAll */,
4046 NULL,
4047 *pMediumLockList);
4048 alock.acquire();
4049 if (FAILED(rc))
4050 delete pMediumLockList;
4051 else
4052 {
4053 mData->mSession.mLockedMedia.Unlock();
4054 alock.release();
4055 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4056 mData->mSession.mLockedMedia.Lock();
4057 alock.acquire();
4058 }
4059 alock.release();
4060 }
4061
4062 if (SUCCEEDED(rc))
4063 {
4064 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4065 /* Remove lock list in case of error. */
4066 if (FAILED(rc))
4067 {
4068 mData->mSession.mLockedMedia.Unlock();
4069 mData->mSession.mLockedMedia.Remove(attachment);
4070 mData->mSession.mLockedMedia.Lock();
4071 }
4072 }
4073 }
4074
4075 /* Save modified registries, but skip this machine as it's the caller's
4076 * job to save its settings like all other settings changes. */
4077 mParent->i_unmarkRegistryModified(i_getId());
4078 mParent->i_saveModifiedRegistries();
4079
4080 if (SUCCEEDED(rc))
4081 {
4082 if (fIndirect && medium != aM)
4083 mParent->i_onMediumConfigChanged(medium);
4084 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4085 }
4086
4087 return rc;
4088}
4089
4090HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4091 LONG aDevice)
4092{
4093 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4094 aName.c_str(), aControllerPort, aDevice));
4095
4096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4097
4098 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4099 if (FAILED(rc)) return rc;
4100
4101 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4102
4103 /* Check for an existing controller. */
4104 ComObjPtr<StorageController> ctl;
4105 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4106 if (FAILED(rc)) return rc;
4107
4108 StorageControllerType_T ctrlType;
4109 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4110 if (FAILED(rc))
4111 return setError(E_FAIL,
4112 tr("Could not get type of controller '%s'"),
4113 aName.c_str());
4114
4115 bool fSilent = false;
4116 Utf8Str strReconfig;
4117
4118 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4119 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4120 if ( mData->mMachineState == MachineState_Paused
4121 && strReconfig == "1")
4122 fSilent = true;
4123
4124 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4125 bool fHotplug = false;
4126 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4127 fHotplug = true;
4128
4129 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4130 return setError(VBOX_E_INVALID_VM_STATE,
4131 tr("Controller '%s' does not support hotplugging"),
4132 aName.c_str());
4133
4134 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4135 aName,
4136 aControllerPort,
4137 aDevice);
4138 if (!pAttach)
4139 return setError(VBOX_E_OBJECT_NOT_FOUND,
4140 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4141 aDevice, aControllerPort, aName.c_str());
4142
4143 if (fHotplug && !pAttach->i_getHotPluggable())
4144 return setError(VBOX_E_NOT_SUPPORTED,
4145 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4146 aDevice, aControllerPort, aName.c_str());
4147
4148 /*
4149 * The VM has to detach the device before we delete any implicit diffs.
4150 * If this fails we can roll back without loosing data.
4151 */
4152 if (fHotplug || fSilent)
4153 {
4154 alock.release();
4155 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4156 alock.acquire();
4157 }
4158 if (FAILED(rc)) return rc;
4159
4160 /* If we are here everything went well and we can delete the implicit now. */
4161 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4162
4163 alock.release();
4164
4165 /* Save modified registries, but skip this machine as it's the caller's
4166 * job to save its settings like all other settings changes. */
4167 mParent->i_unmarkRegistryModified(i_getId());
4168 mParent->i_saveModifiedRegistries();
4169
4170 if (SUCCEEDED(rc))
4171 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4172
4173 return rc;
4174}
4175
4176HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4177 LONG aDevice, BOOL aPassthrough)
4178{
4179 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4180 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4181
4182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4183
4184 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4185 if (FAILED(rc)) return rc;
4186
4187 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4188
4189 /* Check for an existing controller. */
4190 ComObjPtr<StorageController> ctl;
4191 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4192 if (FAILED(rc)) return rc;
4193
4194 StorageControllerType_T ctrlType;
4195 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4196 if (FAILED(rc))
4197 return setError(E_FAIL,
4198 tr("Could not get type of controller '%s'"),
4199 aName.c_str());
4200
4201 bool fSilent = false;
4202 Utf8Str strReconfig;
4203
4204 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4205 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4206 if ( mData->mMachineState == MachineState_Paused
4207 && strReconfig == "1")
4208 fSilent = true;
4209
4210 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4211 bool fHotplug = false;
4212 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4213 fHotplug = true;
4214
4215 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4216 return setError(VBOX_E_INVALID_VM_STATE,
4217 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4218 aName.c_str());
4219
4220 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4221 aName,
4222 aControllerPort,
4223 aDevice);
4224 if (!pAttach)
4225 return setError(VBOX_E_OBJECT_NOT_FOUND,
4226 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4227 aDevice, aControllerPort, aName.c_str());
4228
4229
4230 i_setModified(IsModified_Storage);
4231 mMediumAttachments.backup();
4232
4233 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4234
4235 if (pAttach->i_getType() != DeviceType_DVD)
4236 return setError(E_INVALIDARG,
4237 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4238 aDevice, aControllerPort, aName.c_str());
4239
4240 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4241
4242 pAttach->i_updatePassthrough(!!aPassthrough);
4243
4244 attLock.release();
4245 alock.release();
4246 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4247 if (SUCCEEDED(rc) && fValueChanged)
4248 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4249
4250 return rc;
4251}
4252
4253HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4254 LONG aDevice, BOOL aTemporaryEject)
4255{
4256
4257 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4258 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4259
4260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4261
4262 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4263 if (FAILED(rc)) return rc;
4264
4265 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4266 aName,
4267 aControllerPort,
4268 aDevice);
4269 if (!pAttach)
4270 return setError(VBOX_E_OBJECT_NOT_FOUND,
4271 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4272 aDevice, aControllerPort, aName.c_str());
4273
4274
4275 i_setModified(IsModified_Storage);
4276 mMediumAttachments.backup();
4277
4278 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4279
4280 if (pAttach->i_getType() != DeviceType_DVD)
4281 return setError(E_INVALIDARG,
4282 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4283 aDevice, aControllerPort, aName.c_str());
4284 pAttach->i_updateTempEject(!!aTemporaryEject);
4285
4286 return S_OK;
4287}
4288
4289HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4290 LONG aDevice, BOOL aNonRotational)
4291{
4292
4293 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4294 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4295
4296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4297
4298 HRESULT rc = i_checkStateDependency(MutableStateDep);
4299 if (FAILED(rc)) return rc;
4300
4301 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4302
4303 if (Global::IsOnlineOrTransient(mData->mMachineState))
4304 return setError(VBOX_E_INVALID_VM_STATE,
4305 tr("Invalid machine state: %s"),
4306 Global::stringifyMachineState(mData->mMachineState));
4307
4308 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4309 aName,
4310 aControllerPort,
4311 aDevice);
4312 if (!pAttach)
4313 return setError(VBOX_E_OBJECT_NOT_FOUND,
4314 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4315 aDevice, aControllerPort, aName.c_str());
4316
4317
4318 i_setModified(IsModified_Storage);
4319 mMediumAttachments.backup();
4320
4321 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4322
4323 if (pAttach->i_getType() != DeviceType_HardDisk)
4324 return setError(E_INVALIDARG,
4325 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"),
4326 aDevice, aControllerPort, aName.c_str());
4327 pAttach->i_updateNonRotational(!!aNonRotational);
4328
4329 return S_OK;
4330}
4331
4332HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4333 LONG aDevice, BOOL aDiscard)
4334{
4335
4336 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4337 aName.c_str(), aControllerPort, aDevice, aDiscard));
4338
4339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4340
4341 HRESULT rc = i_checkStateDependency(MutableStateDep);
4342 if (FAILED(rc)) return rc;
4343
4344 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4345
4346 if (Global::IsOnlineOrTransient(mData->mMachineState))
4347 return setError(VBOX_E_INVALID_VM_STATE,
4348 tr("Invalid machine state: %s"),
4349 Global::stringifyMachineState(mData->mMachineState));
4350
4351 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4352 aName,
4353 aControllerPort,
4354 aDevice);
4355 if (!pAttach)
4356 return setError(VBOX_E_OBJECT_NOT_FOUND,
4357 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4358 aDevice, aControllerPort, aName.c_str());
4359
4360
4361 i_setModified(IsModified_Storage);
4362 mMediumAttachments.backup();
4363
4364 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4365
4366 if (pAttach->i_getType() != DeviceType_HardDisk)
4367 return setError(E_INVALIDARG,
4368 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"),
4369 aDevice, aControllerPort, aName.c_str());
4370 pAttach->i_updateDiscard(!!aDiscard);
4371
4372 return S_OK;
4373}
4374
4375HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4376 LONG aDevice, BOOL aHotPluggable)
4377{
4378 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4379 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4380
4381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4382
4383 HRESULT rc = i_checkStateDependency(MutableStateDep);
4384 if (FAILED(rc)) return rc;
4385
4386 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4387
4388 if (Global::IsOnlineOrTransient(mData->mMachineState))
4389 return setError(VBOX_E_INVALID_VM_STATE,
4390 tr("Invalid machine state: %s"),
4391 Global::stringifyMachineState(mData->mMachineState));
4392
4393 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4394 aName,
4395 aControllerPort,
4396 aDevice);
4397 if (!pAttach)
4398 return setError(VBOX_E_OBJECT_NOT_FOUND,
4399 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4400 aDevice, aControllerPort, aName.c_str());
4401
4402 /* Check for an existing controller. */
4403 ComObjPtr<StorageController> ctl;
4404 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4405 if (FAILED(rc)) return rc;
4406
4407 StorageControllerType_T ctrlType;
4408 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4409 if (FAILED(rc))
4410 return setError(E_FAIL,
4411 tr("Could not get type of controller '%s'"),
4412 aName.c_str());
4413
4414 if (!i_isControllerHotplugCapable(ctrlType))
4415 return setError(VBOX_E_NOT_SUPPORTED,
4416 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4417 aName.c_str());
4418
4419 i_setModified(IsModified_Storage);
4420 mMediumAttachments.backup();
4421
4422 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4423
4424 if (pAttach->i_getType() == DeviceType_Floppy)
4425 return setError(E_INVALIDARG,
4426 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"),
4427 aDevice, aControllerPort, aName.c_str());
4428 pAttach->i_updateHotPluggable(!!aHotPluggable);
4429
4430 return S_OK;
4431}
4432
4433HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4434 LONG aDevice)
4435{
4436 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4437 aName.c_str(), aControllerPort, aDevice));
4438
4439 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4440}
4441
4442HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4443 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4444{
4445 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4446 aName.c_str(), aControllerPort, aDevice));
4447
4448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4449
4450 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4451 if (FAILED(rc)) return rc;
4452
4453 if (Global::IsOnlineOrTransient(mData->mMachineState))
4454 return setError(VBOX_E_INVALID_VM_STATE,
4455 tr("Invalid machine state: %s"),
4456 Global::stringifyMachineState(mData->mMachineState));
4457
4458 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4459 aName,
4460 aControllerPort,
4461 aDevice);
4462 if (!pAttach)
4463 return setError(VBOX_E_OBJECT_NOT_FOUND,
4464 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4465 aDevice, aControllerPort, aName.c_str());
4466
4467
4468 i_setModified(IsModified_Storage);
4469 mMediumAttachments.backup();
4470
4471 IBandwidthGroup *iB = aBandwidthGroup;
4472 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4473 if (aBandwidthGroup && group.isNull())
4474 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4475
4476 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4477
4478 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4479 if (strBandwidthGroupOld.isNotEmpty())
4480 {
4481 /* Get the bandwidth group object and release it - this must not fail. */
4482 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4483 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4484 Assert(SUCCEEDED(rc));
4485
4486 pBandwidthGroupOld->i_release();
4487 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4488 }
4489
4490 if (!group.isNull())
4491 {
4492 group->i_reference();
4493 pAttach->i_updateBandwidthGroup(group->i_getName());
4494 }
4495
4496 return S_OK;
4497}
4498
4499HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4500 LONG aControllerPort,
4501 LONG aDevice,
4502 DeviceType_T aType)
4503{
4504 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4505 aName.c_str(), aControllerPort, aDevice, aType));
4506
4507 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4508}
4509
4510
4511HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4512 LONG aControllerPort,
4513 LONG aDevice,
4514 BOOL aForce)
4515{
4516 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4517 aName.c_str(), aControllerPort, aForce));
4518
4519 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4520}
4521
4522HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4523 LONG aControllerPort,
4524 LONG aDevice,
4525 const ComPtr<IMedium> &aMedium,
4526 BOOL aForce)
4527{
4528 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4529 aName.c_str(), aControllerPort, aDevice, aForce));
4530
4531 // request the host lock first, since might be calling Host methods for getting host drives;
4532 // next, protect the media tree all the while we're in here, as well as our member variables
4533 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4534 this->lockHandle(),
4535 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4536
4537 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4538 if (FAILED(hrc)) return hrc;
4539
4540 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4541 aName,
4542 aControllerPort,
4543 aDevice);
4544 if (pAttach.isNull())
4545 return setError(VBOX_E_OBJECT_NOT_FOUND,
4546 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4547 aDevice, aControllerPort, aName.c_str());
4548
4549 /* Remember previously mounted medium. The medium before taking the
4550 * backup is not necessarily the same thing. */
4551 ComObjPtr<Medium> oldmedium;
4552 oldmedium = pAttach->i_getMedium();
4553
4554 IMedium *iM = aMedium;
4555 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4556 if (aMedium && pMedium.isNull())
4557 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4558
4559 AutoCaller mediumCaller(pMedium);
4560 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4561
4562 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4563 if (pMedium)
4564 {
4565 DeviceType_T mediumType = pAttach->i_getType();
4566 switch (mediumType)
4567 {
4568 case DeviceType_DVD:
4569 case DeviceType_Floppy:
4570 break;
4571
4572 default:
4573 return setError(VBOX_E_INVALID_OBJECT_STATE,
4574 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4575 aControllerPort,
4576 aDevice,
4577 aName.c_str());
4578 }
4579 }
4580
4581 i_setModified(IsModified_Storage);
4582 mMediumAttachments.backup();
4583
4584 {
4585 // The backup operation makes the pAttach reference point to the
4586 // old settings. Re-get the correct reference.
4587 pAttach = i_findAttachment(*mMediumAttachments.data(),
4588 aName,
4589 aControllerPort,
4590 aDevice);
4591 if (!oldmedium.isNull())
4592 oldmedium->i_removeBackReference(mData->mUuid);
4593 if (!pMedium.isNull())
4594 {
4595 pMedium->i_addBackReference(mData->mUuid);
4596
4597 mediumLock.release();
4598 multiLock.release();
4599 i_addMediumToRegistry(pMedium);
4600 multiLock.acquire();
4601 mediumLock.acquire();
4602 }
4603
4604 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4605 pAttach->i_updateMedium(pMedium);
4606 }
4607
4608 i_setModified(IsModified_Storage);
4609
4610 mediumLock.release();
4611 multiLock.release();
4612 HRESULT rc = i_onMediumChange(pAttach, aForce);
4613 multiLock.acquire();
4614 mediumLock.acquire();
4615
4616 /* On error roll back this change only. */
4617 if (FAILED(rc))
4618 {
4619 if (!pMedium.isNull())
4620 pMedium->i_removeBackReference(mData->mUuid);
4621 pAttach = i_findAttachment(*mMediumAttachments.data(),
4622 aName,
4623 aControllerPort,
4624 aDevice);
4625 /* If the attachment is gone in the meantime, bail out. */
4626 if (pAttach.isNull())
4627 return rc;
4628 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4629 if (!oldmedium.isNull())
4630 oldmedium->i_addBackReference(mData->mUuid);
4631 pAttach->i_updateMedium(oldmedium);
4632 }
4633
4634 mediumLock.release();
4635 multiLock.release();
4636
4637 /* Save modified registries, but skip this machine as it's the caller's
4638 * job to save its settings like all other settings changes. */
4639 mParent->i_unmarkRegistryModified(i_getId());
4640 mParent->i_saveModifiedRegistries();
4641
4642 return rc;
4643}
4644HRESULT Machine::getMedium(const com::Utf8Str &aName,
4645 LONG aControllerPort,
4646 LONG aDevice,
4647 ComPtr<IMedium> &aMedium)
4648{
4649 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4650 aName.c_str(), aControllerPort, aDevice));
4651
4652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4653
4654 aMedium = NULL;
4655
4656 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4657 aName,
4658 aControllerPort,
4659 aDevice);
4660 if (pAttach.isNull())
4661 return setError(VBOX_E_OBJECT_NOT_FOUND,
4662 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4663 aDevice, aControllerPort, aName.c_str());
4664
4665 aMedium = pAttach->i_getMedium();
4666
4667 return S_OK;
4668}
4669
4670HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4671{
4672 if (aSlot < RT_ELEMENTS(mSerialPorts))
4673 {
4674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4675 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4676 return S_OK;
4677 }
4678 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4679}
4680
4681HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4682{
4683 if (aSlot < RT_ELEMENTS(mParallelPorts))
4684 {
4685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4686 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4687 return S_OK;
4688 }
4689 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4690}
4691
4692
4693HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4694{
4695 /* Do not assert if slot is out of range, just return the advertised
4696 status. testdriver/vbox.py triggers this in logVmInfo. */
4697 if (aSlot >= mNetworkAdapters.size())
4698 return setError(E_INVALIDARG,
4699 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4700 aSlot, mNetworkAdapters.size());
4701
4702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4703
4704 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4705
4706 return S_OK;
4707}
4708
4709HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4710{
4711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4712
4713 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4714 size_t i = 0;
4715 for (settings::StringsMap::const_iterator
4716 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4717 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4718 ++it, ++i)
4719 aKeys[i] = it->first;
4720
4721 return S_OK;
4722}
4723
4724 /**
4725 * @note Locks this object for reading.
4726 */
4727HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4728 com::Utf8Str &aValue)
4729{
4730 /* start with nothing found */
4731 aValue = "";
4732
4733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4734
4735 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4736 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4737 // found:
4738 aValue = it->second; // source is a Utf8Str
4739
4740 /* return the result to caller (may be empty) */
4741 return S_OK;
4742}
4743
4744 /**
4745 * @note Locks mParent for writing + this object for writing.
4746 */
4747HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4748{
4749 /* Because control characters in aKey have caused problems in the settings
4750 * they are rejected unless the key should be deleted. */
4751 if (!aValue.isEmpty())
4752 {
4753 for (size_t i = 0; i < aKey.length(); ++i)
4754 {
4755 char ch = aKey[i];
4756 if (RTLocCIsCntrl(ch))
4757 return E_INVALIDARG;
4758 }
4759 }
4760
4761 Utf8Str strOldValue; // empty
4762
4763 // locking note: we only hold the read lock briefly to look up the old value,
4764 // then release it and call the onExtraCanChange callbacks. There is a small
4765 // chance of a race insofar as the callback might be called twice if two callers
4766 // change the same key at the same time, but that's a much better solution
4767 // than the deadlock we had here before. The actual changing of the extradata
4768 // is then performed under the write lock and race-free.
4769
4770 // look up the old value first; if nothing has changed then we need not do anything
4771 {
4772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4773
4774 // For snapshots don't even think about allowing changes, extradata
4775 // is global for a machine, so there is nothing snapshot specific.
4776 if (i_isSnapshotMachine())
4777 return setError(VBOX_E_INVALID_VM_STATE,
4778 tr("Cannot set extradata for a snapshot"));
4779
4780 // check if the right IMachine instance is used
4781 if (mData->mRegistered && !i_isSessionMachine())
4782 return setError(VBOX_E_INVALID_VM_STATE,
4783 tr("Cannot set extradata for an immutable machine"));
4784
4785 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4786 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4787 strOldValue = it->second;
4788 }
4789
4790 bool fChanged;
4791 if ((fChanged = (strOldValue != aValue)))
4792 {
4793 // ask for permission from all listeners outside the locks;
4794 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4795 // lock to copy the list of callbacks to invoke
4796 Bstr bstrError;
4797 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4798 {
4799 const char *sep = bstrError.isEmpty() ? "" : ": ";
4800 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4801 return setError(E_ACCESSDENIED,
4802 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4803 aKey.c_str(),
4804 aValue.c_str(),
4805 sep,
4806 bstrError.raw());
4807 }
4808
4809 // data is changing and change not vetoed: then write it out under the lock
4810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4811
4812 if (aValue.isEmpty())
4813 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4814 else
4815 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4816 // creates a new key if needed
4817
4818 bool fNeedsGlobalSaveSettings = false;
4819 // This saving of settings is tricky: there is no "old state" for the
4820 // extradata items at all (unlike all other settings), so the old/new
4821 // settings comparison would give a wrong result!
4822 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4823
4824 if (fNeedsGlobalSaveSettings)
4825 {
4826 // save the global settings; for that we should hold only the VirtualBox lock
4827 alock.release();
4828 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4829 mParent->i_saveSettings();
4830 }
4831 }
4832
4833 // fire notification outside the lock
4834 if (fChanged)
4835 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4836
4837 return S_OK;
4838}
4839
4840HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4841{
4842 aProgress = NULL;
4843 NOREF(aSettingsFilePath);
4844 ReturnComNotImplemented();
4845}
4846
4847HRESULT Machine::saveSettings()
4848{
4849 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4850
4851 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4852 if (FAILED(rc)) return rc;
4853
4854 /* the settings file path may never be null */
4855 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4856
4857 /* save all VM data excluding snapshots */
4858 bool fNeedsGlobalSaveSettings = false;
4859 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4860 mlock.release();
4861
4862 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4863 {
4864 // save the global settings; for that we should hold only the VirtualBox lock
4865 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4866 rc = mParent->i_saveSettings();
4867 }
4868
4869 return rc;
4870}
4871
4872
4873HRESULT Machine::discardSettings()
4874{
4875 /*
4876 * We need to take the machine list lock here as well as the machine one
4877 * or we'll get into trouble should any media stuff require rolling back.
4878 *
4879 * Details:
4880 *
4881 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4882 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4883 * 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]
4884 * 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
4885 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4886 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4887 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4888 * 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
4889 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4890 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4891 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4892 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4893 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4894 * 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]
4895 * 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] (*)
4896 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4897 * 0:005> k
4898 * # Child-SP RetAddr Call Site
4899 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4900 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4901 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4902 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4903 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4904 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4905 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4906 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4907 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4908 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4909 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4910 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4911 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4912 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4913 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4914 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4915 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4916 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4917 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4918 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4919 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4920 *
4921 */
4922 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4924
4925 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4926 if (FAILED(rc)) return rc;
4927
4928 /*
4929 * during this rollback, the session will be notified if data has
4930 * been actually changed
4931 */
4932 i_rollback(true /* aNotify */);
4933
4934 return S_OK;
4935}
4936
4937/** @note Locks objects! */
4938HRESULT Machine::unregister(AutoCaller &autoCaller,
4939 CleanupMode_T aCleanupMode,
4940 std::vector<ComPtr<IMedium> > &aMedia)
4941{
4942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4943
4944 Guid id(i_getId());
4945
4946 if (mData->mSession.mState != SessionState_Unlocked)
4947 return setError(VBOX_E_INVALID_OBJECT_STATE,
4948 tr("Cannot unregister the machine '%s' while it is locked"),
4949 mUserData->s.strName.c_str());
4950
4951 // wait for state dependents to drop to zero
4952 i_ensureNoStateDependencies(alock);
4953
4954 if (!mData->mAccessible)
4955 {
4956 // inaccessible machines can only be unregistered; uninitialize ourselves
4957 // here because currently there may be no unregistered that are inaccessible
4958 // (this state combination is not supported). Note releasing the caller and
4959 // leaving the lock before calling uninit()
4960 alock.release();
4961 autoCaller.release();
4962
4963 uninit();
4964
4965 mParent->i_unregisterMachine(this, id);
4966 // calls VirtualBox::i_saveSettings()
4967
4968 return S_OK;
4969 }
4970
4971 HRESULT rc = S_OK;
4972 mData->llFilesToDelete.clear();
4973
4974 if (!mSSData->strStateFilePath.isEmpty())
4975 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4976
4977 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4978 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4979 mData->llFilesToDelete.push_back(strNVRAMFile);
4980
4981 // This list collects the medium objects from all medium attachments
4982 // which we will detach from the machine and its snapshots, in a specific
4983 // order which allows for closing all media without getting "media in use"
4984 // errors, simply by going through the list from the front to the back:
4985 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4986 // and must be closed before the parent media from the snapshots, or closing the parents
4987 // will fail because they still have children);
4988 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4989 // the root ("first") snapshot of the machine.
4990 MediaList llMedia;
4991
4992 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4993 && mMediumAttachments->size()
4994 )
4995 {
4996 // we have media attachments: detach them all and add the Medium objects to our list
4997 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4998 }
4999
5000 if (mData->mFirstSnapshot)
5001 {
5002 // add the media from the medium attachments of the snapshots to llMedia
5003 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5004 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5005 // into the children first
5006
5007 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5008 MachineState_T oldState = mData->mMachineState;
5009 mData->mMachineState = MachineState_DeletingSnapshot;
5010
5011 // make a copy of the first snapshot reference so the refcount does not
5012 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5013 // (would hang due to the AutoCaller voodoo)
5014 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5015
5016 // GO!
5017 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5018
5019 mData->mMachineState = oldState;
5020 }
5021
5022 if (FAILED(rc))
5023 {
5024 i_rollbackMedia();
5025 return rc;
5026 }
5027
5028 // commit all the media changes made above
5029 i_commitMedia();
5030
5031 mData->mRegistered = false;
5032
5033 // machine lock no longer needed
5034 alock.release();
5035
5036 /* Make sure that the settings of the current VM are not saved, because
5037 * they are rather crippled at this point to meet the cleanup expectations
5038 * and there's no point destroying the VM config on disk just because. */
5039 mParent->i_unmarkRegistryModified(id);
5040
5041 // return media to caller
5042 aMedia.resize(llMedia.size());
5043 size_t i = 0;
5044 for (MediaList::const_iterator
5045 it = llMedia.begin();
5046 it != llMedia.end();
5047 ++it, ++i)
5048 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5049
5050 mParent->i_unregisterMachine(this, id);
5051 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5052
5053 return S_OK;
5054}
5055
5056/**
5057 * Task record for deleting a machine config.
5058 */
5059class Machine::DeleteConfigTask
5060 : public Machine::Task
5061{
5062public:
5063 DeleteConfigTask(Machine *m,
5064 Progress *p,
5065 const Utf8Str &t,
5066 const RTCList<ComPtr<IMedium> > &llMediums,
5067 const StringsList &llFilesToDelete)
5068 : Task(m, p, t),
5069 m_llMediums(llMediums),
5070 m_llFilesToDelete(llFilesToDelete)
5071 {}
5072
5073private:
5074 void handler()
5075 {
5076 try
5077 {
5078 m_pMachine->i_deleteConfigHandler(*this);
5079 }
5080 catch (...)
5081 {
5082 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5083 }
5084 }
5085
5086 RTCList<ComPtr<IMedium> > m_llMediums;
5087 StringsList m_llFilesToDelete;
5088
5089 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5090};
5091
5092/**
5093 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5094 * SessionMachine::taskHandler().
5095 *
5096 * @note Locks this object for writing.
5097 *
5098 * @param task
5099 * @return
5100 */
5101void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5102{
5103 LogFlowThisFuncEnter();
5104
5105 AutoCaller autoCaller(this);
5106 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5107 if (FAILED(autoCaller.rc()))
5108 {
5109 /* we might have been uninitialized because the session was accidentally
5110 * closed by the client, so don't assert */
5111 HRESULT rc = setError(E_FAIL,
5112 tr("The session has been accidentally closed"));
5113 task.m_pProgress->i_notifyComplete(rc);
5114 LogFlowThisFuncLeave();
5115 return;
5116 }
5117
5118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5119
5120 HRESULT rc = S_OK;
5121
5122 try
5123 {
5124 ULONG uLogHistoryCount = 3;
5125 ComPtr<ISystemProperties> systemProperties;
5126 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5127 if (FAILED(rc)) throw rc;
5128
5129 if (!systemProperties.isNull())
5130 {
5131 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5132 if (FAILED(rc)) throw rc;
5133 }
5134
5135 MachineState_T oldState = mData->mMachineState;
5136 i_setMachineState(MachineState_SettingUp);
5137 alock.release();
5138 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5139 {
5140 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5141 {
5142 AutoCaller mac(pMedium);
5143 if (FAILED(mac.rc())) throw mac.rc();
5144 Utf8Str strLocation = pMedium->i_getLocationFull();
5145 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5146 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5147 if (FAILED(rc)) throw rc;
5148 }
5149 if (pMedium->i_isMediumFormatFile())
5150 {
5151 ComPtr<IProgress> pProgress2;
5152 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5153 if (FAILED(rc)) throw rc;
5154 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5155 if (FAILED(rc)) throw rc;
5156 }
5157
5158 /* Close the medium, deliberately without checking the return
5159 * code, and without leaving any trace in the error info, as
5160 * a failure here is a very minor issue, which shouldn't happen
5161 * as above we even managed to delete the medium. */
5162 {
5163 ErrorInfoKeeper eik;
5164 pMedium->Close();
5165 }
5166 }
5167 i_setMachineState(oldState);
5168 alock.acquire();
5169
5170 // delete the files pushed on the task list by Machine::Delete()
5171 // (this includes saved states of the machine and snapshots and
5172 // medium storage files from the IMedium list passed in, and the
5173 // machine XML file)
5174 for (StringsList::const_iterator
5175 it = task.m_llFilesToDelete.begin();
5176 it != task.m_llFilesToDelete.end();
5177 ++it)
5178 {
5179 const Utf8Str &strFile = *it;
5180 LogFunc(("Deleting file %s\n", strFile.c_str()));
5181 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5182 if (FAILED(rc)) throw rc;
5183
5184 int vrc = RTFileDelete(strFile.c_str());
5185 if (RT_FAILURE(vrc))
5186 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5187 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5188 }
5189
5190 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5191 if (FAILED(rc)) throw rc;
5192
5193 /* delete the settings only when the file actually exists */
5194 if (mData->pMachineConfigFile->fileExists())
5195 {
5196 /* Delete any backup or uncommitted XML files. Ignore failures.
5197 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5198 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5199 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5200 RTFileDelete(otherXml.c_str());
5201 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5202 RTFileDelete(otherXml.c_str());
5203
5204 /* delete the Logs folder, nothing important should be left
5205 * there (we don't check for errors because the user might have
5206 * some private files there that we don't want to delete) */
5207 Utf8Str logFolder;
5208 getLogFolder(logFolder);
5209 Assert(logFolder.length());
5210 if (RTDirExists(logFolder.c_str()))
5211 {
5212 /* Delete all VBox.log[.N] files from the Logs folder
5213 * (this must be in sync with the rotation logic in
5214 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5215 * files that may have been created by the GUI. */
5216 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5217 RTFileDelete(log.c_str());
5218 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5219 RTFileDelete(log.c_str());
5220 for (ULONG i = uLogHistoryCount; i > 0; i--)
5221 {
5222 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5223 RTFileDelete(log.c_str());
5224 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5225 RTFileDelete(log.c_str());
5226 }
5227 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5228 RTFileDelete(log.c_str());
5229#if defined(RT_OS_WINDOWS)
5230 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5231 RTFileDelete(log.c_str());
5232 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5233 RTFileDelete(log.c_str());
5234#endif
5235
5236 RTDirRemove(logFolder.c_str());
5237 }
5238
5239 /* delete the Snapshots folder, nothing important should be left
5240 * there (we don't check for errors because the user might have
5241 * some private files there that we don't want to delete) */
5242 Utf8Str strFullSnapshotFolder;
5243 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5244 Assert(!strFullSnapshotFolder.isEmpty());
5245 if (RTDirExists(strFullSnapshotFolder.c_str()))
5246 RTDirRemove(strFullSnapshotFolder.c_str());
5247
5248 // delete the directory that contains the settings file, but only
5249 // if it matches the VM name
5250 Utf8Str settingsDir;
5251 if (i_isInOwnDir(&settingsDir))
5252 RTDirRemove(settingsDir.c_str());
5253 }
5254
5255 alock.release();
5256
5257 mParent->i_saveModifiedRegistries();
5258 }
5259 catch (HRESULT aRC) { rc = aRC; }
5260
5261 task.m_pProgress->i_notifyComplete(rc);
5262
5263 LogFlowThisFuncLeave();
5264}
5265
5266HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5267{
5268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5269
5270 HRESULT rc = i_checkStateDependency(MutableStateDep);
5271 if (FAILED(rc)) return rc;
5272
5273 if (mData->mRegistered)
5274 return setError(VBOX_E_INVALID_VM_STATE,
5275 tr("Cannot delete settings of a registered machine"));
5276
5277 // collect files to delete
5278 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5279 // machine config file
5280 if (mData->pMachineConfigFile->fileExists())
5281 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5282 // backup of machine config file
5283 Utf8Str strTmp(mData->m_strConfigFileFull);
5284 strTmp.append("-prev");
5285 if (RTFileExists(strTmp.c_str()))
5286 llFilesToDelete.push_back(strTmp);
5287
5288 RTCList<ComPtr<IMedium> > llMediums;
5289 for (size_t i = 0; i < aMedia.size(); ++i)
5290 {
5291 IMedium *pIMedium(aMedia[i]);
5292 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5293 if (pMedium.isNull())
5294 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5295 SafeArray<BSTR> ids;
5296 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5297 if (FAILED(rc)) return rc;
5298 /* At this point the medium should not have any back references
5299 * anymore. If it has it is attached to another VM and *must* not
5300 * deleted. */
5301 if (ids.size() < 1)
5302 llMediums.append(pMedium);
5303 }
5304
5305 ComObjPtr<Progress> pProgress;
5306 pProgress.createObject();
5307 rc = pProgress->init(i_getVirtualBox(),
5308 static_cast<IMachine*>(this) /* aInitiator */,
5309 tr("Deleting files"),
5310 true /* fCancellable */,
5311 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5312 tr("Collecting file inventory"));
5313 if (FAILED(rc))
5314 return rc;
5315
5316 /* create and start the task on a separate thread (note that it will not
5317 * start working until we release alock) */
5318 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5319 rc = pTask->createThread();
5320 pTask = NULL;
5321 if (FAILED(rc))
5322 return rc;
5323
5324 pProgress.queryInterfaceTo(aProgress.asOutParam());
5325
5326 LogFlowFuncLeave();
5327
5328 return S_OK;
5329}
5330
5331HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5332{
5333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5334
5335 ComObjPtr<Snapshot> pSnapshot;
5336 HRESULT rc;
5337
5338 if (aNameOrId.isEmpty())
5339 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5340 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5341 else
5342 {
5343 Guid uuid(aNameOrId);
5344 if (uuid.isValid())
5345 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5346 else
5347 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5348 }
5349 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5350
5351 return rc;
5352}
5353
5354HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5355 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5356{
5357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5358
5359 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5360 if (FAILED(rc)) return rc;
5361
5362 ComObjPtr<SharedFolder> sharedFolder;
5363 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5364 if (SUCCEEDED(rc))
5365 return setError(VBOX_E_OBJECT_IN_USE,
5366 tr("Shared folder named '%s' already exists"),
5367 aName.c_str());
5368
5369 sharedFolder.createObject();
5370 rc = sharedFolder->init(i_getMachine(),
5371 aName,
5372 aHostPath,
5373 !!aWritable,
5374 !!aAutomount,
5375 aAutoMountPoint,
5376 true /* fFailOnError */);
5377 if (FAILED(rc)) return rc;
5378
5379 i_setModified(IsModified_SharedFolders);
5380 mHWData.backup();
5381 mHWData->mSharedFolders.push_back(sharedFolder);
5382
5383 /* inform the direct session if any */
5384 alock.release();
5385 i_onSharedFolderChange();
5386
5387 return S_OK;
5388}
5389
5390HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5391{
5392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5393
5394 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5395 if (FAILED(rc)) return rc;
5396
5397 ComObjPtr<SharedFolder> sharedFolder;
5398 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5399 if (FAILED(rc)) return rc;
5400
5401 i_setModified(IsModified_SharedFolders);
5402 mHWData.backup();
5403 mHWData->mSharedFolders.remove(sharedFolder);
5404
5405 /* inform the direct session if any */
5406 alock.release();
5407 i_onSharedFolderChange();
5408
5409 return S_OK;
5410}
5411
5412HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5413{
5414 /* start with No */
5415 *aCanShow = FALSE;
5416
5417 ComPtr<IInternalSessionControl> directControl;
5418 {
5419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5420
5421 if (mData->mSession.mState != SessionState_Locked)
5422 return setError(VBOX_E_INVALID_VM_STATE,
5423 tr("Machine is not locked for session (session state: %s)"),
5424 Global::stringifySessionState(mData->mSession.mState));
5425
5426 if (mData->mSession.mLockType == LockType_VM)
5427 directControl = mData->mSession.mDirectControl;
5428 }
5429
5430 /* ignore calls made after #OnSessionEnd() is called */
5431 if (!directControl)
5432 return S_OK;
5433
5434 LONG64 dummy;
5435 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5436}
5437
5438HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5439{
5440 ComPtr<IInternalSessionControl> directControl;
5441 {
5442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5443
5444 if (mData->mSession.mState != SessionState_Locked)
5445 return setError(E_FAIL,
5446 tr("Machine is not locked for session (session state: %s)"),
5447 Global::stringifySessionState(mData->mSession.mState));
5448
5449 if (mData->mSession.mLockType == LockType_VM)
5450 directControl = mData->mSession.mDirectControl;
5451 }
5452
5453 /* ignore calls made after #OnSessionEnd() is called */
5454 if (!directControl)
5455 return S_OK;
5456
5457 BOOL dummy;
5458 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5459}
5460
5461#ifdef VBOX_WITH_GUEST_PROPS
5462/**
5463 * Look up a guest property in VBoxSVC's internal structures.
5464 */
5465HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5466 com::Utf8Str &aValue,
5467 LONG64 *aTimestamp,
5468 com::Utf8Str &aFlags) const
5469{
5470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5471
5472 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5473 if (it != mHWData->mGuestProperties.end())
5474 {
5475 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5476 aValue = it->second.strValue;
5477 *aTimestamp = it->second.mTimestamp;
5478 GuestPropWriteFlags(it->second.mFlags, szFlags);
5479 aFlags = Utf8Str(szFlags);
5480 }
5481
5482 return S_OK;
5483}
5484
5485/**
5486 * Query the VM that a guest property belongs to for the property.
5487 * @returns E_ACCESSDENIED if the VM process is not available or not
5488 * currently handling queries and the lookup should then be done in
5489 * VBoxSVC.
5490 */
5491HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5492 com::Utf8Str &aValue,
5493 LONG64 *aTimestamp,
5494 com::Utf8Str &aFlags) const
5495{
5496 HRESULT rc = S_OK;
5497 Bstr bstrValue;
5498 Bstr bstrFlags;
5499
5500 ComPtr<IInternalSessionControl> directControl;
5501 {
5502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5503 if (mData->mSession.mLockType == LockType_VM)
5504 directControl = mData->mSession.mDirectControl;
5505 }
5506
5507 /* ignore calls made after #OnSessionEnd() is called */
5508 if (!directControl)
5509 rc = E_ACCESSDENIED;
5510 else
5511 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5512 0 /* accessMode */,
5513 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5514
5515 aValue = bstrValue;
5516 aFlags = bstrFlags;
5517
5518 return rc;
5519}
5520#endif // VBOX_WITH_GUEST_PROPS
5521
5522HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5523 com::Utf8Str &aValue,
5524 LONG64 *aTimestamp,
5525 com::Utf8Str &aFlags)
5526{
5527#ifndef VBOX_WITH_GUEST_PROPS
5528 ReturnComNotImplemented();
5529#else // VBOX_WITH_GUEST_PROPS
5530
5531 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5532
5533 if (rc == E_ACCESSDENIED)
5534 /* The VM is not running or the service is not (yet) accessible */
5535 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5536 return rc;
5537#endif // VBOX_WITH_GUEST_PROPS
5538}
5539
5540HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5541{
5542 LONG64 dummyTimestamp;
5543 com::Utf8Str dummyFlags;
5544 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5545 return rc;
5546
5547}
5548HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5549{
5550 com::Utf8Str dummyFlags;
5551 com::Utf8Str dummyValue;
5552 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5553 return rc;
5554}
5555
5556#ifdef VBOX_WITH_GUEST_PROPS
5557/**
5558 * Set a guest property in VBoxSVC's internal structures.
5559 */
5560HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5561 const com::Utf8Str &aFlags, bool fDelete)
5562{
5563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5564 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5565 if (FAILED(rc)) return rc;
5566
5567 try
5568 {
5569 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5570 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5571 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5572
5573 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5574 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5575
5576 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5577 if (it == mHWData->mGuestProperties.end())
5578 {
5579 if (!fDelete)
5580 {
5581 i_setModified(IsModified_MachineData);
5582 mHWData.backupEx();
5583
5584 RTTIMESPEC time;
5585 HWData::GuestProperty prop;
5586 prop.strValue = aValue;
5587 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5588 prop.mFlags = fFlags;
5589 mHWData->mGuestProperties[aName] = prop;
5590 }
5591 }
5592 else
5593 {
5594 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5595 {
5596 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5597 }
5598 else
5599 {
5600 i_setModified(IsModified_MachineData);
5601 mHWData.backupEx();
5602
5603 /* The backupEx() operation invalidates our iterator,
5604 * so get a new one. */
5605 it = mHWData->mGuestProperties.find(aName);
5606 Assert(it != mHWData->mGuestProperties.end());
5607
5608 if (!fDelete)
5609 {
5610 RTTIMESPEC time;
5611 it->second.strValue = aValue;
5612 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5613 it->second.mFlags = fFlags;
5614 }
5615 else
5616 mHWData->mGuestProperties.erase(it);
5617 }
5618 }
5619
5620 if (SUCCEEDED(rc))
5621 {
5622 alock.release();
5623
5624 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5625 }
5626 }
5627 catch (std::bad_alloc &)
5628 {
5629 rc = E_OUTOFMEMORY;
5630 }
5631
5632 return rc;
5633}
5634
5635/**
5636 * Set a property on the VM that that property belongs to.
5637 * @returns E_ACCESSDENIED if the VM process is not available or not
5638 * currently handling queries and the setting should then be done in
5639 * VBoxSVC.
5640 */
5641HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5642 const com::Utf8Str &aFlags, bool fDelete)
5643{
5644 HRESULT rc;
5645
5646 try
5647 {
5648 ComPtr<IInternalSessionControl> directControl;
5649 {
5650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5651 if (mData->mSession.mLockType == LockType_VM)
5652 directControl = mData->mSession.mDirectControl;
5653 }
5654
5655 Bstr dummy1; /* will not be changed (setter) */
5656 Bstr dummy2; /* will not be changed (setter) */
5657 LONG64 dummy64;
5658 if (!directControl)
5659 rc = E_ACCESSDENIED;
5660 else
5661 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5662 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5663 fDelete ? 2 : 1 /* accessMode */,
5664 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5665 }
5666 catch (std::bad_alloc &)
5667 {
5668 rc = E_OUTOFMEMORY;
5669 }
5670
5671 return rc;
5672}
5673#endif // VBOX_WITH_GUEST_PROPS
5674
5675HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5676 const com::Utf8Str &aFlags)
5677{
5678#ifndef VBOX_WITH_GUEST_PROPS
5679 ReturnComNotImplemented();
5680#else // VBOX_WITH_GUEST_PROPS
5681 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5682 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5683
5684 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5685 if (rc == E_ACCESSDENIED)
5686 /* The VM is not running or the service is not (yet) accessible */
5687 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5688 return rc;
5689#endif // VBOX_WITH_GUEST_PROPS
5690}
5691
5692HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5693{
5694 return setGuestProperty(aProperty, aValue, "");
5695}
5696
5697HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5698{
5699#ifndef VBOX_WITH_GUEST_PROPS
5700 ReturnComNotImplemented();
5701#else // VBOX_WITH_GUEST_PROPS
5702 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5703 if (rc == E_ACCESSDENIED)
5704 /* The VM is not running or the service is not (yet) accessible */
5705 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5706 return rc;
5707#endif // VBOX_WITH_GUEST_PROPS
5708}
5709
5710#ifdef VBOX_WITH_GUEST_PROPS
5711/**
5712 * Enumerate the guest properties in VBoxSVC's internal structures.
5713 */
5714HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5715 std::vector<com::Utf8Str> &aNames,
5716 std::vector<com::Utf8Str> &aValues,
5717 std::vector<LONG64> &aTimestamps,
5718 std::vector<com::Utf8Str> &aFlags)
5719{
5720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5721 Utf8Str strPatterns(aPatterns);
5722
5723 /*
5724 * Look for matching patterns and build up a list.
5725 */
5726 HWData::GuestPropertyMap propMap;
5727 for (HWData::GuestPropertyMap::const_iterator
5728 it = mHWData->mGuestProperties.begin();
5729 it != mHWData->mGuestProperties.end();
5730 ++it)
5731 {
5732 if ( strPatterns.isEmpty()
5733 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5734 RTSTR_MAX,
5735 it->first.c_str(),
5736 RTSTR_MAX,
5737 NULL)
5738 )
5739 propMap.insert(*it);
5740 }
5741
5742 alock.release();
5743
5744 /*
5745 * And build up the arrays for returning the property information.
5746 */
5747 size_t cEntries = propMap.size();
5748
5749 aNames.resize(cEntries);
5750 aValues.resize(cEntries);
5751 aTimestamps.resize(cEntries);
5752 aFlags.resize(cEntries);
5753
5754 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5755 size_t i = 0;
5756 for (HWData::GuestPropertyMap::const_iterator
5757 it = propMap.begin();
5758 it != propMap.end();
5759 ++it, ++i)
5760 {
5761 aNames[i] = it->first;
5762 aValues[i] = it->second.strValue;
5763 aTimestamps[i] = it->second.mTimestamp;
5764 GuestPropWriteFlags(it->second.mFlags, szFlags);
5765 aFlags[i] = Utf8Str(szFlags);
5766
5767 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5768 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5769 }
5770
5771 return S_OK;
5772}
5773
5774/**
5775 * Enumerate the properties managed by a VM.
5776 * @returns E_ACCESSDENIED if the VM process is not available or not
5777 * currently handling queries and the setting should then be done in
5778 * VBoxSVC.
5779 */
5780HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5781 std::vector<com::Utf8Str> &aNames,
5782 std::vector<com::Utf8Str> &aValues,
5783 std::vector<LONG64> &aTimestamps,
5784 std::vector<com::Utf8Str> &aFlags)
5785{
5786 HRESULT rc;
5787 ComPtr<IInternalSessionControl> directControl;
5788 {
5789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5790 if (mData->mSession.mLockType == LockType_VM)
5791 directControl = mData->mSession.mDirectControl;
5792 }
5793
5794 com::SafeArray<BSTR> bNames;
5795 com::SafeArray<BSTR> bValues;
5796 com::SafeArray<LONG64> bTimestamps;
5797 com::SafeArray<BSTR> bFlags;
5798
5799 if (!directControl)
5800 rc = E_ACCESSDENIED;
5801 else
5802 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5803 ComSafeArrayAsOutParam(bNames),
5804 ComSafeArrayAsOutParam(bValues),
5805 ComSafeArrayAsOutParam(bTimestamps),
5806 ComSafeArrayAsOutParam(bFlags));
5807 size_t i;
5808 aNames.resize(bNames.size());
5809 for (i = 0; i < bNames.size(); ++i)
5810 aNames[i] = Utf8Str(bNames[i]);
5811 aValues.resize(bValues.size());
5812 for (i = 0; i < bValues.size(); ++i)
5813 aValues[i] = Utf8Str(bValues[i]);
5814 aTimestamps.resize(bTimestamps.size());
5815 for (i = 0; i < bTimestamps.size(); ++i)
5816 aTimestamps[i] = bTimestamps[i];
5817 aFlags.resize(bFlags.size());
5818 for (i = 0; i < bFlags.size(); ++i)
5819 aFlags[i] = Utf8Str(bFlags[i]);
5820
5821 return rc;
5822}
5823#endif // VBOX_WITH_GUEST_PROPS
5824HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5825 std::vector<com::Utf8Str> &aNames,
5826 std::vector<com::Utf8Str> &aValues,
5827 std::vector<LONG64> &aTimestamps,
5828 std::vector<com::Utf8Str> &aFlags)
5829{
5830#ifndef VBOX_WITH_GUEST_PROPS
5831 ReturnComNotImplemented();
5832#else // VBOX_WITH_GUEST_PROPS
5833
5834 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5835
5836 if (rc == E_ACCESSDENIED)
5837 /* The VM is not running or the service is not (yet) accessible */
5838 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5839 return rc;
5840#endif // VBOX_WITH_GUEST_PROPS
5841}
5842
5843HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5844 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5845{
5846 MediumAttachmentList atts;
5847
5848 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5849 if (FAILED(rc)) return rc;
5850
5851 aMediumAttachments.resize(atts.size());
5852 size_t i = 0;
5853 for (MediumAttachmentList::const_iterator
5854 it = atts.begin();
5855 it != atts.end();
5856 ++it, ++i)
5857 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5858
5859 return S_OK;
5860}
5861
5862HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5863 LONG aControllerPort,
5864 LONG aDevice,
5865 ComPtr<IMediumAttachment> &aAttachment)
5866{
5867 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5868 aName.c_str(), aControllerPort, aDevice));
5869
5870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5871
5872 aAttachment = NULL;
5873
5874 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5875 aName,
5876 aControllerPort,
5877 aDevice);
5878 if (pAttach.isNull())
5879 return setError(VBOX_E_OBJECT_NOT_FOUND,
5880 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5881 aDevice, aControllerPort, aName.c_str());
5882
5883 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5884
5885 return S_OK;
5886}
5887
5888
5889HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5890 StorageBus_T aConnectionType,
5891 ComPtr<IStorageController> &aController)
5892{
5893 if ( (aConnectionType <= StorageBus_Null)
5894 || (aConnectionType > StorageBus_VirtioSCSI))
5895 return setError(E_INVALIDARG,
5896 tr("Invalid connection type: %d"),
5897 aConnectionType);
5898
5899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5900
5901 HRESULT rc = i_checkStateDependency(MutableStateDep);
5902 if (FAILED(rc)) return rc;
5903
5904 /* try to find one with the name first. */
5905 ComObjPtr<StorageController> ctrl;
5906
5907 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5908 if (SUCCEEDED(rc))
5909 return setError(VBOX_E_OBJECT_IN_USE,
5910 tr("Storage controller named '%s' already exists"),
5911 aName.c_str());
5912
5913 ctrl.createObject();
5914
5915 /* get a new instance number for the storage controller */
5916 ULONG ulInstance = 0;
5917 bool fBootable = true;
5918 for (StorageControllerList::const_iterator
5919 it = mStorageControllers->begin();
5920 it != mStorageControllers->end();
5921 ++it)
5922 {
5923 if ((*it)->i_getStorageBus() == aConnectionType)
5924 {
5925 ULONG ulCurInst = (*it)->i_getInstance();
5926
5927 if (ulCurInst >= ulInstance)
5928 ulInstance = ulCurInst + 1;
5929
5930 /* Only one controller of each type can be marked as bootable. */
5931 if ((*it)->i_getBootable())
5932 fBootable = false;
5933 }
5934 }
5935
5936 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5937 if (FAILED(rc)) return rc;
5938
5939 i_setModified(IsModified_Storage);
5940 mStorageControllers.backup();
5941 mStorageControllers->push_back(ctrl);
5942
5943 ctrl.queryInterfaceTo(aController.asOutParam());
5944
5945 /* inform the direct session if any */
5946 alock.release();
5947 i_onStorageControllerChange(i_getId(), aName);
5948
5949 return S_OK;
5950}
5951
5952HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5953 ComPtr<IStorageController> &aStorageController)
5954{
5955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5956
5957 ComObjPtr<StorageController> ctrl;
5958
5959 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5960 if (SUCCEEDED(rc))
5961 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5962
5963 return rc;
5964}
5965
5966HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5967 ULONG aInstance,
5968 ComPtr<IStorageController> &aStorageController)
5969{
5970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5971
5972 for (StorageControllerList::const_iterator
5973 it = mStorageControllers->begin();
5974 it != mStorageControllers->end();
5975 ++it)
5976 {
5977 if ( (*it)->i_getStorageBus() == aConnectionType
5978 && (*it)->i_getInstance() == aInstance)
5979 {
5980 (*it).queryInterfaceTo(aStorageController.asOutParam());
5981 return S_OK;
5982 }
5983 }
5984
5985 return setError(VBOX_E_OBJECT_NOT_FOUND,
5986 tr("Could not find a storage controller with instance number '%lu'"),
5987 aInstance);
5988}
5989
5990HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5991{
5992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5993
5994 HRESULT rc = i_checkStateDependency(MutableStateDep);
5995 if (FAILED(rc)) return rc;
5996
5997 ComObjPtr<StorageController> ctrl;
5998
5999 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6000 if (SUCCEEDED(rc))
6001 {
6002 /* Ensure that only one controller of each type is marked as bootable. */
6003 if (aBootable == TRUE)
6004 {
6005 for (StorageControllerList::const_iterator
6006 it = mStorageControllers->begin();
6007 it != mStorageControllers->end();
6008 ++it)
6009 {
6010 ComObjPtr<StorageController> aCtrl = (*it);
6011
6012 if ( (aCtrl->i_getName() != aName)
6013 && aCtrl->i_getBootable() == TRUE
6014 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6015 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6016 {
6017 aCtrl->i_setBootable(FALSE);
6018 break;
6019 }
6020 }
6021 }
6022
6023 if (SUCCEEDED(rc))
6024 {
6025 ctrl->i_setBootable(aBootable);
6026 i_setModified(IsModified_Storage);
6027 }
6028 }
6029
6030 if (SUCCEEDED(rc))
6031 {
6032 /* inform the direct session if any */
6033 alock.release();
6034 i_onStorageControllerChange(i_getId(), aName);
6035 }
6036
6037 return rc;
6038}
6039
6040HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6041{
6042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6043
6044 HRESULT rc = i_checkStateDependency(MutableStateDep);
6045 if (FAILED(rc)) return rc;
6046
6047 ComObjPtr<StorageController> ctrl;
6048 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6049 if (FAILED(rc)) return rc;
6050
6051 MediumAttachmentList llDetachedAttachments;
6052 {
6053 /* find all attached devices to the appropriate storage controller and detach them all */
6054 // make a temporary list because detachDevice invalidates iterators into
6055 // mMediumAttachments
6056 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6057
6058 for (MediumAttachmentList::const_iterator
6059 it = llAttachments2.begin();
6060 it != llAttachments2.end();
6061 ++it)
6062 {
6063 MediumAttachment *pAttachTemp = *it;
6064
6065 AutoCaller localAutoCaller(pAttachTemp);
6066 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6067
6068 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6069
6070 if (pAttachTemp->i_getControllerName() == aName)
6071 {
6072 llDetachedAttachments.push_back(pAttachTemp);
6073 rc = i_detachDevice(pAttachTemp, alock, NULL);
6074 if (FAILED(rc)) return rc;
6075 }
6076 }
6077 }
6078
6079 /* send event about detached devices before removing parent controller */
6080 for (MediumAttachmentList::const_iterator
6081 it = llDetachedAttachments.begin();
6082 it != llDetachedAttachments.end();
6083 ++it)
6084 {
6085 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6086 }
6087
6088 /* We can remove it now. */
6089 i_setModified(IsModified_Storage);
6090 mStorageControllers.backup();
6091
6092 ctrl->i_unshare();
6093
6094 mStorageControllers->remove(ctrl);
6095
6096 /* inform the direct session if any */
6097 alock.release();
6098 i_onStorageControllerChange(i_getId(), aName);
6099
6100 return S_OK;
6101}
6102
6103HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6104 ComPtr<IUSBController> &aController)
6105{
6106 if ( (aType <= USBControllerType_Null)
6107 || (aType >= USBControllerType_Last))
6108 return setError(E_INVALIDARG,
6109 tr("Invalid USB controller type: %d"),
6110 aType);
6111
6112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6113
6114 HRESULT rc = i_checkStateDependency(MutableStateDep);
6115 if (FAILED(rc)) return rc;
6116
6117 /* try to find one with the same type first. */
6118 ComObjPtr<USBController> ctrl;
6119
6120 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6121 if (SUCCEEDED(rc))
6122 return setError(VBOX_E_OBJECT_IN_USE,
6123 tr("USB controller named '%s' already exists"),
6124 aName.c_str());
6125
6126 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6127 ULONG maxInstances;
6128 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6129 if (FAILED(rc))
6130 return rc;
6131
6132 ULONG cInstances = i_getUSBControllerCountByType(aType);
6133 if (cInstances >= maxInstances)
6134 return setError(E_INVALIDARG,
6135 tr("Too many USB controllers of this type"));
6136
6137 ctrl.createObject();
6138
6139 rc = ctrl->init(this, aName, aType);
6140 if (FAILED(rc)) return rc;
6141
6142 i_setModified(IsModified_USB);
6143 mUSBControllers.backup();
6144 mUSBControllers->push_back(ctrl);
6145
6146 ctrl.queryInterfaceTo(aController.asOutParam());
6147
6148 /* inform the direct session if any */
6149 alock.release();
6150 i_onUSBControllerChange();
6151
6152 return S_OK;
6153}
6154
6155HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6156{
6157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6158
6159 ComObjPtr<USBController> ctrl;
6160
6161 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6162 if (SUCCEEDED(rc))
6163 ctrl.queryInterfaceTo(aController.asOutParam());
6164
6165 return rc;
6166}
6167
6168HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6169 ULONG *aControllers)
6170{
6171 if ( (aType <= USBControllerType_Null)
6172 || (aType >= USBControllerType_Last))
6173 return setError(E_INVALIDARG,
6174 tr("Invalid USB controller type: %d"),
6175 aType);
6176
6177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6178
6179 ComObjPtr<USBController> ctrl;
6180
6181 *aControllers = i_getUSBControllerCountByType(aType);
6182
6183 return S_OK;
6184}
6185
6186HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6187{
6188
6189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6190
6191 HRESULT rc = i_checkStateDependency(MutableStateDep);
6192 if (FAILED(rc)) return rc;
6193
6194 ComObjPtr<USBController> ctrl;
6195 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6196 if (FAILED(rc)) return rc;
6197
6198 i_setModified(IsModified_USB);
6199 mUSBControllers.backup();
6200
6201 ctrl->i_unshare();
6202
6203 mUSBControllers->remove(ctrl);
6204
6205 /* inform the direct session if any */
6206 alock.release();
6207 i_onUSBControllerChange();
6208
6209 return S_OK;
6210}
6211
6212HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6213 ULONG *aOriginX,
6214 ULONG *aOriginY,
6215 ULONG *aWidth,
6216 ULONG *aHeight,
6217 BOOL *aEnabled)
6218{
6219 uint32_t u32OriginX= 0;
6220 uint32_t u32OriginY= 0;
6221 uint32_t u32Width = 0;
6222 uint32_t u32Height = 0;
6223 uint16_t u16Flags = 0;
6224
6225 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6226 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6227 if (RT_FAILURE(vrc))
6228 {
6229#ifdef RT_OS_WINDOWS
6230 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6231 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6232 * So just assign fEnable to TRUE again.
6233 * The right fix would be to change GUI API wrappers to make sure that parameters
6234 * are changed only if API succeeds.
6235 */
6236 *aEnabled = TRUE;
6237#endif
6238 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6239 tr("Saved guest size is not available (%Rrc)"),
6240 vrc);
6241 }
6242
6243 *aOriginX = u32OriginX;
6244 *aOriginY = u32OriginY;
6245 *aWidth = u32Width;
6246 *aHeight = u32Height;
6247 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6248
6249 return S_OK;
6250}
6251
6252HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6253 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6254{
6255 if (aScreenId != 0)
6256 return E_NOTIMPL;
6257
6258 if ( aBitmapFormat != BitmapFormat_BGR0
6259 && aBitmapFormat != BitmapFormat_BGRA
6260 && aBitmapFormat != BitmapFormat_RGBA
6261 && aBitmapFormat != BitmapFormat_PNG)
6262 return setError(E_NOTIMPL,
6263 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6264
6265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6266
6267 uint8_t *pu8Data = NULL;
6268 uint32_t cbData = 0;
6269 uint32_t u32Width = 0;
6270 uint32_t u32Height = 0;
6271
6272 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6273
6274 if (RT_FAILURE(vrc))
6275 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6276 tr("Saved thumbnail data is not available (%Rrc)"),
6277 vrc);
6278
6279 HRESULT hr = S_OK;
6280
6281 *aWidth = u32Width;
6282 *aHeight = u32Height;
6283
6284 if (cbData > 0)
6285 {
6286 /* Convert pixels to the format expected by the API caller. */
6287 if (aBitmapFormat == BitmapFormat_BGR0)
6288 {
6289 /* [0] B, [1] G, [2] R, [3] 0. */
6290 aData.resize(cbData);
6291 memcpy(&aData.front(), pu8Data, cbData);
6292 }
6293 else if (aBitmapFormat == BitmapFormat_BGRA)
6294 {
6295 /* [0] B, [1] G, [2] R, [3] A. */
6296 aData.resize(cbData);
6297 for (uint32_t i = 0; i < cbData; i += 4)
6298 {
6299 aData[i] = pu8Data[i];
6300 aData[i + 1] = pu8Data[i + 1];
6301 aData[i + 2] = pu8Data[i + 2];
6302 aData[i + 3] = 0xff;
6303 }
6304 }
6305 else if (aBitmapFormat == BitmapFormat_RGBA)
6306 {
6307 /* [0] R, [1] G, [2] B, [3] A. */
6308 aData.resize(cbData);
6309 for (uint32_t i = 0; i < cbData; i += 4)
6310 {
6311 aData[i] = pu8Data[i + 2];
6312 aData[i + 1] = pu8Data[i + 1];
6313 aData[i + 2] = pu8Data[i];
6314 aData[i + 3] = 0xff;
6315 }
6316 }
6317 else if (aBitmapFormat == BitmapFormat_PNG)
6318 {
6319 uint8_t *pu8PNG = NULL;
6320 uint32_t cbPNG = 0;
6321 uint32_t cxPNG = 0;
6322 uint32_t cyPNG = 0;
6323
6324 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6325
6326 if (RT_SUCCESS(vrc))
6327 {
6328 aData.resize(cbPNG);
6329 if (cbPNG)
6330 memcpy(&aData.front(), pu8PNG, cbPNG);
6331 }
6332 else
6333 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6334 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6335 vrc);
6336
6337 RTMemFree(pu8PNG);
6338 }
6339 }
6340
6341 freeSavedDisplayScreenshot(pu8Data);
6342
6343 return hr;
6344}
6345
6346HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6347 ULONG *aWidth,
6348 ULONG *aHeight,
6349 std::vector<BitmapFormat_T> &aBitmapFormats)
6350{
6351 if (aScreenId != 0)
6352 return E_NOTIMPL;
6353
6354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6355
6356 uint8_t *pu8Data = NULL;
6357 uint32_t cbData = 0;
6358 uint32_t u32Width = 0;
6359 uint32_t u32Height = 0;
6360
6361 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6362
6363 if (RT_FAILURE(vrc))
6364 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6365 tr("Saved screenshot data is not available (%Rrc)"),
6366 vrc);
6367
6368 *aWidth = u32Width;
6369 *aHeight = u32Height;
6370 aBitmapFormats.resize(1);
6371 aBitmapFormats[0] = BitmapFormat_PNG;
6372
6373 freeSavedDisplayScreenshot(pu8Data);
6374
6375 return S_OK;
6376}
6377
6378HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6379 BitmapFormat_T aBitmapFormat,
6380 ULONG *aWidth,
6381 ULONG *aHeight,
6382 std::vector<BYTE> &aData)
6383{
6384 if (aScreenId != 0)
6385 return E_NOTIMPL;
6386
6387 if (aBitmapFormat != BitmapFormat_PNG)
6388 return E_NOTIMPL;
6389
6390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6391
6392 uint8_t *pu8Data = NULL;
6393 uint32_t cbData = 0;
6394 uint32_t u32Width = 0;
6395 uint32_t u32Height = 0;
6396
6397 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6398
6399 if (RT_FAILURE(vrc))
6400 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6401 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6402 vrc);
6403
6404 *aWidth = u32Width;
6405 *aHeight = u32Height;
6406
6407 aData.resize(cbData);
6408 if (cbData)
6409 memcpy(&aData.front(), pu8Data, cbData);
6410
6411 freeSavedDisplayScreenshot(pu8Data);
6412
6413 return S_OK;
6414}
6415
6416HRESULT Machine::hotPlugCPU(ULONG aCpu)
6417{
6418 HRESULT rc = S_OK;
6419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 if (!mHWData->mCPUHotPlugEnabled)
6422 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6423
6424 if (aCpu >= mHWData->mCPUCount)
6425 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6426
6427 if (mHWData->mCPUAttached[aCpu])
6428 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6429
6430 rc = i_checkStateDependency(MutableOrRunningStateDep);
6431 if (FAILED(rc)) return rc;
6432
6433 alock.release();
6434 rc = i_onCPUChange(aCpu, false);
6435 alock.acquire();
6436 if (FAILED(rc)) return rc;
6437
6438 i_setModified(IsModified_MachineData);
6439 mHWData.backup();
6440 mHWData->mCPUAttached[aCpu] = true;
6441
6442 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6443 if (Global::IsOnline(mData->mMachineState))
6444 i_saveSettings(NULL, alock);
6445
6446 return S_OK;
6447}
6448
6449HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6450{
6451 HRESULT rc = S_OK;
6452
6453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 if (!mHWData->mCPUHotPlugEnabled)
6456 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6457
6458 if (aCpu >= SchemaDefs::MaxCPUCount)
6459 return setError(E_INVALIDARG,
6460 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6461 SchemaDefs::MaxCPUCount);
6462
6463 if (!mHWData->mCPUAttached[aCpu])
6464 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6465
6466 /* CPU 0 can't be detached */
6467 if (aCpu == 0)
6468 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6469
6470 rc = i_checkStateDependency(MutableOrRunningStateDep);
6471 if (FAILED(rc)) return rc;
6472
6473 alock.release();
6474 rc = i_onCPUChange(aCpu, true);
6475 alock.acquire();
6476 if (FAILED(rc)) return rc;
6477
6478 i_setModified(IsModified_MachineData);
6479 mHWData.backup();
6480 mHWData->mCPUAttached[aCpu] = false;
6481
6482 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6483 if (Global::IsOnline(mData->mMachineState))
6484 i_saveSettings(NULL, alock);
6485
6486 return S_OK;
6487}
6488
6489HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6490{
6491 *aAttached = false;
6492
6493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6494
6495 /* If hotplug is enabled the CPU is always enabled. */
6496 if (!mHWData->mCPUHotPlugEnabled)
6497 {
6498 if (aCpu < mHWData->mCPUCount)
6499 *aAttached = true;
6500 }
6501 else
6502 {
6503 if (aCpu < SchemaDefs::MaxCPUCount)
6504 *aAttached = mHWData->mCPUAttached[aCpu];
6505 }
6506
6507 return S_OK;
6508}
6509
6510HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6511{
6512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6513
6514 Utf8Str log = i_getLogFilename(aIdx);
6515 if (!RTFileExists(log.c_str()))
6516 log.setNull();
6517 aFilename = log;
6518
6519 return S_OK;
6520}
6521
6522HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6523{
6524 if (aSize < 0)
6525 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6526
6527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6528
6529 HRESULT rc = S_OK;
6530 Utf8Str log = i_getLogFilename(aIdx);
6531
6532 /* do not unnecessarily hold the lock while doing something which does
6533 * not need the lock and potentially takes a long time. */
6534 alock.release();
6535
6536 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6537 * keeps the SOAP reply size under 1M for the webservice (we're using
6538 * base64 encoded strings for binary data for years now, avoiding the
6539 * expansion of each byte array element to approx. 25 bytes of XML. */
6540 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6541 aData.resize(cbData);
6542
6543 RTFILE LogFile;
6544 int vrc = RTFileOpen(&LogFile, log.c_str(),
6545 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6546 if (RT_SUCCESS(vrc))
6547 {
6548 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6549 if (RT_SUCCESS(vrc))
6550 aData.resize(cbData);
6551 else
6552 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6553 tr("Could not read log file '%s' (%Rrc)"),
6554 log.c_str(), vrc);
6555 RTFileClose(LogFile);
6556 }
6557 else
6558 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6559 tr("Could not open log file '%s' (%Rrc)"),
6560 log.c_str(), vrc);
6561
6562 if (FAILED(rc))
6563 aData.resize(0);
6564
6565 return rc;
6566}
6567
6568
6569/**
6570 * Currently this method doesn't attach device to the running VM,
6571 * just makes sure it's plugged on next VM start.
6572 */
6573HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6574{
6575 // lock scope
6576 {
6577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6578
6579 HRESULT rc = i_checkStateDependency(MutableStateDep);
6580 if (FAILED(rc)) return rc;
6581
6582 ChipsetType_T aChipset = ChipsetType_PIIX3;
6583 COMGETTER(ChipsetType)(&aChipset);
6584
6585 if (aChipset != ChipsetType_ICH9)
6586 {
6587 return setError(E_INVALIDARG,
6588 tr("Host PCI attachment only supported with ICH9 chipset"));
6589 }
6590
6591 // check if device with this host PCI address already attached
6592 for (HWData::PCIDeviceAssignmentList::const_iterator
6593 it = mHWData->mPCIDeviceAssignments.begin();
6594 it != mHWData->mPCIDeviceAssignments.end();
6595 ++it)
6596 {
6597 LONG iHostAddress = -1;
6598 ComPtr<PCIDeviceAttachment> pAttach;
6599 pAttach = *it;
6600 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6601 if (iHostAddress == aHostAddress)
6602 return setError(E_INVALIDARG,
6603 tr("Device with host PCI address already attached to this VM"));
6604 }
6605
6606 ComObjPtr<PCIDeviceAttachment> pda;
6607 char name[32];
6608
6609 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6610 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6611 pda.createObject();
6612 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6613 i_setModified(IsModified_MachineData);
6614 mHWData.backup();
6615 mHWData->mPCIDeviceAssignments.push_back(pda);
6616 }
6617
6618 return S_OK;
6619}
6620
6621/**
6622 * Currently this method doesn't detach device from the running VM,
6623 * just makes sure it's not plugged on next VM start.
6624 */
6625HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6626{
6627 ComObjPtr<PCIDeviceAttachment> pAttach;
6628 bool fRemoved = false;
6629 HRESULT rc;
6630
6631 // lock scope
6632 {
6633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 rc = i_checkStateDependency(MutableStateDep);
6636 if (FAILED(rc)) return rc;
6637
6638 for (HWData::PCIDeviceAssignmentList::const_iterator
6639 it = mHWData->mPCIDeviceAssignments.begin();
6640 it != mHWData->mPCIDeviceAssignments.end();
6641 ++it)
6642 {
6643 LONG iHostAddress = -1;
6644 pAttach = *it;
6645 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6646 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6647 {
6648 i_setModified(IsModified_MachineData);
6649 mHWData.backup();
6650 mHWData->mPCIDeviceAssignments.remove(pAttach);
6651 fRemoved = true;
6652 break;
6653 }
6654 }
6655 }
6656
6657
6658 /* Fire event outside of the lock */
6659 if (fRemoved)
6660 {
6661 Assert(!pAttach.isNull());
6662 ComPtr<IEventSource> es;
6663 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6664 Assert(SUCCEEDED(rc));
6665 Bstr mid;
6666 rc = this->COMGETTER(Id)(mid.asOutParam());
6667 Assert(SUCCEEDED(rc));
6668 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6669 }
6670
6671 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6672 tr("No host PCI device %08x attached"),
6673 aHostAddress
6674 );
6675}
6676
6677HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6678{
6679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6680
6681 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6682 size_t i = 0;
6683 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6684 it = mHWData->mPCIDeviceAssignments.begin();
6685 it != mHWData->mPCIDeviceAssignments.end();
6686 ++it, ++i)
6687 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6688
6689 return S_OK;
6690}
6691
6692HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6693{
6694 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6695
6696 return S_OK;
6697}
6698
6699HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6700{
6701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6702
6703 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6704
6705 return S_OK;
6706}
6707
6708HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6709{
6710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6711 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6712 if (SUCCEEDED(hrc))
6713 {
6714 hrc = mHWData.backupEx();
6715 if (SUCCEEDED(hrc))
6716 {
6717 i_setModified(IsModified_MachineData);
6718 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6719 }
6720 }
6721 return hrc;
6722}
6723
6724HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6725{
6726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6727 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6728 return S_OK;
6729}
6730
6731HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6732{
6733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6734 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6735 if (SUCCEEDED(hrc))
6736 {
6737 hrc = mHWData.backupEx();
6738 if (SUCCEEDED(hrc))
6739 {
6740 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6741 if (SUCCEEDED(hrc))
6742 i_setModified(IsModified_MachineData);
6743 }
6744 }
6745 return hrc;
6746}
6747
6748HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6749{
6750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6751
6752 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6753
6754 return S_OK;
6755}
6756
6757HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6758{
6759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6760 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6761 if (SUCCEEDED(hrc))
6762 {
6763 hrc = mHWData.backupEx();
6764 if (SUCCEEDED(hrc))
6765 {
6766 i_setModified(IsModified_MachineData);
6767 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6768 }
6769 }
6770 return hrc;
6771}
6772
6773HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6774{
6775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6778
6779 return S_OK;
6780}
6781
6782HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6783{
6784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6785
6786 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6787 if ( SUCCEEDED(hrc)
6788 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6789 {
6790 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6791 int vrc;
6792
6793 if (aAutostartEnabled)
6794 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6795 else
6796 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6797
6798 if (RT_SUCCESS(vrc))
6799 {
6800 hrc = mHWData.backupEx();
6801 if (SUCCEEDED(hrc))
6802 {
6803 i_setModified(IsModified_MachineData);
6804 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6805 }
6806 }
6807 else if (vrc == VERR_NOT_SUPPORTED)
6808 hrc = setError(VBOX_E_NOT_SUPPORTED,
6809 tr("The VM autostart feature is not supported on this platform"));
6810 else if (vrc == VERR_PATH_NOT_FOUND)
6811 hrc = setError(E_FAIL,
6812 tr("The path to the autostart database is not set"));
6813 else
6814 hrc = setError(E_UNEXPECTED,
6815 aAutostartEnabled ?
6816 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6817 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6818 mUserData->s.strName.c_str(), vrc);
6819 }
6820 return hrc;
6821}
6822
6823HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6824{
6825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6826
6827 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6828
6829 return S_OK;
6830}
6831
6832HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6833{
6834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6835 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6836 if (SUCCEEDED(hrc))
6837 {
6838 hrc = mHWData.backupEx();
6839 if (SUCCEEDED(hrc))
6840 {
6841 i_setModified(IsModified_MachineData);
6842 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6843 }
6844 }
6845 return hrc;
6846}
6847
6848HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6849{
6850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6851
6852 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6853
6854 return S_OK;
6855}
6856
6857HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6858{
6859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6860 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6861 if ( SUCCEEDED(hrc)
6862 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6863 {
6864 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6865 int vrc;
6866
6867 if (aAutostopType != AutostopType_Disabled)
6868 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6869 else
6870 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6871
6872 if (RT_SUCCESS(vrc))
6873 {
6874 hrc = mHWData.backupEx();
6875 if (SUCCEEDED(hrc))
6876 {
6877 i_setModified(IsModified_MachineData);
6878 mHWData->mAutostart.enmAutostopType = aAutostopType;
6879 }
6880 }
6881 else if (vrc == VERR_NOT_SUPPORTED)
6882 hrc = setError(VBOX_E_NOT_SUPPORTED,
6883 tr("The VM autostop feature is not supported on this platform"));
6884 else if (vrc == VERR_PATH_NOT_FOUND)
6885 hrc = setError(E_FAIL,
6886 tr("The path to the autostart database is not set"));
6887 else
6888 hrc = setError(E_UNEXPECTED,
6889 aAutostopType != AutostopType_Disabled ?
6890 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6891 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6892 mUserData->s.strName.c_str(), vrc);
6893 }
6894 return hrc;
6895}
6896
6897HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6898{
6899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6900
6901 aDefaultFrontend = mHWData->mDefaultFrontend;
6902
6903 return S_OK;
6904}
6905
6906HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6907{
6908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6909 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6910 if (SUCCEEDED(hrc))
6911 {
6912 hrc = mHWData.backupEx();
6913 if (SUCCEEDED(hrc))
6914 {
6915 i_setModified(IsModified_MachineData);
6916 mHWData->mDefaultFrontend = aDefaultFrontend;
6917 }
6918 }
6919 return hrc;
6920}
6921
6922HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6923{
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925 size_t cbIcon = mUserData->s.ovIcon.size();
6926 aIcon.resize(cbIcon);
6927 if (cbIcon)
6928 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6929 return S_OK;
6930}
6931
6932HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6933{
6934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6935 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6936 if (SUCCEEDED(hrc))
6937 {
6938 i_setModified(IsModified_MachineData);
6939 mUserData.backup();
6940 size_t cbIcon = aIcon.size();
6941 mUserData->s.ovIcon.resize(cbIcon);
6942 if (cbIcon)
6943 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6944 }
6945 return hrc;
6946}
6947
6948HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6949{
6950#ifdef VBOX_WITH_USB
6951 *aUSBProxyAvailable = true;
6952#else
6953 *aUSBProxyAvailable = false;
6954#endif
6955 return S_OK;
6956}
6957
6958HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6959{
6960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6961
6962 *aVMProcessPriority = mUserData->s.enmVMPriority;
6963
6964 return S_OK;
6965}
6966
6967HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6968{
6969 RT_NOREF(aVMProcessPriority);
6970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6971 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6972 if (SUCCEEDED(hrc))
6973 {
6974 hrc = mUserData.backupEx();
6975 if (SUCCEEDED(hrc))
6976 {
6977 i_setModified(IsModified_MachineData);
6978 mUserData->s.enmVMPriority = aVMProcessPriority;
6979 }
6980 }
6981 alock.release();
6982 if (SUCCEEDED(hrc))
6983 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6984 return hrc;
6985}
6986
6987HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6988 ComPtr<IProgress> &aProgress)
6989{
6990 ComObjPtr<Progress> pP;
6991 Progress *ppP = pP;
6992 IProgress *iP = static_cast<IProgress *>(ppP);
6993 IProgress **pProgress = &iP;
6994
6995 IMachine *pTarget = aTarget;
6996
6997 /* Convert the options. */
6998 RTCList<CloneOptions_T> optList;
6999 if (aOptions.size())
7000 for (size_t i = 0; i < aOptions.size(); ++i)
7001 optList.append(aOptions[i]);
7002
7003 if (optList.contains(CloneOptions_Link))
7004 {
7005 if (!i_isSnapshotMachine())
7006 return setError(E_INVALIDARG,
7007 tr("Linked clone can only be created from a snapshot"));
7008 if (aMode != CloneMode_MachineState)
7009 return setError(E_INVALIDARG,
7010 tr("Linked clone can only be created for a single machine state"));
7011 }
7012 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7013
7014 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7015
7016 HRESULT rc = pWorker->start(pProgress);
7017
7018 pP = static_cast<Progress *>(*pProgress);
7019 pP.queryInterfaceTo(aProgress.asOutParam());
7020
7021 return rc;
7022
7023}
7024
7025HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7026 const com::Utf8Str &aType,
7027 ComPtr<IProgress> &aProgress)
7028{
7029 LogFlowThisFuncEnter();
7030
7031 ComObjPtr<Progress> ptrProgress;
7032 HRESULT hrc = ptrProgress.createObject();
7033 if (SUCCEEDED(hrc))
7034 {
7035 com::Utf8Str strDefaultPath;
7036 if (aTargetPath.isEmpty())
7037 i_calculateFullPath(".", strDefaultPath);
7038
7039 /* Initialize our worker task */
7040 MachineMoveVM *pTask = NULL;
7041 try
7042 {
7043 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7044 }
7045 catch (std::bad_alloc &)
7046 {
7047 return E_OUTOFMEMORY;
7048 }
7049
7050 hrc = pTask->init();//no exceptions are thrown
7051
7052 if (SUCCEEDED(hrc))
7053 {
7054 hrc = pTask->createThread();
7055 pTask = NULL; /* Consumed by createThread(). */
7056 if (SUCCEEDED(hrc))
7057 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7058 else
7059 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7060 }
7061 else
7062 delete pTask;
7063 }
7064
7065 LogFlowThisFuncLeave();
7066 return hrc;
7067
7068}
7069
7070HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7071{
7072 NOREF(aProgress);
7073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7074
7075 // This check should always fail.
7076 HRESULT rc = i_checkStateDependency(MutableStateDep);
7077 if (FAILED(rc)) return rc;
7078
7079 AssertFailedReturn(E_NOTIMPL);
7080}
7081
7082HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7083{
7084 NOREF(aSavedStateFile);
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 // This check should always fail.
7088 HRESULT rc = i_checkStateDependency(MutableStateDep);
7089 if (FAILED(rc)) return rc;
7090
7091 AssertFailedReturn(E_NOTIMPL);
7092}
7093
7094HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7095{
7096 NOREF(aFRemoveFile);
7097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7098
7099 // This check should always fail.
7100 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7101 if (FAILED(rc)) return rc;
7102
7103 AssertFailedReturn(E_NOTIMPL);
7104}
7105
7106// public methods for internal purposes
7107/////////////////////////////////////////////////////////////////////////////
7108
7109/**
7110 * Adds the given IsModified_* flag to the dirty flags of the machine.
7111 * This must be called either during i_loadSettings or under the machine write lock.
7112 * @param fl Flag
7113 * @param fAllowStateModification If state modifications are allowed.
7114 */
7115void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7116{
7117 mData->flModifications |= fl;
7118 if (fAllowStateModification && i_isStateModificationAllowed())
7119 mData->mCurrentStateModified = true;
7120}
7121
7122/**
7123 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7124 * care of the write locking.
7125 *
7126 * @param fModification The flag to add.
7127 * @param fAllowStateModification If state modifications are allowed.
7128 */
7129void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7130{
7131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7132 i_setModified(fModification, fAllowStateModification);
7133}
7134
7135/**
7136 * Saves the registry entry of this machine to the given configuration node.
7137 *
7138 * @param data Machine registry data.
7139 *
7140 * @note locks this object for reading.
7141 */
7142HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7143{
7144 AutoLimitedCaller autoCaller(this);
7145 AssertComRCReturnRC(autoCaller.rc());
7146
7147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7148
7149 data.uuid = mData->mUuid;
7150 data.strSettingsFile = mData->m_strConfigFile;
7151
7152 return S_OK;
7153}
7154
7155/**
7156 * Calculates the absolute path of the given path taking the directory of the
7157 * machine settings file as the current directory.
7158 *
7159 * @param strPath Path to calculate the absolute path for.
7160 * @param aResult Where to put the result (used only on success, can be the
7161 * same Utf8Str instance as passed in @a aPath).
7162 * @return IPRT result.
7163 *
7164 * @note Locks this object for reading.
7165 */
7166int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7167{
7168 AutoCaller autoCaller(this);
7169 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7170
7171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7172
7173 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7174
7175 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7176
7177 strSettingsDir.stripFilename();
7178 char szFolder[RTPATH_MAX];
7179 size_t cbFolder = sizeof(szFolder);
7180 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7181 if (RT_SUCCESS(vrc))
7182 aResult = szFolder;
7183
7184 return vrc;
7185}
7186
7187/**
7188 * Copies strSource to strTarget, making it relative to the machine folder
7189 * if it is a subdirectory thereof, or simply copying it otherwise.
7190 *
7191 * @param strSource Path to evaluate and copy.
7192 * @param strTarget Buffer to receive target path.
7193 *
7194 * @note Locks this object for reading.
7195 */
7196void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7197 Utf8Str &strTarget)
7198{
7199 AutoCaller autoCaller(this);
7200 AssertComRCReturn(autoCaller.rc(), (void)0);
7201
7202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7203
7204 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7205 // use strTarget as a temporary buffer to hold the machine settings dir
7206 strTarget = mData->m_strConfigFileFull;
7207 strTarget.stripFilename();
7208 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7209 {
7210 // is relative: then append what's left
7211 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7212 // for empty paths (only possible for subdirs) use "." to avoid
7213 // triggering default settings for not present config attributes.
7214 if (strTarget.isEmpty())
7215 strTarget = ".";
7216 }
7217 else
7218 // is not relative: then overwrite
7219 strTarget = strSource;
7220}
7221
7222/**
7223 * Returns the full path to the machine's log folder in the
7224 * \a aLogFolder argument.
7225 */
7226void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7227{
7228 AutoCaller autoCaller(this);
7229 AssertComRCReturnVoid(autoCaller.rc());
7230
7231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7232
7233 char szTmp[RTPATH_MAX];
7234 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7235 if (RT_SUCCESS(vrc))
7236 {
7237 if (szTmp[0] && !mUserData.isNull())
7238 {
7239 char szTmp2[RTPATH_MAX];
7240 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7241 if (RT_SUCCESS(vrc))
7242 aLogFolder.printf("%s%c%s",
7243 szTmp2,
7244 RTPATH_DELIMITER,
7245 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7246 }
7247 else
7248 vrc = VERR_PATH_IS_RELATIVE;
7249 }
7250
7251 if (RT_FAILURE(vrc))
7252 {
7253 // fallback if VBOX_USER_LOGHOME is not set or invalid
7254 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7255 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7256 aLogFolder.append(RTPATH_DELIMITER);
7257 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7258 }
7259}
7260
7261/**
7262 * Returns the full path to the machine's log file for an given index.
7263 */
7264Utf8Str Machine::i_getLogFilename(ULONG idx)
7265{
7266 Utf8Str logFolder;
7267 getLogFolder(logFolder);
7268 Assert(logFolder.length());
7269
7270 Utf8Str log;
7271 if (idx == 0)
7272 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7273#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7274 else if (idx == 1)
7275 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7276 else
7277 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7278#else
7279 else
7280 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7281#endif
7282 return log;
7283}
7284
7285/**
7286 * Returns the full path to the machine's hardened log file.
7287 */
7288Utf8Str Machine::i_getHardeningLogFilename(void)
7289{
7290 Utf8Str strFilename;
7291 getLogFolder(strFilename);
7292 Assert(strFilename.length());
7293 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7294 return strFilename;
7295}
7296
7297/**
7298 * Returns the default NVRAM filename based on the location of the VM config.
7299 * Note that this is a relative path.
7300 */
7301Utf8Str Machine::i_getDefaultNVRAMFilename()
7302{
7303 AutoCaller autoCaller(this);
7304 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7305
7306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7307
7308 if (i_isSnapshotMachine())
7309 return Utf8Str::Empty;
7310
7311 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7312 strNVRAMFilePath.stripPath();
7313 strNVRAMFilePath.stripSuffix();
7314 strNVRAMFilePath += ".nvram";
7315
7316 return strNVRAMFilePath;
7317}
7318
7319/**
7320 * Returns the NVRAM filename for a new snapshot. This intentionally works
7321 * similarly to the saved state file naming. Note that this is usually
7322 * a relative path, unless the snapshot folder is absolute.
7323 */
7324Utf8Str Machine::i_getSnapshotNVRAMFilename()
7325{
7326 AutoCaller autoCaller(this);
7327 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7328
7329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7330
7331 RTTIMESPEC ts;
7332 RTTimeNow(&ts);
7333 RTTIME time;
7334 RTTimeExplode(&time, &ts);
7335
7336 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7337 strNVRAMFilePath += RTPATH_DELIMITER;
7338 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7339 time.i32Year, time.u8Month, time.u8MonthDay,
7340 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7341
7342 return strNVRAMFilePath;
7343}
7344
7345/**
7346 * Returns the version of the settings file.
7347 */
7348SettingsVersion_T Machine::i_getSettingsVersion(void)
7349{
7350 AutoCaller autoCaller(this);
7351 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7352
7353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7354
7355 return mData->pMachineConfigFile->getSettingsVersion();
7356}
7357
7358/**
7359 * Composes a unique saved state filename based on the current system time. The filename is
7360 * granular to the second so this will work so long as no more than one snapshot is taken on
7361 * a machine per second.
7362 *
7363 * Before version 4.1, we used this formula for saved state files:
7364 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7365 * which no longer works because saved state files can now be shared between the saved state of the
7366 * "saved" machine and an online snapshot, and the following would cause problems:
7367 * 1) save machine
7368 * 2) create online snapshot from that machine state --> reusing saved state file
7369 * 3) save machine again --> filename would be reused, breaking the online snapshot
7370 *
7371 * So instead we now use a timestamp.
7372 *
7373 * @param strStateFilePath
7374 */
7375
7376void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7377{
7378 AutoCaller autoCaller(this);
7379 AssertComRCReturnVoid(autoCaller.rc());
7380
7381 {
7382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7383 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7384 }
7385
7386 RTTIMESPEC ts;
7387 RTTimeNow(&ts);
7388 RTTIME time;
7389 RTTimeExplode(&time, &ts);
7390
7391 strStateFilePath += RTPATH_DELIMITER;
7392 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7393 time.i32Year, time.u8Month, time.u8MonthDay,
7394 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7395}
7396
7397/**
7398 * Returns whether at least one USB controller is present for the VM.
7399 */
7400bool Machine::i_isUSBControllerPresent()
7401{
7402 AutoCaller autoCaller(this);
7403 AssertComRCReturn(autoCaller.rc(), false);
7404
7405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7406
7407 return (mUSBControllers->size() > 0);
7408}
7409
7410
7411/**
7412 * @note Locks this object for writing, calls the client process
7413 * (inside the lock).
7414 */
7415HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7416 const Utf8Str &strFrontend,
7417 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7418 ProgressProxy *aProgress)
7419{
7420 LogFlowThisFuncEnter();
7421
7422 AssertReturn(aControl, E_FAIL);
7423 AssertReturn(aProgress, E_FAIL);
7424 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7425
7426 AutoCaller autoCaller(this);
7427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7428
7429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7430
7431 if (!mData->mRegistered)
7432 return setError(E_UNEXPECTED,
7433 tr("The machine '%s' is not registered"),
7434 mUserData->s.strName.c_str());
7435
7436 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7437
7438 /* The process started when launching a VM with separate UI/VM processes is always
7439 * the UI process, i.e. needs special handling as it won't claim the session. */
7440 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7441
7442 if (fSeparate)
7443 {
7444 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7445 return setError(VBOX_E_INVALID_OBJECT_STATE,
7446 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7447 mUserData->s.strName.c_str());
7448 }
7449 else
7450 {
7451 if ( mData->mSession.mState == SessionState_Locked
7452 || mData->mSession.mState == SessionState_Spawning
7453 || mData->mSession.mState == SessionState_Unlocking)
7454 return setError(VBOX_E_INVALID_OBJECT_STATE,
7455 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7456 mUserData->s.strName.c_str());
7457
7458 /* may not be busy */
7459 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7460 }
7461
7462 /* Hardening logging */
7463#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7464 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7465 {
7466 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7467 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7468 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7469 {
7470 Utf8Str strStartupLogDir = strHardeningLogFile;
7471 strStartupLogDir.stripFilename();
7472 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7473 file without stripping the file. */
7474 }
7475 strSupHardeningLogArg.append(strHardeningLogFile);
7476
7477 /* Remove legacy log filename to avoid confusion. */
7478 Utf8Str strOldStartupLogFile;
7479 getLogFolder(strOldStartupLogFile);
7480 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7481 RTFileDelete(strOldStartupLogFile.c_str());
7482 }
7483#else
7484 Utf8Str strSupHardeningLogArg;
7485#endif
7486
7487 Utf8Str strAppOverride;
7488#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7489 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7490#endif
7491
7492 bool fUseVBoxSDS = false;
7493 Utf8Str strCanonicalName;
7494 if (false)
7495 { }
7496#ifdef VBOX_WITH_QTGUI
7497 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7498 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7499 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7500 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7501 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7502 {
7503 strCanonicalName = "GUI/Qt";
7504 fUseVBoxSDS = true;
7505 }
7506#endif
7507#ifdef VBOX_WITH_VBOXSDL
7508 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7509 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7510 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7511 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7512 {
7513 strCanonicalName = "GUI/SDL";
7514 fUseVBoxSDS = true;
7515 }
7516#endif
7517#ifdef VBOX_WITH_HEADLESS
7518 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7519 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7520 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7521 {
7522 strCanonicalName = "headless";
7523 }
7524#endif
7525 else
7526 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7527
7528 Utf8Str idStr = mData->mUuid.toString();
7529 Utf8Str const &strMachineName = mUserData->s.strName;
7530 RTPROCESS pid = NIL_RTPROCESS;
7531
7532#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7533 RT_NOREF(fUseVBoxSDS);
7534#else
7535 DWORD idCallerSession = ~(DWORD)0;
7536 if (fUseVBoxSDS)
7537 {
7538 /*
7539 * The VBoxSDS should be used for process launching the VM with
7540 * GUI only if the caller and the VBoxSDS are in different Windows
7541 * sessions and the caller in the interactive one.
7542 */
7543 fUseVBoxSDS = false;
7544
7545 /* Get windows session of the current process. The process token used
7546 due to several reasons:
7547 1. The token is absent for the current thread except someone set it
7548 for us.
7549 2. Needs to get the id of the session where the process is started.
7550 We only need to do this once, though. */
7551 static DWORD s_idCurrentSession = ~(DWORD)0;
7552 DWORD idCurrentSession = s_idCurrentSession;
7553 if (idCurrentSession == ~(DWORD)0)
7554 {
7555 HANDLE hCurrentProcessToken = NULL;
7556 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7557 {
7558 DWORD cbIgn = 0;
7559 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7560 s_idCurrentSession = idCurrentSession;
7561 else
7562 {
7563 idCurrentSession = ~(DWORD)0;
7564 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7565 }
7566 CloseHandle(hCurrentProcessToken);
7567 }
7568 else
7569 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7570 }
7571
7572 /* get the caller's session */
7573 HRESULT hrc = CoImpersonateClient();
7574 if (SUCCEEDED(hrc))
7575 {
7576 HANDLE hCallerThreadToken;
7577 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7578 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7579 &hCallerThreadToken))
7580 {
7581 SetLastError(NO_ERROR);
7582 DWORD cbIgn = 0;
7583 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7584 {
7585 /* Only need to use SDS if the session ID differs: */
7586 if (idCurrentSession != idCallerSession)
7587 {
7588 fUseVBoxSDS = false;
7589
7590 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7591 DWORD cbTokenGroups = 0;
7592 PTOKEN_GROUPS pTokenGroups = NULL;
7593 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7594 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7595 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7596 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7597 {
7598 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7599 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7600 PSID pInteractiveSid = NULL;
7601 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7602 {
7603 /* Iterate over the groups looking for the interactive SID: */
7604 fUseVBoxSDS = false;
7605 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7606 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7607 {
7608 fUseVBoxSDS = true;
7609 break;
7610 }
7611 FreeSid(pInteractiveSid);
7612 }
7613 }
7614 else
7615 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7616 RTMemTmpFree(pTokenGroups);
7617 }
7618 }
7619 else
7620 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7621 CloseHandle(hCallerThreadToken);
7622 }
7623 else
7624 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7625 CoRevertToSelf();
7626 }
7627 else
7628 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7629 }
7630 if (fUseVBoxSDS)
7631 {
7632 /* connect to VBoxSDS */
7633 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7634 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7635 if (FAILED(rc))
7636 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7637 strMachineName.c_str());
7638
7639 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7640 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7641 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7642 service to access the files. */
7643 rc = CoSetProxyBlanket(pVBoxSDS,
7644 RPC_C_AUTHN_DEFAULT,
7645 RPC_C_AUTHZ_DEFAULT,
7646 COLE_DEFAULT_PRINCIPAL,
7647 RPC_C_AUTHN_LEVEL_DEFAULT,
7648 RPC_C_IMP_LEVEL_IMPERSONATE,
7649 NULL,
7650 EOAC_DEFAULT);
7651 if (FAILED(rc))
7652 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7653
7654 size_t const cEnvVars = aEnvironmentChanges.size();
7655 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7656 for (size_t i = 0; i < cEnvVars; i++)
7657 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7658
7659 ULONG uPid = 0;
7660 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7661 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7662 idCallerSession, &uPid);
7663 if (FAILED(rc))
7664 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7665 pid = (RTPROCESS)uPid;
7666 }
7667 else
7668#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7669 {
7670 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7671 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7672 if (RT_FAILURE(vrc))
7673 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7674 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7675 }
7676
7677 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7678 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7679
7680 if (!fSeparate)
7681 {
7682 /*
7683 * Note that we don't release the lock here before calling the client,
7684 * because it doesn't need to call us back if called with a NULL argument.
7685 * Releasing the lock here is dangerous because we didn't prepare the
7686 * launch data yet, but the client we've just started may happen to be
7687 * too fast and call LockMachine() that will fail (because of PID, etc.),
7688 * so that the Machine will never get out of the Spawning session state.
7689 */
7690
7691 /* inform the session that it will be a remote one */
7692 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7693#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7694 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7695#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7696 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7697#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7698 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7699
7700 if (FAILED(rc))
7701 {
7702 /* restore the session state */
7703 mData->mSession.mState = SessionState_Unlocked;
7704 alock.release();
7705 mParent->i_addProcessToReap(pid);
7706 /* The failure may occur w/o any error info (from RPC), so provide one */
7707 return setError(VBOX_E_VM_ERROR,
7708 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7709 }
7710
7711 /* attach launch data to the machine */
7712 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7713 mData->mSession.mRemoteControls.push_back(aControl);
7714 mData->mSession.mProgress = aProgress;
7715 mData->mSession.mPID = pid;
7716 mData->mSession.mState = SessionState_Spawning;
7717 Assert(strCanonicalName.isNotEmpty());
7718 mData->mSession.mName = strCanonicalName;
7719 }
7720 else
7721 {
7722 /* For separate UI process we declare the launch as completed instantly, as the
7723 * actual headless VM start may or may not come. No point in remembering anything
7724 * yet, as what matters for us is when the headless VM gets started. */
7725 aProgress->i_notifyComplete(S_OK);
7726 }
7727
7728 alock.release();
7729 mParent->i_addProcessToReap(pid);
7730
7731 LogFlowThisFuncLeave();
7732 return S_OK;
7733}
7734
7735/**
7736 * Returns @c true if the given session machine instance has an open direct
7737 * session (and optionally also for direct sessions which are closing) and
7738 * returns the session control machine instance if so.
7739 *
7740 * Note that when the method returns @c false, the arguments remain unchanged.
7741 *
7742 * @param aMachine Session machine object.
7743 * @param aControl Direct session control object (optional).
7744 * @param aRequireVM If true then only allow VM sessions.
7745 * @param aAllowClosing If true then additionally a session which is currently
7746 * being closed will also be allowed.
7747 *
7748 * @note locks this object for reading.
7749 */
7750bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7751 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7752 bool aRequireVM /*= false*/,
7753 bool aAllowClosing /*= false*/)
7754{
7755 AutoLimitedCaller autoCaller(this);
7756 AssertComRCReturn(autoCaller.rc(), false);
7757
7758 /* just return false for inaccessible machines */
7759 if (getObjectState().getState() != ObjectState::Ready)
7760 return false;
7761
7762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7763
7764 if ( ( mData->mSession.mState == SessionState_Locked
7765 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7766 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7767 )
7768 {
7769 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7770
7771 aMachine = mData->mSession.mMachine;
7772
7773 if (aControl != NULL)
7774 *aControl = mData->mSession.mDirectControl;
7775
7776 return true;
7777 }
7778
7779 return false;
7780}
7781
7782/**
7783 * Returns @c true if the given machine has an spawning direct session.
7784 *
7785 * @note locks this object for reading.
7786 */
7787bool Machine::i_isSessionSpawning()
7788{
7789 AutoLimitedCaller autoCaller(this);
7790 AssertComRCReturn(autoCaller.rc(), false);
7791
7792 /* just return false for inaccessible machines */
7793 if (getObjectState().getState() != ObjectState::Ready)
7794 return false;
7795
7796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 if (mData->mSession.mState == SessionState_Spawning)
7799 return true;
7800
7801 return false;
7802}
7803
7804/**
7805 * Called from the client watcher thread to check for unexpected client process
7806 * death during Session_Spawning state (e.g. before it successfully opened a
7807 * direct session).
7808 *
7809 * On Win32 and on OS/2, this method is called only when we've got the
7810 * direct client's process termination notification, so it always returns @c
7811 * true.
7812 *
7813 * On other platforms, this method returns @c true if the client process is
7814 * terminated and @c false if it's still alive.
7815 *
7816 * @note Locks this object for writing.
7817 */
7818bool Machine::i_checkForSpawnFailure()
7819{
7820 AutoCaller autoCaller(this);
7821 if (!autoCaller.isOk())
7822 {
7823 /* nothing to do */
7824 LogFlowThisFunc(("Already uninitialized!\n"));
7825 return true;
7826 }
7827
7828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7829
7830 if (mData->mSession.mState != SessionState_Spawning)
7831 {
7832 /* nothing to do */
7833 LogFlowThisFunc(("Not spawning any more!\n"));
7834 return true;
7835 }
7836
7837 HRESULT rc = S_OK;
7838
7839 /* PID not yet initialized, skip check. */
7840 if (mData->mSession.mPID == NIL_RTPROCESS)
7841 return false;
7842
7843 RTPROCSTATUS status;
7844 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7845
7846 if (vrc != VERR_PROCESS_RUNNING)
7847 {
7848 Utf8Str strExtraInfo;
7849
7850#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7851 /* If the startup logfile exists and is of non-zero length, tell the
7852 user to look there for more details to encourage them to attach it
7853 when reporting startup issues. */
7854 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7855 uint64_t cbStartupLogFile = 0;
7856 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7857 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7858 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7859#endif
7860
7861 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7862 rc = setError(E_FAIL,
7863 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7864 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7865 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7866 rc = setError(E_FAIL,
7867 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7868 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7869 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7870 rc = setError(E_FAIL,
7871 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7872 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7873 else
7874 rc = setErrorBoth(E_FAIL, vrc,
7875 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7876 i_getName().c_str(), vrc, strExtraInfo.c_str());
7877 }
7878
7879 if (FAILED(rc))
7880 {
7881 /* Close the remote session, remove the remote control from the list
7882 * and reset session state to Closed (@note keep the code in sync with
7883 * the relevant part in LockMachine()). */
7884
7885 Assert(mData->mSession.mRemoteControls.size() == 1);
7886 if (mData->mSession.mRemoteControls.size() == 1)
7887 {
7888 ErrorInfoKeeper eik;
7889 mData->mSession.mRemoteControls.front()->Uninitialize();
7890 }
7891
7892 mData->mSession.mRemoteControls.clear();
7893 mData->mSession.mState = SessionState_Unlocked;
7894
7895 /* finalize the progress after setting the state */
7896 if (!mData->mSession.mProgress.isNull())
7897 {
7898 mData->mSession.mProgress->notifyComplete(rc);
7899 mData->mSession.mProgress.setNull();
7900 }
7901
7902 mData->mSession.mPID = NIL_RTPROCESS;
7903
7904 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7905 return true;
7906 }
7907
7908 return false;
7909}
7910
7911/**
7912 * Checks whether the machine can be registered. If so, commits and saves
7913 * all settings.
7914 *
7915 * @note Must be called from mParent's write lock. Locks this object and
7916 * children for writing.
7917 */
7918HRESULT Machine::i_prepareRegister()
7919{
7920 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7921
7922 AutoLimitedCaller autoCaller(this);
7923 AssertComRCReturnRC(autoCaller.rc());
7924
7925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7926
7927 /* wait for state dependents to drop to zero */
7928 i_ensureNoStateDependencies(alock);
7929
7930 if (!mData->mAccessible)
7931 return setError(VBOX_E_INVALID_OBJECT_STATE,
7932 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7933 mUserData->s.strName.c_str(),
7934 mData->mUuid.toString().c_str());
7935
7936 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7937
7938 if (mData->mRegistered)
7939 return setError(VBOX_E_INVALID_OBJECT_STATE,
7940 tr("The machine '%s' with UUID {%s} is already registered"),
7941 mUserData->s.strName.c_str(),
7942 mData->mUuid.toString().c_str());
7943
7944 HRESULT rc = S_OK;
7945
7946 // Ensure the settings are saved. If we are going to be registered and
7947 // no config file exists yet, create it by calling i_saveSettings() too.
7948 if ( (mData->flModifications)
7949 || (!mData->pMachineConfigFile->fileExists())
7950 )
7951 {
7952 rc = i_saveSettings(NULL, alock);
7953 // no need to check whether VirtualBox.xml needs saving too since
7954 // we can't have a machine XML file rename pending
7955 if (FAILED(rc)) return rc;
7956 }
7957
7958 /* more config checking goes here */
7959
7960 if (SUCCEEDED(rc))
7961 {
7962 /* we may have had implicit modifications we want to fix on success */
7963 i_commit();
7964
7965 mData->mRegistered = true;
7966 }
7967 else
7968 {
7969 /* we may have had implicit modifications we want to cancel on failure*/
7970 i_rollback(false /* aNotify */);
7971 }
7972
7973 return rc;
7974}
7975
7976/**
7977 * Increases the number of objects dependent on the machine state or on the
7978 * registered state. Guarantees that these two states will not change at least
7979 * until #i_releaseStateDependency() is called.
7980 *
7981 * Depending on the @a aDepType value, additional state checks may be made.
7982 * These checks will set extended error info on failure. See
7983 * #i_checkStateDependency() for more info.
7984 *
7985 * If this method returns a failure, the dependency is not added and the caller
7986 * is not allowed to rely on any particular machine state or registration state
7987 * value and may return the failed result code to the upper level.
7988 *
7989 * @param aDepType Dependency type to add.
7990 * @param aState Current machine state (NULL if not interested).
7991 * @param aRegistered Current registered state (NULL if not interested).
7992 *
7993 * @note Locks this object for writing.
7994 */
7995HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7996 MachineState_T *aState /* = NULL */,
7997 BOOL *aRegistered /* = NULL */)
7998{
7999 AutoCaller autoCaller(this);
8000 AssertComRCReturnRC(autoCaller.rc());
8001
8002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8003
8004 HRESULT rc = i_checkStateDependency(aDepType);
8005 if (FAILED(rc)) return rc;
8006
8007 {
8008 if (mData->mMachineStateChangePending != 0)
8009 {
8010 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8011 * drop to zero so don't add more. It may make sense to wait a bit
8012 * and retry before reporting an error (since the pending state
8013 * transition should be really quick) but let's just assert for
8014 * now to see if it ever happens on practice. */
8015
8016 AssertFailed();
8017
8018 return setError(E_ACCESSDENIED,
8019 tr("Machine state change is in progress. Please retry the operation later."));
8020 }
8021
8022 ++mData->mMachineStateDeps;
8023 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8024 }
8025
8026 if (aState)
8027 *aState = mData->mMachineState;
8028 if (aRegistered)
8029 *aRegistered = mData->mRegistered;
8030
8031 return S_OK;
8032}
8033
8034/**
8035 * Decreases the number of objects dependent on the machine state.
8036 * Must always complete the #i_addStateDependency() call after the state
8037 * dependency is no more necessary.
8038 */
8039void Machine::i_releaseStateDependency()
8040{
8041 AutoCaller autoCaller(this);
8042 AssertComRCReturnVoid(autoCaller.rc());
8043
8044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8045
8046 /* releaseStateDependency() w/o addStateDependency()? */
8047 AssertReturnVoid(mData->mMachineStateDeps != 0);
8048 -- mData->mMachineStateDeps;
8049
8050 if (mData->mMachineStateDeps == 0)
8051 {
8052 /* inform i_ensureNoStateDependencies() that there are no more deps */
8053 if (mData->mMachineStateChangePending != 0)
8054 {
8055 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8056 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8057 }
8058 }
8059}
8060
8061Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8062{
8063 /* start with nothing found */
8064 Utf8Str strResult("");
8065
8066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8067
8068 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8069 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8070 // found:
8071 strResult = it->second; // source is a Utf8Str
8072
8073 return strResult;
8074}
8075
8076// protected methods
8077/////////////////////////////////////////////////////////////////////////////
8078
8079/**
8080 * Performs machine state checks based on the @a aDepType value. If a check
8081 * fails, this method will set extended error info, otherwise it will return
8082 * S_OK. It is supposed, that on failure, the caller will immediately return
8083 * the return value of this method to the upper level.
8084 *
8085 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8086 *
8087 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8088 * current state of this machine object allows to change settings of the
8089 * machine (i.e. the machine is not registered, or registered but not running
8090 * and not saved). It is useful to call this method from Machine setters
8091 * before performing any change.
8092 *
8093 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8094 * as for MutableStateDep except that if the machine is saved, S_OK is also
8095 * returned. This is useful in setters which allow changing machine
8096 * properties when it is in the saved state.
8097 *
8098 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8099 * if the current state of this machine object allows to change runtime
8100 * changeable settings of the machine (i.e. the machine is not registered, or
8101 * registered but either running or not running and not saved). It is useful
8102 * to call this method from Machine setters before performing any changes to
8103 * runtime changeable settings.
8104 *
8105 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8106 * the same as for MutableOrRunningStateDep except that if the machine is
8107 * saved, S_OK is also returned. This is useful in setters which allow
8108 * changing runtime and saved state changeable machine properties.
8109 *
8110 * @param aDepType Dependency type to check.
8111 *
8112 * @note Non Machine based classes should use #i_addStateDependency() and
8113 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8114 * template.
8115 *
8116 * @note This method must be called from under this object's read or write
8117 * lock.
8118 */
8119HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8120{
8121 switch (aDepType)
8122 {
8123 case AnyStateDep:
8124 {
8125 break;
8126 }
8127 case MutableStateDep:
8128 {
8129 if ( mData->mRegistered
8130 && ( !i_isSessionMachine()
8131 || ( mData->mMachineState != MachineState_Aborted
8132 && mData->mMachineState != MachineState_Teleported
8133 && mData->mMachineState != MachineState_PoweredOff
8134 )
8135 )
8136 )
8137 return setError(VBOX_E_INVALID_VM_STATE,
8138 tr("The machine is not mutable (state is %s)"),
8139 Global::stringifyMachineState(mData->mMachineState));
8140 break;
8141 }
8142 case MutableOrSavedStateDep:
8143 {
8144 if ( mData->mRegistered
8145 && ( !i_isSessionMachine()
8146 || ( mData->mMachineState != MachineState_Aborted
8147 && mData->mMachineState != MachineState_Teleported
8148 && mData->mMachineState != MachineState_Saved
8149 && mData->mMachineState != MachineState_AbortedSaved
8150 && mData->mMachineState != MachineState_PoweredOff
8151 )
8152 )
8153 )
8154 return setError(VBOX_E_INVALID_VM_STATE,
8155 tr("The machine is not mutable or saved (state is %s)"),
8156 Global::stringifyMachineState(mData->mMachineState));
8157 break;
8158 }
8159 case MutableOrRunningStateDep:
8160 {
8161 if ( mData->mRegistered
8162 && ( !i_isSessionMachine()
8163 || ( mData->mMachineState != MachineState_Aborted
8164 && mData->mMachineState != MachineState_Teleported
8165 && mData->mMachineState != MachineState_PoweredOff
8166 && !Global::IsOnline(mData->mMachineState)
8167 )
8168 )
8169 )
8170 return setError(VBOX_E_INVALID_VM_STATE,
8171 tr("The machine is not mutable or running (state is %s)"),
8172 Global::stringifyMachineState(mData->mMachineState));
8173 break;
8174 }
8175 case MutableOrSavedOrRunningStateDep:
8176 {
8177 if ( mData->mRegistered
8178 && ( !i_isSessionMachine()
8179 || ( mData->mMachineState != MachineState_Aborted
8180 && mData->mMachineState != MachineState_Teleported
8181 && mData->mMachineState != MachineState_Saved
8182 && mData->mMachineState != MachineState_AbortedSaved
8183 && mData->mMachineState != MachineState_PoweredOff
8184 && !Global::IsOnline(mData->mMachineState)
8185 )
8186 )
8187 )
8188 return setError(VBOX_E_INVALID_VM_STATE,
8189 tr("The machine is not mutable, saved or running (state is %s)"),
8190 Global::stringifyMachineState(mData->mMachineState));
8191 break;
8192 }
8193 }
8194
8195 return S_OK;
8196}
8197
8198/**
8199 * Helper to initialize all associated child objects and allocate data
8200 * structures.
8201 *
8202 * This method must be called as a part of the object's initialization procedure
8203 * (usually done in the #init() method).
8204 *
8205 * @note Must be called only from #init() or from #i_registeredInit().
8206 */
8207HRESULT Machine::initDataAndChildObjects()
8208{
8209 AutoCaller autoCaller(this);
8210 AssertComRCReturnRC(autoCaller.rc());
8211 AssertReturn( getObjectState().getState() == ObjectState::InInit
8212 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8213
8214 AssertReturn(!mData->mAccessible, E_FAIL);
8215
8216 /* allocate data structures */
8217 mSSData.allocate();
8218 mUserData.allocate();
8219 mHWData.allocate();
8220 mMediumAttachments.allocate();
8221 mStorageControllers.allocate();
8222 mUSBControllers.allocate();
8223
8224 /* initialize mOSTypeId */
8225 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8226
8227/** @todo r=bird: init() methods never fails, right? Why don't we make them
8228 * return void then! */
8229
8230 /* create associated BIOS settings object */
8231 unconst(mBIOSSettings).createObject();
8232 mBIOSSettings->init(this);
8233
8234 /* create associated trusted platform module object */
8235 unconst(mTrustedPlatformModule).createObject();
8236 mTrustedPlatformModule->init(this);
8237
8238 /* create associated NVRAM store object */
8239 unconst(mNvramStore).createObject();
8240 mNvramStore->init(this);
8241
8242 /* create associated record settings object */
8243 unconst(mRecordingSettings).createObject();
8244 mRecordingSettings->init(this);
8245
8246 /* create the graphics adapter object (always present) */
8247 unconst(mGraphicsAdapter).createObject();
8248 mGraphicsAdapter->init(this);
8249
8250 /* create an associated VRDE object (default is disabled) */
8251 unconst(mVRDEServer).createObject();
8252 mVRDEServer->init(this);
8253
8254 /* create associated serial port objects */
8255 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8256 {
8257 unconst(mSerialPorts[slot]).createObject();
8258 mSerialPorts[slot]->init(this, slot);
8259 }
8260
8261 /* create associated parallel port objects */
8262 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8263 {
8264 unconst(mParallelPorts[slot]).createObject();
8265 mParallelPorts[slot]->init(this, slot);
8266 }
8267
8268 /* create the audio adapter object (always present, default is disabled) */
8269 unconst(mAudioAdapter).createObject();
8270 mAudioAdapter->init(this);
8271
8272 /* create the USB device filters object (always present) */
8273 unconst(mUSBDeviceFilters).createObject();
8274 mUSBDeviceFilters->init(this);
8275
8276 /* create associated network adapter objects */
8277 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8278 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8279 {
8280 unconst(mNetworkAdapters[slot]).createObject();
8281 mNetworkAdapters[slot]->init(this, slot);
8282 }
8283
8284 /* create the bandwidth control */
8285 unconst(mBandwidthControl).createObject();
8286 mBandwidthControl->init(this);
8287
8288 return S_OK;
8289}
8290
8291/**
8292 * Helper to uninitialize all associated child objects and to free all data
8293 * structures.
8294 *
8295 * This method must be called as a part of the object's uninitialization
8296 * procedure (usually done in the #uninit() method).
8297 *
8298 * @note Must be called only from #uninit() or from #i_registeredInit().
8299 */
8300void Machine::uninitDataAndChildObjects()
8301{
8302 AutoCaller autoCaller(this);
8303 AssertComRCReturnVoid(autoCaller.rc());
8304 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8305 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8306 || getObjectState().getState() == ObjectState::InUninit
8307 || getObjectState().getState() == ObjectState::Limited);
8308
8309 /* tell all our other child objects we've been uninitialized */
8310 if (mBandwidthControl)
8311 {
8312 mBandwidthControl->uninit();
8313 unconst(mBandwidthControl).setNull();
8314 }
8315
8316 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8317 {
8318 if (mNetworkAdapters[slot])
8319 {
8320 mNetworkAdapters[slot]->uninit();
8321 unconst(mNetworkAdapters[slot]).setNull();
8322 }
8323 }
8324
8325 if (mUSBDeviceFilters)
8326 {
8327 mUSBDeviceFilters->uninit();
8328 unconst(mUSBDeviceFilters).setNull();
8329 }
8330
8331 if (mAudioAdapter)
8332 {
8333 mAudioAdapter->uninit();
8334 unconst(mAudioAdapter).setNull();
8335 }
8336
8337 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8338 {
8339 if (mParallelPorts[slot])
8340 {
8341 mParallelPorts[slot]->uninit();
8342 unconst(mParallelPorts[slot]).setNull();
8343 }
8344 }
8345
8346 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8347 {
8348 if (mSerialPorts[slot])
8349 {
8350 mSerialPorts[slot]->uninit();
8351 unconst(mSerialPorts[slot]).setNull();
8352 }
8353 }
8354
8355 if (mVRDEServer)
8356 {
8357 mVRDEServer->uninit();
8358 unconst(mVRDEServer).setNull();
8359 }
8360
8361 if (mGraphicsAdapter)
8362 {
8363 mGraphicsAdapter->uninit();
8364 unconst(mGraphicsAdapter).setNull();
8365 }
8366
8367 if (mBIOSSettings)
8368 {
8369 mBIOSSettings->uninit();
8370 unconst(mBIOSSettings).setNull();
8371 }
8372
8373 if (mTrustedPlatformModule)
8374 {
8375 mTrustedPlatformModule->uninit();
8376 unconst(mTrustedPlatformModule).setNull();
8377 }
8378
8379 if (mNvramStore)
8380 {
8381 mNvramStore->uninit();
8382 unconst(mNvramStore).setNull();
8383 }
8384
8385 if (mRecordingSettings)
8386 {
8387 mRecordingSettings->uninit();
8388 unconst(mRecordingSettings).setNull();
8389 }
8390
8391 /* Deassociate media (only when a real Machine or a SnapshotMachine
8392 * instance is uninitialized; SessionMachine instances refer to real
8393 * Machine media). This is necessary for a clean re-initialization of
8394 * the VM after successfully re-checking the accessibility state. Note
8395 * that in case of normal Machine or SnapshotMachine uninitialization (as
8396 * a result of unregistering or deleting the snapshot), outdated media
8397 * attachments will already be uninitialized and deleted, so this
8398 * code will not affect them. */
8399 if ( !mMediumAttachments.isNull()
8400 && !i_isSessionMachine()
8401 )
8402 {
8403 for (MediumAttachmentList::const_iterator
8404 it = mMediumAttachments->begin();
8405 it != mMediumAttachments->end();
8406 ++it)
8407 {
8408 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8409 if (pMedium.isNull())
8410 continue;
8411 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8412 AssertComRC(rc);
8413 }
8414 }
8415
8416 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8417 {
8418 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8419 if (mData->mFirstSnapshot)
8420 {
8421 // snapshots tree is protected by machine write lock; strictly
8422 // this isn't necessary here since we're deleting the entire
8423 // machine, but otherwise we assert in Snapshot::uninit()
8424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8425 mData->mFirstSnapshot->uninit();
8426 mData->mFirstSnapshot.setNull();
8427 }
8428
8429 mData->mCurrentSnapshot.setNull();
8430 }
8431
8432 /* free data structures (the essential mData structure is not freed here
8433 * since it may be still in use) */
8434 mMediumAttachments.free();
8435 mStorageControllers.free();
8436 mUSBControllers.free();
8437 mHWData.free();
8438 mUserData.free();
8439 mSSData.free();
8440}
8441
8442/**
8443 * Returns a pointer to the Machine object for this machine that acts like a
8444 * parent for complex machine data objects such as shared folders, etc.
8445 *
8446 * For primary Machine objects and for SnapshotMachine objects, returns this
8447 * object's pointer itself. For SessionMachine objects, returns the peer
8448 * (primary) machine pointer.
8449 */
8450Machine *Machine::i_getMachine()
8451{
8452 if (i_isSessionMachine())
8453 return (Machine*)mPeer;
8454 return this;
8455}
8456
8457/**
8458 * Makes sure that there are no machine state dependents. If necessary, waits
8459 * for the number of dependents to drop to zero.
8460 *
8461 * Make sure this method is called from under this object's write lock to
8462 * guarantee that no new dependents may be added when this method returns
8463 * control to the caller.
8464 *
8465 * @note Receives a lock to this object for writing. The lock will be released
8466 * while waiting (if necessary).
8467 *
8468 * @warning To be used only in methods that change the machine state!
8469 */
8470void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8471{
8472 AssertReturnVoid(isWriteLockOnCurrentThread());
8473
8474 /* Wait for all state dependents if necessary */
8475 if (mData->mMachineStateDeps != 0)
8476 {
8477 /* lazy semaphore creation */
8478 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8479 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8480
8481 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8482 mData->mMachineStateDeps));
8483
8484 ++mData->mMachineStateChangePending;
8485
8486 /* reset the semaphore before waiting, the last dependent will signal
8487 * it */
8488 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8489
8490 alock.release();
8491
8492 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8493
8494 alock.acquire();
8495
8496 -- mData->mMachineStateChangePending;
8497 }
8498}
8499
8500/**
8501 * Changes the machine state and informs callbacks.
8502 *
8503 * This method is not intended to fail so it either returns S_OK or asserts (and
8504 * returns a failure).
8505 *
8506 * @note Locks this object for writing.
8507 */
8508HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8509{
8510 LogFlowThisFuncEnter();
8511 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8512 Assert(aMachineState != MachineState_Null);
8513
8514 AutoCaller autoCaller(this);
8515 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8516
8517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8518
8519 /* wait for state dependents to drop to zero */
8520 i_ensureNoStateDependencies(alock);
8521
8522 MachineState_T const enmOldState = mData->mMachineState;
8523 if (enmOldState != aMachineState)
8524 {
8525 mData->mMachineState = aMachineState;
8526 RTTimeNow(&mData->mLastStateChange);
8527
8528#ifdef VBOX_WITH_DTRACE_R3_MAIN
8529 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8530#endif
8531 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8532 }
8533
8534 LogFlowThisFuncLeave();
8535 return S_OK;
8536}
8537
8538/**
8539 * Searches for a shared folder with the given logical name
8540 * in the collection of shared folders.
8541 *
8542 * @param aName logical name of the shared folder
8543 * @param aSharedFolder where to return the found object
8544 * @param aSetError whether to set the error info if the folder is
8545 * not found
8546 * @return
8547 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8548 *
8549 * @note
8550 * must be called from under the object's lock!
8551 */
8552HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8553 ComObjPtr<SharedFolder> &aSharedFolder,
8554 bool aSetError /* = false */)
8555{
8556 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8557 for (HWData::SharedFolderList::const_iterator
8558 it = mHWData->mSharedFolders.begin();
8559 it != mHWData->mSharedFolders.end();
8560 ++it)
8561 {
8562 SharedFolder *pSF = *it;
8563 AutoCaller autoCaller(pSF);
8564 if (pSF->i_getName() == aName)
8565 {
8566 aSharedFolder = pSF;
8567 rc = S_OK;
8568 break;
8569 }
8570 }
8571
8572 if (aSetError && FAILED(rc))
8573 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8574
8575 return rc;
8576}
8577
8578/**
8579 * Initializes all machine instance data from the given settings structures
8580 * from XML. The exception is the machine UUID which needs special handling
8581 * depending on the caller's use case, so the caller needs to set that herself.
8582 *
8583 * This gets called in several contexts during machine initialization:
8584 *
8585 * -- When machine XML exists on disk already and needs to be loaded into memory,
8586 * for example, from #i_registeredInit() to load all registered machines on
8587 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8588 * attached to the machine should be part of some media registry already.
8589 *
8590 * -- During OVF import, when a machine config has been constructed from an
8591 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8592 * ensure that the media listed as attachments in the config (which have
8593 * been imported from the OVF) receive the correct registry ID.
8594 *
8595 * -- During VM cloning.
8596 *
8597 * @param config Machine settings from XML.
8598 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8599 * for each attached medium in the config.
8600 * @return
8601 */
8602HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8603 const Guid *puuidRegistry)
8604{
8605 // copy name, description, OS type, teleporter, UTC etc.
8606 mUserData->s = config.machineUserData;
8607
8608 // look up the object by Id to check it is valid
8609 ComObjPtr<GuestOSType> pGuestOSType;
8610 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8611 if (!pGuestOSType.isNull())
8612 mUserData->s.strOsType = pGuestOSType->i_id();
8613
8614 // stateFile (optional)
8615 if (config.strStateFile.isEmpty())
8616 mSSData->strStateFilePath.setNull();
8617 else
8618 {
8619 Utf8Str stateFilePathFull(config.strStateFile);
8620 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8621 if (RT_FAILURE(vrc))
8622 return setErrorBoth(E_FAIL, vrc,
8623 tr("Invalid saved state file path '%s' (%Rrc)"),
8624 config.strStateFile.c_str(),
8625 vrc);
8626 mSSData->strStateFilePath = stateFilePathFull;
8627 }
8628
8629 // snapshot folder needs special processing so set it again
8630 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8631 if (FAILED(rc)) return rc;
8632
8633 /* Copy the extra data items (config may or may not be the same as
8634 * mData->pMachineConfigFile) if necessary. When loading the XML files
8635 * from disk they are the same, but not for OVF import. */
8636 if (mData->pMachineConfigFile != &config)
8637 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8638
8639 /* currentStateModified (optional, default is true) */
8640 mData->mCurrentStateModified = config.fCurrentStateModified;
8641
8642 mData->mLastStateChange = config.timeLastStateChange;
8643
8644 /*
8645 * note: all mUserData members must be assigned prior this point because
8646 * we need to commit changes in order to let mUserData be shared by all
8647 * snapshot machine instances.
8648 */
8649 mUserData.commitCopy();
8650
8651 // machine registry, if present (must be loaded before snapshots)
8652 if (config.canHaveOwnMediaRegistry())
8653 {
8654 // determine machine folder
8655 Utf8Str strMachineFolder = i_getSettingsFileFull();
8656 strMachineFolder.stripFilename();
8657 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8658 config.mediaRegistry,
8659 strMachineFolder);
8660 if (FAILED(rc)) return rc;
8661 }
8662
8663 /* Snapshot node (optional) */
8664 size_t cRootSnapshots;
8665 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8666 {
8667 // there must be only one root snapshot
8668 Assert(cRootSnapshots == 1);
8669
8670 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8671
8672 rc = i_loadSnapshot(snap,
8673 config.uuidCurrentSnapshot,
8674 NULL); // no parent == first snapshot
8675 if (FAILED(rc)) return rc;
8676 }
8677
8678 // hardware data
8679 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8680 if (FAILED(rc)) return rc;
8681
8682 /*
8683 * NOTE: the assignment below must be the last thing to do,
8684 * otherwise it will be not possible to change the settings
8685 * somewhere in the code above because all setters will be
8686 * blocked by i_checkStateDependency(MutableStateDep).
8687 */
8688
8689 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8690 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8691 {
8692 /* no need to use i_setMachineState() during init() */
8693 mData->mMachineState = MachineState_AbortedSaved;
8694 }
8695 else if (config.fAborted)
8696 {
8697 mSSData->strStateFilePath.setNull();
8698
8699 /* no need to use i_setMachineState() during init() */
8700 mData->mMachineState = MachineState_Aborted;
8701 }
8702 else if (!mSSData->strStateFilePath.isEmpty())
8703 {
8704 /* no need to use i_setMachineState() during init() */
8705 mData->mMachineState = MachineState_Saved;
8706 }
8707
8708 // after loading settings, we are no longer different from the XML on disk
8709 mData->flModifications = 0;
8710
8711 return S_OK;
8712}
8713
8714/**
8715 * Recursively loads all snapshots starting from the given.
8716 *
8717 * @param data snapshot settings.
8718 * @param aCurSnapshotId Current snapshot ID from the settings file.
8719 * @param aParentSnapshot Parent snapshot.
8720 */
8721HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8722 const Guid &aCurSnapshotId,
8723 Snapshot *aParentSnapshot)
8724{
8725 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8726 AssertReturn(!i_isSessionMachine(), E_FAIL);
8727
8728 HRESULT rc = S_OK;
8729
8730 Utf8Str strStateFile;
8731 if (!data.strStateFile.isEmpty())
8732 {
8733 /* optional */
8734 strStateFile = data.strStateFile;
8735 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8736 if (RT_FAILURE(vrc))
8737 return setErrorBoth(E_FAIL, vrc,
8738 tr("Invalid saved state file path '%s' (%Rrc)"),
8739 strStateFile.c_str(),
8740 vrc);
8741 }
8742
8743 /* create a snapshot machine object */
8744 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8745 pSnapshotMachine.createObject();
8746 rc = pSnapshotMachine->initFromSettings(this,
8747 data.hardware,
8748 &data.debugging,
8749 &data.autostart,
8750 data.uuid.ref(),
8751 strStateFile);
8752 if (FAILED(rc)) return rc;
8753
8754 /* create a snapshot object */
8755 ComObjPtr<Snapshot> pSnapshot;
8756 pSnapshot.createObject();
8757 /* initialize the snapshot */
8758 rc = pSnapshot->init(mParent, // VirtualBox object
8759 data.uuid,
8760 data.strName,
8761 data.strDescription,
8762 data.timestamp,
8763 pSnapshotMachine,
8764 aParentSnapshot);
8765 if (FAILED(rc)) return rc;
8766
8767 /* memorize the first snapshot if necessary */
8768 if (!mData->mFirstSnapshot)
8769 mData->mFirstSnapshot = pSnapshot;
8770
8771 /* memorize the current snapshot when appropriate */
8772 if ( !mData->mCurrentSnapshot
8773 && pSnapshot->i_getId() == aCurSnapshotId
8774 )
8775 mData->mCurrentSnapshot = pSnapshot;
8776
8777 // now create the children
8778 for (settings::SnapshotsList::const_iterator
8779 it = data.llChildSnapshots.begin();
8780 it != data.llChildSnapshots.end();
8781 ++it)
8782 {
8783 const settings::Snapshot &childData = *it;
8784 // recurse
8785 rc = i_loadSnapshot(childData,
8786 aCurSnapshotId,
8787 pSnapshot); // parent = the one we created above
8788 if (FAILED(rc)) return rc;
8789 }
8790
8791 return rc;
8792}
8793
8794/**
8795 * Loads settings into mHWData.
8796 *
8797 * @param puuidRegistry Registry ID.
8798 * @param puuidSnapshot Snapshot ID
8799 * @param data Reference to the hardware settings.
8800 * @param pDbg Pointer to the debugging settings.
8801 * @param pAutostart Pointer to the autostart settings.
8802 */
8803HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8804 const Guid *puuidSnapshot,
8805 const settings::Hardware &data,
8806 const settings::Debugging *pDbg,
8807 const settings::Autostart *pAutostart)
8808{
8809 AssertReturn(!i_isSessionMachine(), E_FAIL);
8810
8811 HRESULT rc = S_OK;
8812
8813 try
8814 {
8815 ComObjPtr<GuestOSType> pGuestOSType;
8816 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8817
8818 /* The hardware version attribute (optional). */
8819 mHWData->mHWVersion = data.strVersion;
8820 mHWData->mHardwareUUID = data.uuid;
8821
8822 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8823 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8824 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8825 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8826 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8827 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8828 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8829 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8830 mHWData->mPAEEnabled = data.fPAE;
8831 mHWData->mLongMode = data.enmLongMode;
8832 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8833 mHWData->mAPIC = data.fAPIC;
8834 mHWData->mX2APIC = data.fX2APIC;
8835 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8836 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8837 mHWData->mSpecCtrl = data.fSpecCtrl;
8838 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8839 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8840 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8841 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8842 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8843 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8844 mHWData->mCPUCount = data.cCPUs;
8845 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8846 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8847 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8848 mHWData->mCpuProfile = data.strCpuProfile;
8849
8850 // cpu
8851 if (mHWData->mCPUHotPlugEnabled)
8852 {
8853 for (settings::CpuList::const_iterator
8854 it = data.llCpus.begin();
8855 it != data.llCpus.end();
8856 ++it)
8857 {
8858 const settings::Cpu &cpu = *it;
8859
8860 mHWData->mCPUAttached[cpu.ulId] = true;
8861 }
8862 }
8863
8864 // cpuid leafs
8865 for (settings::CpuIdLeafsList::const_iterator
8866 it = data.llCpuIdLeafs.begin();
8867 it != data.llCpuIdLeafs.end();
8868 ++it)
8869 {
8870 const settings::CpuIdLeaf &rLeaf= *it;
8871 if ( rLeaf.idx < UINT32_C(0x20)
8872 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8873 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8874 mHWData->mCpuIdLeafList.push_back(rLeaf);
8875 /* else: just ignore */
8876 }
8877
8878 mHWData->mMemorySize = data.ulMemorySizeMB;
8879 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8880
8881 // boot order
8882 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8883 {
8884 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8885 if (it == data.mapBootOrder.end())
8886 mHWData->mBootOrder[i] = DeviceType_Null;
8887 else
8888 mHWData->mBootOrder[i] = it->second;
8889 }
8890
8891 mHWData->mFirmwareType = data.firmwareType;
8892 mHWData->mPointingHIDType = data.pointingHIDType;
8893 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8894 mHWData->mChipsetType = data.chipsetType;
8895 mHWData->mIommuType = data.iommuType;
8896 mHWData->mParavirtProvider = data.paravirtProvider;
8897 mHWData->mParavirtDebug = data.strParavirtDebug;
8898 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8899 mHWData->mHPETEnabled = data.fHPETEnabled;
8900
8901 /* GraphicsAdapter */
8902 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8903 if (FAILED(rc)) return rc;
8904
8905 /* VRDEServer */
8906 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8907 if (FAILED(rc)) return rc;
8908
8909 /* BIOS */
8910 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8911 if (FAILED(rc)) return rc;
8912
8913 /* Trusted Platform Module */
8914 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8915 if (FAILED(rc)) return rc;
8916
8917 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8918 if (FAILED(rc)) return rc;
8919
8920 /* Recording settings */
8921 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8922 if (FAILED(rc)) return rc;
8923
8924 // Bandwidth control (must come before network adapters)
8925 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8926 if (FAILED(rc)) return rc;
8927
8928 /* USB controllers */
8929 for (settings::USBControllerList::const_iterator
8930 it = data.usbSettings.llUSBControllers.begin();
8931 it != data.usbSettings.llUSBControllers.end();
8932 ++it)
8933 {
8934 const settings::USBController &settingsCtrl = *it;
8935 ComObjPtr<USBController> newCtrl;
8936
8937 newCtrl.createObject();
8938 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8939 mUSBControllers->push_back(newCtrl);
8940 }
8941
8942 /* USB device filters */
8943 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8944 if (FAILED(rc)) return rc;
8945
8946 // network adapters (establish array size first and apply defaults, to
8947 // ensure reading the same settings as we saved, since the list skips
8948 // adapters having defaults)
8949 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8950 size_t oldCount = mNetworkAdapters.size();
8951 if (newCount > oldCount)
8952 {
8953 mNetworkAdapters.resize(newCount);
8954 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8955 {
8956 unconst(mNetworkAdapters[slot]).createObject();
8957 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8958 }
8959 }
8960 else if (newCount < oldCount)
8961 mNetworkAdapters.resize(newCount);
8962 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8963 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8964 for (settings::NetworkAdaptersList::const_iterator
8965 it = data.llNetworkAdapters.begin();
8966 it != data.llNetworkAdapters.end();
8967 ++it)
8968 {
8969 const settings::NetworkAdapter &nic = *it;
8970
8971 /* slot uniqueness is guaranteed by XML Schema */
8972 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8973 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8974 if (FAILED(rc)) return rc;
8975 }
8976
8977 // serial ports (establish defaults first, to ensure reading the same
8978 // settings as we saved, since the list skips ports having defaults)
8979 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8980 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8981 for (settings::SerialPortsList::const_iterator
8982 it = data.llSerialPorts.begin();
8983 it != data.llSerialPorts.end();
8984 ++it)
8985 {
8986 const settings::SerialPort &s = *it;
8987
8988 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8989 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8990 if (FAILED(rc)) return rc;
8991 }
8992
8993 // parallel ports (establish defaults first, to ensure reading the same
8994 // settings as we saved, since the list skips ports having defaults)
8995 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8996 mParallelPorts[i]->i_applyDefaults();
8997 for (settings::ParallelPortsList::const_iterator
8998 it = data.llParallelPorts.begin();
8999 it != data.llParallelPorts.end();
9000 ++it)
9001 {
9002 const settings::ParallelPort &p = *it;
9003
9004 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9005 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9006 if (FAILED(rc)) return rc;
9007 }
9008
9009 /* AudioAdapter */
9010 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9011 if (FAILED(rc)) return rc;
9012
9013 /* storage controllers */
9014 rc = i_loadStorageControllers(data.storage,
9015 puuidRegistry,
9016 puuidSnapshot);
9017 if (FAILED(rc)) return rc;
9018
9019 /* Shared folders */
9020 for (settings::SharedFoldersList::const_iterator
9021 it = data.llSharedFolders.begin();
9022 it != data.llSharedFolders.end();
9023 ++it)
9024 {
9025 const settings::SharedFolder &sf = *it;
9026
9027 ComObjPtr<SharedFolder> sharedFolder;
9028 /* Check for double entries. Not allowed! */
9029 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9030 if (SUCCEEDED(rc))
9031 return setError(VBOX_E_OBJECT_IN_USE,
9032 tr("Shared folder named '%s' already exists"),
9033 sf.strName.c_str());
9034
9035 /* Create the new shared folder. Don't break on error. This will be
9036 * reported when the machine starts. */
9037 sharedFolder.createObject();
9038 rc = sharedFolder->init(i_getMachine(),
9039 sf.strName,
9040 sf.strHostPath,
9041 RT_BOOL(sf.fWritable),
9042 RT_BOOL(sf.fAutoMount),
9043 sf.strAutoMountPoint,
9044 false /* fFailOnError */);
9045 if (FAILED(rc)) return rc;
9046 mHWData->mSharedFolders.push_back(sharedFolder);
9047 }
9048
9049 // Clipboard
9050 mHWData->mClipboardMode = data.clipboardMode;
9051 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9052
9053 // drag'n'drop
9054 mHWData->mDnDMode = data.dndMode;
9055
9056 // guest settings
9057 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9058
9059 // IO settings
9060 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9061 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9062
9063 // Host PCI devices
9064 for (settings::HostPCIDeviceAttachmentList::const_iterator
9065 it = data.pciAttachments.begin();
9066 it != data.pciAttachments.end();
9067 ++it)
9068 {
9069 const settings::HostPCIDeviceAttachment &hpda = *it;
9070 ComObjPtr<PCIDeviceAttachment> pda;
9071
9072 pda.createObject();
9073 pda->i_loadSettings(this, hpda);
9074 mHWData->mPCIDeviceAssignments.push_back(pda);
9075 }
9076
9077 /*
9078 * (The following isn't really real hardware, but it lives in HWData
9079 * for reasons of convenience.)
9080 */
9081
9082#ifdef VBOX_WITH_GUEST_PROPS
9083 /* Guest properties (optional) */
9084
9085 /* Only load transient guest properties for configs which have saved
9086 * state, because there shouldn't be any for powered off VMs. The same
9087 * logic applies for snapshots, as offline snapshots shouldn't have
9088 * any such properties. They confuse the code in various places.
9089 * Note: can't rely on the machine state, as it isn't set yet. */
9090 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9091 /* apologies for the hacky unconst() usage, but this needs hacking
9092 * actually inconsistent settings into consistency, otherwise there
9093 * will be some corner cases where the inconsistency survives
9094 * surprisingly long without getting fixed, especially for snapshots
9095 * as there are no config changes. */
9096 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9097 for (settings::GuestPropertiesList::iterator
9098 it = llGuestProperties.begin();
9099 it != llGuestProperties.end();
9100 /*nothing*/)
9101 {
9102 const settings::GuestProperty &prop = *it;
9103 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9104 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9105 if ( fSkipTransientGuestProperties
9106 && ( fFlags & GUEST_PROP_F_TRANSIENT
9107 || fFlags & GUEST_PROP_F_TRANSRESET))
9108 {
9109 it = llGuestProperties.erase(it);
9110 continue;
9111 }
9112 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9113 mHWData->mGuestProperties[prop.strName] = property;
9114 ++it;
9115 }
9116#endif /* VBOX_WITH_GUEST_PROPS defined */
9117
9118 rc = i_loadDebugging(pDbg);
9119 if (FAILED(rc))
9120 return rc;
9121
9122 mHWData->mAutostart = *pAutostart;
9123
9124 /* default frontend */
9125 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9126 }
9127 catch (std::bad_alloc &)
9128 {
9129 return E_OUTOFMEMORY;
9130 }
9131
9132 AssertComRC(rc);
9133 return rc;
9134}
9135
9136/**
9137 * Called from i_loadHardware() to load the debugging settings of the
9138 * machine.
9139 *
9140 * @param pDbg Pointer to the settings.
9141 */
9142HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9143{
9144 mHWData->mDebugging = *pDbg;
9145 /* no more processing currently required, this will probably change. */
9146 return S_OK;
9147}
9148
9149/**
9150 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9151 *
9152 * @param data storage settings.
9153 * @param puuidRegistry media registry ID to set media to or NULL;
9154 * see Machine::i_loadMachineDataFromSettings()
9155 * @param puuidSnapshot snapshot ID
9156 * @return
9157 */
9158HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9159 const Guid *puuidRegistry,
9160 const Guid *puuidSnapshot)
9161{
9162 AssertReturn(!i_isSessionMachine(), E_FAIL);
9163
9164 HRESULT rc = S_OK;
9165
9166 for (settings::StorageControllersList::const_iterator
9167 it = data.llStorageControllers.begin();
9168 it != data.llStorageControllers.end();
9169 ++it)
9170 {
9171 const settings::StorageController &ctlData = *it;
9172
9173 ComObjPtr<StorageController> pCtl;
9174 /* Try to find one with the name first. */
9175 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9176 if (SUCCEEDED(rc))
9177 return setError(VBOX_E_OBJECT_IN_USE,
9178 tr("Storage controller named '%s' already exists"),
9179 ctlData.strName.c_str());
9180
9181 pCtl.createObject();
9182 rc = pCtl->init(this,
9183 ctlData.strName,
9184 ctlData.storageBus,
9185 ctlData.ulInstance,
9186 ctlData.fBootable);
9187 if (FAILED(rc)) return rc;
9188
9189 mStorageControllers->push_back(pCtl);
9190
9191 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9192 if (FAILED(rc)) return rc;
9193
9194 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9195 if (FAILED(rc)) return rc;
9196
9197 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9198 if (FAILED(rc)) return rc;
9199
9200 /* Load the attached devices now. */
9201 rc = i_loadStorageDevices(pCtl,
9202 ctlData,
9203 puuidRegistry,
9204 puuidSnapshot);
9205 if (FAILED(rc)) return rc;
9206 }
9207
9208 return S_OK;
9209}
9210
9211/**
9212 * Called from i_loadStorageControllers for a controller's devices.
9213 *
9214 * @param aStorageController
9215 * @param data
9216 * @param puuidRegistry media registry ID to set media to or NULL; see
9217 * Machine::i_loadMachineDataFromSettings()
9218 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9219 * @return
9220 */
9221HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9222 const settings::StorageController &data,
9223 const Guid *puuidRegistry,
9224 const Guid *puuidSnapshot)
9225{
9226 HRESULT rc = S_OK;
9227
9228 /* paranoia: detect duplicate attachments */
9229 for (settings::AttachedDevicesList::const_iterator
9230 it = data.llAttachedDevices.begin();
9231 it != data.llAttachedDevices.end();
9232 ++it)
9233 {
9234 const settings::AttachedDevice &ad = *it;
9235
9236 for (settings::AttachedDevicesList::const_iterator it2 = it;
9237 it2 != data.llAttachedDevices.end();
9238 ++it2)
9239 {
9240 if (it == it2)
9241 continue;
9242
9243 const settings::AttachedDevice &ad2 = *it2;
9244
9245 if ( ad.lPort == ad2.lPort
9246 && ad.lDevice == ad2.lDevice)
9247 {
9248 return setError(E_FAIL,
9249 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9250 aStorageController->i_getName().c_str(),
9251 ad.lPort,
9252 ad.lDevice,
9253 mUserData->s.strName.c_str());
9254 }
9255 }
9256 }
9257
9258 for (settings::AttachedDevicesList::const_iterator
9259 it = data.llAttachedDevices.begin();
9260 it != data.llAttachedDevices.end();
9261 ++it)
9262 {
9263 const settings::AttachedDevice &dev = *it;
9264 ComObjPtr<Medium> medium;
9265
9266 switch (dev.deviceType)
9267 {
9268 case DeviceType_Floppy:
9269 case DeviceType_DVD:
9270 if (dev.strHostDriveSrc.isNotEmpty())
9271 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9272 false /* fRefresh */, medium);
9273 else
9274 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9275 dev.uuid,
9276 false /* fRefresh */,
9277 false /* aSetError */,
9278 medium);
9279 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9280 // This is not an error. The host drive or UUID might have vanished, so just go
9281 // ahead without this removeable medium attachment
9282 rc = S_OK;
9283 break;
9284
9285 case DeviceType_HardDisk:
9286 {
9287 /* find a hard disk by UUID */
9288 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9289 if (FAILED(rc))
9290 {
9291 if (i_isSnapshotMachine())
9292 {
9293 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9294 // so the user knows that the bad disk is in a snapshot somewhere
9295 com::ErrorInfo info;
9296 return setError(E_FAIL,
9297 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9298 puuidSnapshot->raw(),
9299 info.getText().raw());
9300 }
9301 else
9302 return rc;
9303 }
9304
9305 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9306
9307 if (medium->i_getType() == MediumType_Immutable)
9308 {
9309 if (i_isSnapshotMachine())
9310 return setError(E_FAIL,
9311 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9312 "of the virtual machine '%s' ('%s')"),
9313 medium->i_getLocationFull().c_str(),
9314 dev.uuid.raw(),
9315 puuidSnapshot->raw(),
9316 mUserData->s.strName.c_str(),
9317 mData->m_strConfigFileFull.c_str());
9318
9319 return setError(E_FAIL,
9320 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9321 medium->i_getLocationFull().c_str(),
9322 dev.uuid.raw(),
9323 mUserData->s.strName.c_str(),
9324 mData->m_strConfigFileFull.c_str());
9325 }
9326
9327 if (medium->i_getType() == MediumType_MultiAttach)
9328 {
9329 if (i_isSnapshotMachine())
9330 return setError(E_FAIL,
9331 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9332 "of the virtual machine '%s' ('%s')"),
9333 medium->i_getLocationFull().c_str(),
9334 dev.uuid.raw(),
9335 puuidSnapshot->raw(),
9336 mUserData->s.strName.c_str(),
9337 mData->m_strConfigFileFull.c_str());
9338
9339 return setError(E_FAIL,
9340 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9341 medium->i_getLocationFull().c_str(),
9342 dev.uuid.raw(),
9343 mUserData->s.strName.c_str(),
9344 mData->m_strConfigFileFull.c_str());
9345 }
9346
9347 if ( !i_isSnapshotMachine()
9348 && medium->i_getChildren().size() != 0
9349 )
9350 return setError(E_FAIL,
9351 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9352 "because it has %d differencing child hard disks"),
9353 medium->i_getLocationFull().c_str(),
9354 dev.uuid.raw(),
9355 mUserData->s.strName.c_str(),
9356 mData->m_strConfigFileFull.c_str(),
9357 medium->i_getChildren().size());
9358
9359 if (i_findAttachment(*mMediumAttachments.data(),
9360 medium))
9361 return setError(E_FAIL,
9362 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9363 medium->i_getLocationFull().c_str(),
9364 dev.uuid.raw(),
9365 mUserData->s.strName.c_str(),
9366 mData->m_strConfigFileFull.c_str());
9367
9368 break;
9369 }
9370
9371 default:
9372 return setError(E_FAIL,
9373 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9374 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9375 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9376 }
9377
9378 if (FAILED(rc))
9379 break;
9380
9381 /* Bandwidth groups are loaded at this point. */
9382 ComObjPtr<BandwidthGroup> pBwGroup;
9383
9384 if (!dev.strBwGroup.isEmpty())
9385 {
9386 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9387 if (FAILED(rc))
9388 return setError(E_FAIL,
9389 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9390 medium->i_getLocationFull().c_str(),
9391 dev.strBwGroup.c_str(),
9392 mUserData->s.strName.c_str(),
9393 mData->m_strConfigFileFull.c_str());
9394 pBwGroup->i_reference();
9395 }
9396
9397 const Utf8Str controllerName = aStorageController->i_getName();
9398 ComObjPtr<MediumAttachment> pAttachment;
9399 pAttachment.createObject();
9400 rc = pAttachment->init(this,
9401 medium,
9402 controllerName,
9403 dev.lPort,
9404 dev.lDevice,
9405 dev.deviceType,
9406 false,
9407 dev.fPassThrough,
9408 dev.fTempEject,
9409 dev.fNonRotational,
9410 dev.fDiscard,
9411 dev.fHotPluggable,
9412 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9413 if (FAILED(rc)) break;
9414
9415 /* associate the medium with this machine and snapshot */
9416 if (!medium.isNull())
9417 {
9418 AutoCaller medCaller(medium);
9419 if (FAILED(medCaller.rc())) return medCaller.rc();
9420 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9421
9422 if (i_isSnapshotMachine())
9423 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9424 else
9425 rc = medium->i_addBackReference(mData->mUuid);
9426 /* If the medium->addBackReference fails it sets an appropriate
9427 * error message, so no need to do any guesswork here. */
9428
9429 if (puuidRegistry)
9430 // caller wants registry ID to be set on all attached media (OVF import case)
9431 medium->i_addRegistry(*puuidRegistry);
9432 }
9433
9434 if (FAILED(rc))
9435 break;
9436
9437 /* back up mMediumAttachments to let registeredInit() properly rollback
9438 * on failure (= limited accessibility) */
9439 i_setModified(IsModified_Storage);
9440 mMediumAttachments.backup();
9441 mMediumAttachments->push_back(pAttachment);
9442 }
9443
9444 return rc;
9445}
9446
9447/**
9448 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9449 *
9450 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9451 * @param aSnapshot where to return the found snapshot
9452 * @param aSetError true to set extended error info on failure
9453 */
9454HRESULT Machine::i_findSnapshotById(const Guid &aId,
9455 ComObjPtr<Snapshot> &aSnapshot,
9456 bool aSetError /* = false */)
9457{
9458 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9459
9460 if (!mData->mFirstSnapshot)
9461 {
9462 if (aSetError)
9463 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9464 return E_FAIL;
9465 }
9466
9467 if (aId.isZero())
9468 aSnapshot = mData->mFirstSnapshot;
9469 else
9470 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9471
9472 if (!aSnapshot)
9473 {
9474 if (aSetError)
9475 return setError(E_FAIL,
9476 tr("Could not find a snapshot with UUID {%s}"),
9477 aId.toString().c_str());
9478 return E_FAIL;
9479 }
9480
9481 return S_OK;
9482}
9483
9484/**
9485 * Returns the snapshot with the given name or fails of no such snapshot.
9486 *
9487 * @param strName snapshot name to find
9488 * @param aSnapshot where to return the found snapshot
9489 * @param aSetError true to set extended error info on failure
9490 */
9491HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9492 ComObjPtr<Snapshot> &aSnapshot,
9493 bool aSetError /* = false */)
9494{
9495 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9496
9497 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9498
9499 if (!mData->mFirstSnapshot)
9500 {
9501 if (aSetError)
9502 return setError(VBOX_E_OBJECT_NOT_FOUND,
9503 tr("This machine does not have any snapshots"));
9504 return VBOX_E_OBJECT_NOT_FOUND;
9505 }
9506
9507 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9508
9509 if (!aSnapshot)
9510 {
9511 if (aSetError)
9512 return setError(VBOX_E_OBJECT_NOT_FOUND,
9513 tr("Could not find a snapshot named '%s'"), strName.c_str());
9514 return VBOX_E_OBJECT_NOT_FOUND;
9515 }
9516
9517 return S_OK;
9518}
9519
9520/**
9521 * Returns a storage controller object with the given name.
9522 *
9523 * @param aName storage controller name to find
9524 * @param aStorageController where to return the found storage controller
9525 * @param aSetError true to set extended error info on failure
9526 */
9527HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9528 ComObjPtr<StorageController> &aStorageController,
9529 bool aSetError /* = false */)
9530{
9531 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9532
9533 for (StorageControllerList::const_iterator
9534 it = mStorageControllers->begin();
9535 it != mStorageControllers->end();
9536 ++it)
9537 {
9538 if ((*it)->i_getName() == aName)
9539 {
9540 aStorageController = (*it);
9541 return S_OK;
9542 }
9543 }
9544
9545 if (aSetError)
9546 return setError(VBOX_E_OBJECT_NOT_FOUND,
9547 tr("Could not find a storage controller named '%s'"),
9548 aName.c_str());
9549 return VBOX_E_OBJECT_NOT_FOUND;
9550}
9551
9552/**
9553 * Returns a USB controller object with the given name.
9554 *
9555 * @param aName USB controller name to find
9556 * @param aUSBController where to return the found USB controller
9557 * @param aSetError true to set extended error info on failure
9558 */
9559HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9560 ComObjPtr<USBController> &aUSBController,
9561 bool aSetError /* = false */)
9562{
9563 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9564
9565 for (USBControllerList::const_iterator
9566 it = mUSBControllers->begin();
9567 it != mUSBControllers->end();
9568 ++it)
9569 {
9570 if ((*it)->i_getName() == aName)
9571 {
9572 aUSBController = (*it);
9573 return S_OK;
9574 }
9575 }
9576
9577 if (aSetError)
9578 return setError(VBOX_E_OBJECT_NOT_FOUND,
9579 tr("Could not find a storage controller named '%s'"),
9580 aName.c_str());
9581 return VBOX_E_OBJECT_NOT_FOUND;
9582}
9583
9584/**
9585 * Returns the number of USB controller instance of the given type.
9586 *
9587 * @param enmType USB controller type.
9588 */
9589ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9590{
9591 ULONG cCtrls = 0;
9592
9593 for (USBControllerList::const_iterator
9594 it = mUSBControllers->begin();
9595 it != mUSBControllers->end();
9596 ++it)
9597 {
9598 if ((*it)->i_getControllerType() == enmType)
9599 cCtrls++;
9600 }
9601
9602 return cCtrls;
9603}
9604
9605HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9606 MediumAttachmentList &atts)
9607{
9608 AutoCaller autoCaller(this);
9609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9610
9611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9612
9613 for (MediumAttachmentList::const_iterator
9614 it = mMediumAttachments->begin();
9615 it != mMediumAttachments->end();
9616 ++it)
9617 {
9618 const ComObjPtr<MediumAttachment> &pAtt = *it;
9619 // should never happen, but deal with NULL pointers in the list.
9620 AssertContinue(!pAtt.isNull());
9621
9622 // getControllerName() needs caller+read lock
9623 AutoCaller autoAttCaller(pAtt);
9624 if (FAILED(autoAttCaller.rc()))
9625 {
9626 atts.clear();
9627 return autoAttCaller.rc();
9628 }
9629 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9630
9631 if (pAtt->i_getControllerName() == aName)
9632 atts.push_back(pAtt);
9633 }
9634
9635 return S_OK;
9636}
9637
9638
9639/**
9640 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9641 * file if the machine name was changed and about creating a new settings file
9642 * if this is a new machine.
9643 *
9644 * @note Must be never called directly but only from #saveSettings().
9645 */
9646HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9647 bool *pfSettingsFileIsNew)
9648{
9649 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9650
9651 HRESULT rc = S_OK;
9652
9653 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9654 /// @todo need to handle primary group change, too
9655
9656 /* attempt to rename the settings file if machine name is changed */
9657 if ( mUserData->s.fNameSync
9658 && mUserData.isBackedUp()
9659 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9660 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9661 )
9662 {
9663 bool dirRenamed = false;
9664 bool fileRenamed = false;
9665
9666 Utf8Str configFile, newConfigFile;
9667 Utf8Str configFilePrev, newConfigFilePrev;
9668 Utf8Str NVRAMFile, newNVRAMFile;
9669 Utf8Str configDir, newConfigDir;
9670
9671 do
9672 {
9673 int vrc = VINF_SUCCESS;
9674
9675 Utf8Str name = mUserData.backedUpData()->s.strName;
9676 Utf8Str newName = mUserData->s.strName;
9677 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9678 if (group == "/")
9679 group.setNull();
9680 Utf8Str newGroup = mUserData->s.llGroups.front();
9681 if (newGroup == "/")
9682 newGroup.setNull();
9683
9684 configFile = mData->m_strConfigFileFull;
9685
9686 /* first, rename the directory if it matches the group and machine name */
9687 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9688 /** @todo hack, make somehow use of ComposeMachineFilename */
9689 if (mUserData->s.fDirectoryIncludesUUID)
9690 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9691 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9692 /** @todo hack, make somehow use of ComposeMachineFilename */
9693 if (mUserData->s.fDirectoryIncludesUUID)
9694 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9695 configDir = configFile;
9696 configDir.stripFilename();
9697 newConfigDir = configDir;
9698 if ( configDir.length() >= groupPlusName.length()
9699 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9700 groupPlusName.c_str()))
9701 {
9702 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9703 Utf8Str newConfigBaseDir(newConfigDir);
9704 newConfigDir.append(newGroupPlusName);
9705 /* consistency: use \ if appropriate on the platform */
9706 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9707 /* new dir and old dir cannot be equal here because of 'if'
9708 * above and because name != newName */
9709 Assert(configDir != newConfigDir);
9710 if (!fSettingsFileIsNew)
9711 {
9712 /* perform real rename only if the machine is not new */
9713 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9714 if ( vrc == VERR_FILE_NOT_FOUND
9715 || vrc == VERR_PATH_NOT_FOUND)
9716 {
9717 /* create the parent directory, then retry renaming */
9718 Utf8Str parent(newConfigDir);
9719 parent.stripFilename();
9720 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9721 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9722 }
9723 if (RT_FAILURE(vrc))
9724 {
9725 rc = setErrorBoth(E_FAIL, vrc,
9726 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9727 configDir.c_str(),
9728 newConfigDir.c_str(),
9729 vrc);
9730 break;
9731 }
9732 /* delete subdirectories which are no longer needed */
9733 Utf8Str dir(configDir);
9734 dir.stripFilename();
9735 while (dir != newConfigBaseDir && dir != ".")
9736 {
9737 vrc = RTDirRemove(dir.c_str());
9738 if (RT_FAILURE(vrc))
9739 break;
9740 dir.stripFilename();
9741 }
9742 dirRenamed = true;
9743 }
9744 }
9745
9746 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9747
9748 /* then try to rename the settings file itself */
9749 if (newConfigFile != configFile)
9750 {
9751 /* get the path to old settings file in renamed directory */
9752 Assert(mData->m_strConfigFileFull == configFile);
9753 configFile.printf("%s%c%s",
9754 newConfigDir.c_str(),
9755 RTPATH_DELIMITER,
9756 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9757 if (!fSettingsFileIsNew)
9758 {
9759 /* perform real rename only if the machine is not new */
9760 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9761 if (RT_FAILURE(vrc))
9762 {
9763 rc = setErrorBoth(E_FAIL, vrc,
9764 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9765 configFile.c_str(),
9766 newConfigFile.c_str(),
9767 vrc);
9768 break;
9769 }
9770 fileRenamed = true;
9771 configFilePrev = configFile;
9772 configFilePrev += "-prev";
9773 newConfigFilePrev = newConfigFile;
9774 newConfigFilePrev += "-prev";
9775 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9776 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9777 if (NVRAMFile.isNotEmpty())
9778 {
9779 // in the NVRAM file path, replace the old directory with the new directory
9780 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9781 {
9782 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9783 NVRAMFile = newConfigDir + strNVRAMFile;
9784 }
9785 newNVRAMFile = newConfigFile;
9786 newNVRAMFile.stripSuffix();
9787 newNVRAMFile += ".nvram";
9788 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9789 }
9790 }
9791 }
9792
9793 // update m_strConfigFileFull amd mConfigFile
9794 mData->m_strConfigFileFull = newConfigFile;
9795 // compute the relative path too
9796 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9797
9798 // store the old and new so that VirtualBox::i_saveSettings() can update
9799 // the media registry
9800 if ( mData->mRegistered
9801 && (configDir != newConfigDir || configFile != newConfigFile))
9802 {
9803 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9804
9805 if (pfNeedsGlobalSaveSettings)
9806 *pfNeedsGlobalSaveSettings = true;
9807 }
9808
9809 // in the saved state file path, replace the old directory with the new directory
9810 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9811 {
9812 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9813 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9814 }
9815 if (newNVRAMFile.isNotEmpty())
9816 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9817
9818 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9819 if (mData->mFirstSnapshot)
9820 {
9821 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9822 newConfigDir.c_str());
9823 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9824 newConfigDir.c_str());
9825 }
9826 }
9827 while (0);
9828
9829 if (FAILED(rc))
9830 {
9831 /* silently try to rename everything back */
9832 if (fileRenamed)
9833 {
9834 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9835 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9836 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9837 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9838 }
9839 if (dirRenamed)
9840 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9841 }
9842
9843 if (FAILED(rc)) return rc;
9844 }
9845
9846 if (fSettingsFileIsNew)
9847 {
9848 /* create a virgin config file */
9849 int vrc = VINF_SUCCESS;
9850
9851 /* ensure the settings directory exists */
9852 Utf8Str path(mData->m_strConfigFileFull);
9853 path.stripFilename();
9854 if (!RTDirExists(path.c_str()))
9855 {
9856 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9857 if (RT_FAILURE(vrc))
9858 {
9859 return setErrorBoth(E_FAIL, vrc,
9860 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9861 path.c_str(),
9862 vrc);
9863 }
9864 }
9865
9866 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9867 path = mData->m_strConfigFileFull;
9868 RTFILE f = NIL_RTFILE;
9869 vrc = RTFileOpen(&f, path.c_str(),
9870 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9871 if (RT_FAILURE(vrc))
9872 return setErrorBoth(E_FAIL, vrc,
9873 tr("Could not create the settings file '%s' (%Rrc)"),
9874 path.c_str(),
9875 vrc);
9876 RTFileClose(f);
9877 }
9878 if (pfSettingsFileIsNew)
9879 *pfSettingsFileIsNew = fSettingsFileIsNew;
9880
9881 return rc;
9882}
9883
9884/**
9885 * Saves and commits machine data, user data and hardware data.
9886 *
9887 * Note that on failure, the data remains uncommitted.
9888 *
9889 * @a aFlags may combine the following flags:
9890 *
9891 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9892 * Used when saving settings after an operation that makes them 100%
9893 * correspond to the settings from the current snapshot.
9894 * - SaveS_Force: settings will be saved without doing a deep compare of the
9895 * settings structures. This is used when this is called because snapshots
9896 * have changed to avoid the overhead of the deep compare.
9897 *
9898 * @note Must be called from under this object's write lock. Locks children for
9899 * writing.
9900 *
9901 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9902 * initialized to false and that will be set to true by this function if
9903 * the caller must invoke VirtualBox::i_saveSettings() because the global
9904 * settings have changed. This will happen if a machine rename has been
9905 * saved and the global machine and media registries will therefore need
9906 * updating.
9907 * @param alock Reference to the lock for this machine object.
9908 * @param aFlags Flags.
9909 */
9910HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9911 AutoWriteLock &alock,
9912 int aFlags /*= 0*/)
9913{
9914 LogFlowThisFuncEnter();
9915
9916 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9917
9918 /* make sure child objects are unable to modify the settings while we are
9919 * saving them */
9920 i_ensureNoStateDependencies(alock);
9921
9922 AssertReturn(!i_isSnapshotMachine(),
9923 E_FAIL);
9924
9925 if (!mData->mAccessible)
9926 return setError(VBOX_E_INVALID_VM_STATE,
9927 tr("The machine is not accessible, so cannot save settings"));
9928
9929 HRESULT rc = S_OK;
9930 bool fNeedsWrite = false;
9931 bool fSettingsFileIsNew = false;
9932
9933 /* First, prepare to save settings. It will care about renaming the
9934 * settings directory and file if the machine name was changed and about
9935 * creating a new settings file if this is a new machine. */
9936 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
9937 &fSettingsFileIsNew);
9938 if (FAILED(rc)) return rc;
9939
9940 // keep a pointer to the current settings structures
9941 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9942 settings::MachineConfigFile *pNewConfig = NULL;
9943
9944 try
9945 {
9946 // make a fresh one to have everyone write stuff into
9947 pNewConfig = new settings::MachineConfigFile(NULL);
9948 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9949
9950 // now go and copy all the settings data from COM to the settings structures
9951 // (this calls i_saveSettings() on all the COM objects in the machine)
9952 i_copyMachineDataToSettings(*pNewConfig);
9953
9954 if (aFlags & SaveS_ResetCurStateModified)
9955 {
9956 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9957 mData->mCurrentStateModified = FALSE;
9958 fNeedsWrite = true; // always, no need to compare
9959 }
9960 else if (aFlags & SaveS_Force)
9961 {
9962 fNeedsWrite = true; // always, no need to compare
9963 }
9964 else
9965 {
9966 if (!mData->mCurrentStateModified)
9967 {
9968 // do a deep compare of the settings that we just saved with the settings
9969 // previously stored in the config file; this invokes MachineConfigFile::operator==
9970 // which does a deep compare of all the settings, which is expensive but less expensive
9971 // than writing out XML in vain
9972 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9973
9974 // could still be modified if any settings changed
9975 mData->mCurrentStateModified = fAnySettingsChanged;
9976
9977 fNeedsWrite = fAnySettingsChanged;
9978 }
9979 else
9980 fNeedsWrite = true;
9981 }
9982
9983 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9984
9985 if (fNeedsWrite)
9986 // now spit it all out!
9987 pNewConfig->write(mData->m_strConfigFileFull);
9988
9989 mData->pMachineConfigFile = pNewConfig;
9990 delete pOldConfig;
9991 i_commit();
9992
9993 // after saving settings, we are no longer different from the XML on disk
9994 mData->flModifications = 0;
9995 }
9996 catch (HRESULT err)
9997 {
9998 // we assume that error info is set by the thrower
9999 rc = err;
10000
10001 // delete any newly created settings file
10002 if (fSettingsFileIsNew)
10003 RTFileDelete(mData->m_strConfigFileFull.c_str());
10004
10005 // restore old config
10006 delete pNewConfig;
10007 mData->pMachineConfigFile = pOldConfig;
10008 }
10009 catch (...)
10010 {
10011 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10012 }
10013
10014 if (fNeedsWrite)
10015 {
10016 /* Fire the data change event, even on failure (since we've already
10017 * committed all data). This is done only for SessionMachines because
10018 * mutable Machine instances are always not registered (i.e. private
10019 * to the client process that creates them) and thus don't need to
10020 * inform callbacks. */
10021 if (i_isSessionMachine())
10022 mParent->i_onMachineDataChanged(mData->mUuid);
10023 }
10024
10025 LogFlowThisFunc(("rc=%08X\n", rc));
10026 LogFlowThisFuncLeave();
10027 return rc;
10028}
10029
10030/**
10031 * Implementation for saving the machine settings into the given
10032 * settings::MachineConfigFile instance. This copies machine extradata
10033 * from the previous machine config file in the instance data, if any.
10034 *
10035 * This gets called from two locations:
10036 *
10037 * -- Machine::i_saveSettings(), during the regular XML writing;
10038 *
10039 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10040 * exported to OVF and we write the VirtualBox proprietary XML
10041 * into a <vbox:Machine> tag.
10042 *
10043 * This routine fills all the fields in there, including snapshots, *except*
10044 * for the following:
10045 *
10046 * -- fCurrentStateModified. There is some special logic associated with that.
10047 *
10048 * The caller can then call MachineConfigFile::write() or do something else
10049 * with it.
10050 *
10051 * Caller must hold the machine lock!
10052 *
10053 * This throws XML errors and HRESULT, so the caller must have a catch block!
10054 */
10055void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10056{
10057 // deep copy extradata, being extra careful with self assignment (the STL
10058 // map assignment on Mac OS X clang based Xcode isn't checking)
10059 if (&config != mData->pMachineConfigFile)
10060 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10061
10062 config.uuid = mData->mUuid;
10063
10064 // copy name, description, OS type, teleport, UTC etc.
10065 config.machineUserData = mUserData->s;
10066
10067 if ( mData->mMachineState == MachineState_Saved
10068 || mData->mMachineState == MachineState_AbortedSaved
10069 || mData->mMachineState == MachineState_Restoring
10070 // when doing certain snapshot operations we may or may not have
10071 // a saved state in the current state, so keep everything as is
10072 || ( ( mData->mMachineState == MachineState_Snapshotting
10073 || mData->mMachineState == MachineState_DeletingSnapshot
10074 || mData->mMachineState == MachineState_RestoringSnapshot)
10075 && (!mSSData->strStateFilePath.isEmpty())
10076 )
10077 )
10078 {
10079 Assert(!mSSData->strStateFilePath.isEmpty());
10080 /* try to make the file name relative to the settings file dir */
10081 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10082 }
10083 else
10084 {
10085 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10086 config.strStateFile.setNull();
10087 }
10088
10089 if (mData->mCurrentSnapshot)
10090 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10091 else
10092 config.uuidCurrentSnapshot.clear();
10093
10094 config.timeLastStateChange = mData->mLastStateChange;
10095 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10096 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10097
10098 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10099 if (FAILED(rc)) throw rc;
10100
10101 // save machine's media registry if this is VirtualBox 4.0 or later
10102 if (config.canHaveOwnMediaRegistry())
10103 {
10104 // determine machine folder
10105 Utf8Str strMachineFolder = i_getSettingsFileFull();
10106 strMachineFolder.stripFilename();
10107 mParent->i_saveMediaRegistry(config.mediaRegistry,
10108 i_getId(), // only media with registry ID == machine UUID
10109 strMachineFolder);
10110 // this throws HRESULT
10111 }
10112
10113 // save snapshots
10114 rc = i_saveAllSnapshots(config);
10115 if (FAILED(rc)) throw rc;
10116}
10117
10118/**
10119 * Saves all snapshots of the machine into the given machine config file. Called
10120 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10121 * @param config
10122 * @return
10123 */
10124HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10125{
10126 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10127
10128 HRESULT rc = S_OK;
10129
10130 try
10131 {
10132 config.llFirstSnapshot.clear();
10133
10134 if (mData->mFirstSnapshot)
10135 {
10136 // the settings use a list for "the first snapshot"
10137 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10138
10139 // get reference to the snapshot on the list and work on that
10140 // element straight in the list to avoid excessive copying later
10141 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10142 if (FAILED(rc)) throw rc;
10143 }
10144
10145// if (mType == IsSessionMachine)
10146// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10147
10148 }
10149 catch (HRESULT err)
10150 {
10151 /* we assume that error info is set by the thrower */
10152 rc = err;
10153 }
10154 catch (...)
10155 {
10156 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10157 }
10158
10159 return rc;
10160}
10161
10162/**
10163 * Saves the VM hardware configuration. It is assumed that the
10164 * given node is empty.
10165 *
10166 * @param data Reference to the settings object for the hardware config.
10167 * @param pDbg Pointer to the settings object for the debugging config
10168 * which happens to live in mHWData.
10169 * @param pAutostart Pointer to the settings object for the autostart config
10170 * which happens to live in mHWData.
10171 */
10172HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10173 settings::Autostart *pAutostart)
10174{
10175 HRESULT rc = S_OK;
10176
10177 try
10178 {
10179 /* The hardware version attribute (optional).
10180 Automatically upgrade from 1 to current default hardware version
10181 when there is no saved state. (ugly!) */
10182 if ( mHWData->mHWVersion == "1"
10183 && mSSData->strStateFilePath.isEmpty()
10184 )
10185 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10186
10187 data.strVersion = mHWData->mHWVersion;
10188 data.uuid = mHWData->mHardwareUUID;
10189
10190 // CPU
10191 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10192 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10193 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10194 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10195 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10196 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10197 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10198 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10199 data.fPAE = !!mHWData->mPAEEnabled;
10200 data.enmLongMode = mHWData->mLongMode;
10201 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10202 data.fAPIC = !!mHWData->mAPIC;
10203 data.fX2APIC = !!mHWData->mX2APIC;
10204 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10205 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10206 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10207 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10208 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10209 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10210 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10211 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10212 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10213 data.cCPUs = mHWData->mCPUCount;
10214 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10215 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10216 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10217 data.strCpuProfile = mHWData->mCpuProfile;
10218
10219 data.llCpus.clear();
10220 if (data.fCpuHotPlug)
10221 {
10222 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10223 {
10224 if (mHWData->mCPUAttached[idx])
10225 {
10226 settings::Cpu cpu;
10227 cpu.ulId = idx;
10228 data.llCpus.push_back(cpu);
10229 }
10230 }
10231 }
10232
10233 /* Standard and Extended CPUID leafs. */
10234 data.llCpuIdLeafs.clear();
10235 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10236
10237 // memory
10238 data.ulMemorySizeMB = mHWData->mMemorySize;
10239 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10240
10241 // firmware
10242 data.firmwareType = mHWData->mFirmwareType;
10243
10244 // HID
10245 data.pointingHIDType = mHWData->mPointingHIDType;
10246 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10247
10248 // chipset
10249 data.chipsetType = mHWData->mChipsetType;
10250
10251 // iommu
10252 data.iommuType = mHWData->mIommuType;
10253
10254 // paravirt
10255 data.paravirtProvider = mHWData->mParavirtProvider;
10256 data.strParavirtDebug = mHWData->mParavirtDebug;
10257
10258 // emulated USB card reader
10259 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10260
10261 // HPET
10262 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10263
10264 // boot order
10265 data.mapBootOrder.clear();
10266 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10267 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10268
10269 /* VRDEServer settings (optional) */
10270 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10271 if (FAILED(rc)) throw rc;
10272
10273 /* BIOS settings (required) */
10274 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10275 if (FAILED(rc)) throw rc;
10276
10277 /* Trusted Platform Module settings (required) */
10278 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10279 if (FAILED(rc)) throw rc;
10280
10281 /* NVRAM settings (required) */
10282 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10283 if (FAILED(rc)) throw rc;
10284
10285 /* Recording settings (required) */
10286 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10287 if (FAILED(rc)) throw rc;
10288
10289 /* GraphicsAdapter settings (required) */
10290 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10291 if (FAILED(rc)) throw rc;
10292
10293 /* USB Controller (required) */
10294 data.usbSettings.llUSBControllers.clear();
10295 for (USBControllerList::const_iterator
10296 it = mUSBControllers->begin();
10297 it != mUSBControllers->end();
10298 ++it)
10299 {
10300 ComObjPtr<USBController> ctrl = *it;
10301 settings::USBController settingsCtrl;
10302
10303 settingsCtrl.strName = ctrl->i_getName();
10304 settingsCtrl.enmType = ctrl->i_getControllerType();
10305
10306 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10307 }
10308
10309 /* USB device filters (required) */
10310 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10311 if (FAILED(rc)) throw rc;
10312
10313 /* Network adapters (required) */
10314 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10315 data.llNetworkAdapters.clear();
10316 /* Write out only the nominal number of network adapters for this
10317 * chipset type. Since Machine::commit() hasn't been called there
10318 * may be extra NIC settings in the vector. */
10319 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10320 {
10321 settings::NetworkAdapter nic;
10322 nic.ulSlot = (uint32_t)slot;
10323 /* paranoia check... must not be NULL, but must not crash either. */
10324 if (mNetworkAdapters[slot])
10325 {
10326 if (mNetworkAdapters[slot]->i_hasDefaults())
10327 continue;
10328
10329 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10330 if (FAILED(rc)) throw rc;
10331
10332 data.llNetworkAdapters.push_back(nic);
10333 }
10334 }
10335
10336 /* Serial ports */
10337 data.llSerialPorts.clear();
10338 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10339 {
10340 if (mSerialPorts[slot]->i_hasDefaults())
10341 continue;
10342
10343 settings::SerialPort s;
10344 s.ulSlot = slot;
10345 rc = mSerialPorts[slot]->i_saveSettings(s);
10346 if (FAILED(rc)) return rc;
10347
10348 data.llSerialPorts.push_back(s);
10349 }
10350
10351 /* Parallel ports */
10352 data.llParallelPorts.clear();
10353 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10354 {
10355 if (mParallelPorts[slot]->i_hasDefaults())
10356 continue;
10357
10358 settings::ParallelPort p;
10359 p.ulSlot = slot;
10360 rc = mParallelPorts[slot]->i_saveSettings(p);
10361 if (FAILED(rc)) return rc;
10362
10363 data.llParallelPorts.push_back(p);
10364 }
10365
10366 /* Audio adapter */
10367 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10368 if (FAILED(rc)) return rc;
10369
10370 rc = i_saveStorageControllers(data.storage);
10371 if (FAILED(rc)) return rc;
10372
10373 /* Shared folders */
10374 data.llSharedFolders.clear();
10375 for (HWData::SharedFolderList::const_iterator
10376 it = mHWData->mSharedFolders.begin();
10377 it != mHWData->mSharedFolders.end();
10378 ++it)
10379 {
10380 SharedFolder *pSF = *it;
10381 AutoCaller sfCaller(pSF);
10382 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10383 settings::SharedFolder sf;
10384 sf.strName = pSF->i_getName();
10385 sf.strHostPath = pSF->i_getHostPath();
10386 sf.fWritable = !!pSF->i_isWritable();
10387 sf.fAutoMount = !!pSF->i_isAutoMounted();
10388 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10389
10390 data.llSharedFolders.push_back(sf);
10391 }
10392
10393 // clipboard
10394 data.clipboardMode = mHWData->mClipboardMode;
10395 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10396
10397 // drag'n'drop
10398 data.dndMode = mHWData->mDnDMode;
10399
10400 /* Guest */
10401 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10402
10403 // IO settings
10404 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10405 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10406
10407 /* BandwidthControl (required) */
10408 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10409 if (FAILED(rc)) throw rc;
10410
10411 /* Host PCI devices */
10412 data.pciAttachments.clear();
10413 for (HWData::PCIDeviceAssignmentList::const_iterator
10414 it = mHWData->mPCIDeviceAssignments.begin();
10415 it != mHWData->mPCIDeviceAssignments.end();
10416 ++it)
10417 {
10418 ComObjPtr<PCIDeviceAttachment> pda = *it;
10419 settings::HostPCIDeviceAttachment hpda;
10420
10421 rc = pda->i_saveSettings(hpda);
10422 if (FAILED(rc)) throw rc;
10423
10424 data.pciAttachments.push_back(hpda);
10425 }
10426
10427 // guest properties
10428 data.llGuestProperties.clear();
10429#ifdef VBOX_WITH_GUEST_PROPS
10430 for (HWData::GuestPropertyMap::const_iterator
10431 it = mHWData->mGuestProperties.begin();
10432 it != mHWData->mGuestProperties.end();
10433 ++it)
10434 {
10435 HWData::GuestProperty property = it->second;
10436
10437 /* Remove transient guest properties at shutdown unless we
10438 * are saving state. Note that restoring snapshot intentionally
10439 * keeps them, they will be removed if appropriate once the final
10440 * machine state is set (as crashes etc. need to work). */
10441 if ( ( mData->mMachineState == MachineState_PoweredOff
10442 || mData->mMachineState == MachineState_Aborted
10443 || mData->mMachineState == MachineState_Teleported)
10444 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10445 continue;
10446 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10447 prop.strName = it->first;
10448 prop.strValue = property.strValue;
10449 prop.timestamp = (uint64_t)property.mTimestamp;
10450 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10451 GuestPropWriteFlags(property.mFlags, szFlags);
10452 prop.strFlags = szFlags;
10453
10454 data.llGuestProperties.push_back(prop);
10455 }
10456
10457 /* I presume this doesn't require a backup(). */
10458 mData->mGuestPropertiesModified = FALSE;
10459#endif /* VBOX_WITH_GUEST_PROPS defined */
10460
10461 *pDbg = mHWData->mDebugging;
10462 *pAutostart = mHWData->mAutostart;
10463
10464 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10465 }
10466 catch (std::bad_alloc &)
10467 {
10468 return E_OUTOFMEMORY;
10469 }
10470
10471 AssertComRC(rc);
10472 return rc;
10473}
10474
10475/**
10476 * Saves the storage controller configuration.
10477 *
10478 * @param data storage settings.
10479 */
10480HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10481{
10482 data.llStorageControllers.clear();
10483
10484 for (StorageControllerList::const_iterator
10485 it = mStorageControllers->begin();
10486 it != mStorageControllers->end();
10487 ++it)
10488 {
10489 HRESULT rc;
10490 ComObjPtr<StorageController> pCtl = *it;
10491
10492 settings::StorageController ctl;
10493 ctl.strName = pCtl->i_getName();
10494 ctl.controllerType = pCtl->i_getControllerType();
10495 ctl.storageBus = pCtl->i_getStorageBus();
10496 ctl.ulInstance = pCtl->i_getInstance();
10497 ctl.fBootable = pCtl->i_getBootable();
10498
10499 /* Save the port count. */
10500 ULONG portCount;
10501 rc = pCtl->COMGETTER(PortCount)(&portCount);
10502 ComAssertComRCRet(rc, rc);
10503 ctl.ulPortCount = portCount;
10504
10505 /* Save fUseHostIOCache */
10506 BOOL fUseHostIOCache;
10507 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10508 ComAssertComRCRet(rc, rc);
10509 ctl.fUseHostIOCache = !!fUseHostIOCache;
10510
10511 /* save the devices now. */
10512 rc = i_saveStorageDevices(pCtl, ctl);
10513 ComAssertComRCRet(rc, rc);
10514
10515 data.llStorageControllers.push_back(ctl);
10516 }
10517
10518 return S_OK;
10519}
10520
10521/**
10522 * Saves the hard disk configuration.
10523 */
10524HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10525 settings::StorageController &data)
10526{
10527 MediumAttachmentList atts;
10528
10529 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10530 if (FAILED(rc)) return rc;
10531
10532 data.llAttachedDevices.clear();
10533 for (MediumAttachmentList::const_iterator
10534 it = atts.begin();
10535 it != atts.end();
10536 ++it)
10537 {
10538 settings::AttachedDevice dev;
10539 IMediumAttachment *iA = *it;
10540 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10541 Medium *pMedium = pAttach->i_getMedium();
10542
10543 dev.deviceType = pAttach->i_getType();
10544 dev.lPort = pAttach->i_getPort();
10545 dev.lDevice = pAttach->i_getDevice();
10546 dev.fPassThrough = pAttach->i_getPassthrough();
10547 dev.fHotPluggable = pAttach->i_getHotPluggable();
10548 if (pMedium)
10549 {
10550 if (pMedium->i_isHostDrive())
10551 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10552 else
10553 dev.uuid = pMedium->i_getId();
10554 dev.fTempEject = pAttach->i_getTempEject();
10555 dev.fNonRotational = pAttach->i_getNonRotational();
10556 dev.fDiscard = pAttach->i_getDiscard();
10557 }
10558
10559 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10560
10561 data.llAttachedDevices.push_back(dev);
10562 }
10563
10564 return S_OK;
10565}
10566
10567/**
10568 * Saves machine state settings as defined by aFlags
10569 * (SaveSTS_* values).
10570 *
10571 * @param aFlags Combination of SaveSTS_* flags.
10572 *
10573 * @note Locks objects for writing.
10574 */
10575HRESULT Machine::i_saveStateSettings(int aFlags)
10576{
10577 if (aFlags == 0)
10578 return S_OK;
10579
10580 AutoCaller autoCaller(this);
10581 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10582
10583 /* This object's write lock is also necessary to serialize file access
10584 * (prevent concurrent reads and writes) */
10585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10586
10587 HRESULT rc = S_OK;
10588
10589 Assert(mData->pMachineConfigFile);
10590
10591 try
10592 {
10593 if (aFlags & SaveSTS_CurStateModified)
10594 mData->pMachineConfigFile->fCurrentStateModified = true;
10595
10596 if (aFlags & SaveSTS_StateFilePath)
10597 {
10598 if (!mSSData->strStateFilePath.isEmpty())
10599 /* try to make the file name relative to the settings file dir */
10600 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10601 else
10602 mData->pMachineConfigFile->strStateFile.setNull();
10603 }
10604
10605 if (aFlags & SaveSTS_StateTimeStamp)
10606 {
10607 Assert( mData->mMachineState != MachineState_Aborted
10608 || mSSData->strStateFilePath.isEmpty());
10609
10610 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10611
10612 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10613 || mData->mMachineState == MachineState_AbortedSaved);
10614/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10615 }
10616
10617 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10618 }
10619 catch (...)
10620 {
10621 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10622 }
10623
10624 return rc;
10625}
10626
10627/**
10628 * Ensures that the given medium is added to a media registry. If this machine
10629 * was created with 4.0 or later, then the machine registry is used. Otherwise
10630 * the global VirtualBox media registry is used.
10631 *
10632 * Caller must NOT hold machine lock, media tree or any medium locks!
10633 *
10634 * @param pMedium
10635 */
10636void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10637{
10638 /* Paranoia checks: do not hold machine or media tree locks. */
10639 AssertReturnVoid(!isWriteLockOnCurrentThread());
10640 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10641
10642 ComObjPtr<Medium> pBase;
10643 {
10644 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10645 pBase = pMedium->i_getBase();
10646 }
10647
10648 /* Paranoia checks: do not hold medium locks. */
10649 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10650 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10651
10652 // decide which medium registry to use now that the medium is attached:
10653 Guid uuid;
10654 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10655 if (fCanHaveOwnMediaRegistry)
10656 // machine XML is VirtualBox 4.0 or higher:
10657 uuid = i_getId(); // machine UUID
10658 else
10659 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10660
10661 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10662 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10663 if (pMedium->i_addRegistry(uuid))
10664 mParent->i_markRegistryModified(uuid);
10665
10666 /* For more complex hard disk structures it can happen that the base
10667 * medium isn't yet associated with any medium registry. Do that now. */
10668 if (pMedium != pBase)
10669 {
10670 /* Tree lock needed by Medium::addRegistry when recursing. */
10671 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10672 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10673 {
10674 treeLock.release();
10675 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10676 treeLock.acquire();
10677 }
10678 if (pBase->i_addRegistryRecursive(uuid))
10679 {
10680 treeLock.release();
10681 mParent->i_markRegistryModified(uuid);
10682 }
10683 }
10684}
10685
10686/**
10687 * Creates differencing hard disks for all normal hard disks attached to this
10688 * machine and a new set of attachments to refer to created disks.
10689 *
10690 * Used when taking a snapshot or when deleting the current state. Gets called
10691 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10692 *
10693 * This method assumes that mMediumAttachments contains the original hard disk
10694 * attachments it needs to create diffs for. On success, these attachments will
10695 * be replaced with the created diffs.
10696 *
10697 * Attachments with non-normal hard disks are left as is.
10698 *
10699 * If @a aOnline is @c false then the original hard disks that require implicit
10700 * diffs will be locked for reading. Otherwise it is assumed that they are
10701 * already locked for writing (when the VM was started). Note that in the latter
10702 * case it is responsibility of the caller to lock the newly created diffs for
10703 * writing if this method succeeds.
10704 *
10705 * @param aProgress Progress object to run (must contain at least as
10706 * many operations left as the number of hard disks
10707 * attached).
10708 * @param aWeight Weight of this operation.
10709 * @param aOnline Whether the VM was online prior to this operation.
10710 *
10711 * @note The progress object is not marked as completed, neither on success nor
10712 * on failure. This is a responsibility of the caller.
10713 *
10714 * @note Locks this object and the media tree for writing.
10715 */
10716HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10717 ULONG aWeight,
10718 bool aOnline)
10719{
10720 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10721
10722 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10723 AssertReturn(!!pProgressControl, E_INVALIDARG);
10724
10725 AutoCaller autoCaller(this);
10726 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10727
10728 AutoMultiWriteLock2 alock(this->lockHandle(),
10729 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10730
10731 /* must be in a protective state because we release the lock below */
10732 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10733 || mData->mMachineState == MachineState_OnlineSnapshotting
10734 || mData->mMachineState == MachineState_LiveSnapshotting
10735 || mData->mMachineState == MachineState_RestoringSnapshot
10736 || mData->mMachineState == MachineState_DeletingSnapshot
10737 , E_FAIL);
10738
10739 HRESULT rc = S_OK;
10740
10741 // use appropriate locked media map (online or offline)
10742 MediumLockListMap lockedMediaOffline;
10743 MediumLockListMap *lockedMediaMap;
10744 if (aOnline)
10745 lockedMediaMap = &mData->mSession.mLockedMedia;
10746 else
10747 lockedMediaMap = &lockedMediaOffline;
10748
10749 try
10750 {
10751 if (!aOnline)
10752 {
10753 /* lock all attached hard disks early to detect "in use"
10754 * situations before creating actual diffs */
10755 for (MediumAttachmentList::const_iterator
10756 it = mMediumAttachments->begin();
10757 it != mMediumAttachments->end();
10758 ++it)
10759 {
10760 MediumAttachment *pAtt = *it;
10761 if (pAtt->i_getType() == DeviceType_HardDisk)
10762 {
10763 Medium *pMedium = pAtt->i_getMedium();
10764 Assert(pMedium);
10765
10766 MediumLockList *pMediumLockList(new MediumLockList());
10767 alock.release();
10768 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10769 NULL /* pToLockWrite */,
10770 false /* fMediumLockWriteAll */,
10771 NULL,
10772 *pMediumLockList);
10773 alock.acquire();
10774 if (FAILED(rc))
10775 {
10776 delete pMediumLockList;
10777 throw rc;
10778 }
10779 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10780 if (FAILED(rc))
10781 {
10782 throw setError(rc,
10783 tr("Collecting locking information for all attached media failed"));
10784 }
10785 }
10786 }
10787
10788 /* Now lock all media. If this fails, nothing is locked. */
10789 alock.release();
10790 rc = lockedMediaMap->Lock();
10791 alock.acquire();
10792 if (FAILED(rc))
10793 {
10794 throw setError(rc,
10795 tr("Locking of attached media failed"));
10796 }
10797 }
10798
10799 /* remember the current list (note that we don't use backup() since
10800 * mMediumAttachments may be already backed up) */
10801 MediumAttachmentList atts = *mMediumAttachments.data();
10802
10803 /* start from scratch */
10804 mMediumAttachments->clear();
10805
10806 /* go through remembered attachments and create diffs for normal hard
10807 * disks and attach them */
10808 for (MediumAttachmentList::const_iterator
10809 it = atts.begin();
10810 it != atts.end();
10811 ++it)
10812 {
10813 MediumAttachment *pAtt = *it;
10814
10815 DeviceType_T devType = pAtt->i_getType();
10816 Medium *pMedium = pAtt->i_getMedium();
10817
10818 if ( devType != DeviceType_HardDisk
10819 || pMedium == NULL
10820 || pMedium->i_getType() != MediumType_Normal)
10821 {
10822 /* copy the attachment as is */
10823
10824 /** @todo the progress object created in SessionMachine::TakeSnaphot
10825 * only expects operations for hard disks. Later other
10826 * device types need to show up in the progress as well. */
10827 if (devType == DeviceType_HardDisk)
10828 {
10829 if (pMedium == NULL)
10830 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10831 aWeight); // weight
10832 else
10833 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10834 pMedium->i_getBase()->i_getName().c_str()).raw(),
10835 aWeight); // weight
10836 }
10837
10838 mMediumAttachments->push_back(pAtt);
10839 continue;
10840 }
10841
10842 /* need a diff */
10843 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10844 pMedium->i_getBase()->i_getName().c_str()).raw(),
10845 aWeight); // weight
10846
10847 Utf8Str strFullSnapshotFolder;
10848 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10849
10850 ComObjPtr<Medium> diff;
10851 diff.createObject();
10852 // store the diff in the same registry as the parent
10853 // (this cannot fail here because we can't create implicit diffs for
10854 // unregistered images)
10855 Guid uuidRegistryParent;
10856 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10857 Assert(fInRegistry); NOREF(fInRegistry);
10858 rc = diff->init(mParent,
10859 pMedium->i_getPreferredDiffFormat(),
10860 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10861 uuidRegistryParent,
10862 DeviceType_HardDisk);
10863 if (FAILED(rc)) throw rc;
10864
10865 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10866 * the push_back? Looks like we're going to release medium with the
10867 * wrong kind of lock (general issue with if we fail anywhere at all)
10868 * and an orphaned VDI in the snapshots folder. */
10869
10870 /* update the appropriate lock list */
10871 MediumLockList *pMediumLockList;
10872 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10873 AssertComRCThrowRC(rc);
10874 if (aOnline)
10875 {
10876 alock.release();
10877 /* The currently attached medium will be read-only, change
10878 * the lock type to read. */
10879 rc = pMediumLockList->Update(pMedium, false);
10880 alock.acquire();
10881 AssertComRCThrowRC(rc);
10882 }
10883
10884 /* release the locks before the potentially lengthy operation */
10885 alock.release();
10886 rc = pMedium->i_createDiffStorage(diff,
10887 pMedium->i_getPreferredDiffVariant(),
10888 pMediumLockList,
10889 NULL /* aProgress */,
10890 true /* aWait */,
10891 false /* aNotify */);
10892 alock.acquire();
10893 if (FAILED(rc)) throw rc;
10894
10895 /* actual lock list update is done in Machine::i_commitMedia */
10896
10897 rc = diff->i_addBackReference(mData->mUuid);
10898 AssertComRCThrowRC(rc);
10899
10900 /* add a new attachment */
10901 ComObjPtr<MediumAttachment> attachment;
10902 attachment.createObject();
10903 rc = attachment->init(this,
10904 diff,
10905 pAtt->i_getControllerName(),
10906 pAtt->i_getPort(),
10907 pAtt->i_getDevice(),
10908 DeviceType_HardDisk,
10909 true /* aImplicit */,
10910 false /* aPassthrough */,
10911 false /* aTempEject */,
10912 pAtt->i_getNonRotational(),
10913 pAtt->i_getDiscard(),
10914 pAtt->i_getHotPluggable(),
10915 pAtt->i_getBandwidthGroup());
10916 if (FAILED(rc)) throw rc;
10917
10918 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10919 AssertComRCThrowRC(rc);
10920 mMediumAttachments->push_back(attachment);
10921 }
10922 }
10923 catch (HRESULT aRC) { rc = aRC; }
10924
10925 /* unlock all hard disks we locked when there is no VM */
10926 if (!aOnline)
10927 {
10928 ErrorInfoKeeper eik;
10929
10930 HRESULT rc1 = lockedMediaMap->Clear();
10931 AssertComRC(rc1);
10932 }
10933
10934 return rc;
10935}
10936
10937/**
10938 * Deletes implicit differencing hard disks created either by
10939 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10940 * mMediumAttachments.
10941 *
10942 * Note that to delete hard disks created by #attachDevice() this method is
10943 * called from #i_rollbackMedia() when the changes are rolled back.
10944 *
10945 * @note Locks this object and the media tree for writing.
10946 */
10947HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10948{
10949 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10950
10951 AutoCaller autoCaller(this);
10952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10953
10954 AutoMultiWriteLock2 alock(this->lockHandle(),
10955 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10956
10957 /* We absolutely must have backed up state. */
10958 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10959
10960 /* Check if there are any implicitly created diff images. */
10961 bool fImplicitDiffs = false;
10962 for (MediumAttachmentList::const_iterator
10963 it = mMediumAttachments->begin();
10964 it != mMediumAttachments->end();
10965 ++it)
10966 {
10967 const ComObjPtr<MediumAttachment> &pAtt = *it;
10968 if (pAtt->i_isImplicit())
10969 {
10970 fImplicitDiffs = true;
10971 break;
10972 }
10973 }
10974 /* If there is nothing to do, leave early. This saves lots of image locking
10975 * effort. It also avoids a MachineStateChanged event without real reason.
10976 * This is important e.g. when loading a VM config, because there should be
10977 * no events. Otherwise API clients can become thoroughly confused for
10978 * inaccessible VMs (the code for loading VM configs uses this method for
10979 * cleanup if the config makes no sense), as they take such events as an
10980 * indication that the VM is alive, and they would force the VM config to
10981 * be reread, leading to an endless loop. */
10982 if (!fImplicitDiffs)
10983 return S_OK;
10984
10985 HRESULT rc = S_OK;
10986 MachineState_T oldState = mData->mMachineState;
10987
10988 /* will release the lock before the potentially lengthy operation,
10989 * so protect with the special state (unless already protected) */
10990 if ( oldState != MachineState_Snapshotting
10991 && oldState != MachineState_OnlineSnapshotting
10992 && oldState != MachineState_LiveSnapshotting
10993 && oldState != MachineState_RestoringSnapshot
10994 && oldState != MachineState_DeletingSnapshot
10995 && oldState != MachineState_DeletingSnapshotOnline
10996 && oldState != MachineState_DeletingSnapshotPaused
10997 )
10998 i_setMachineState(MachineState_SettingUp);
10999
11000 // use appropriate locked media map (online or offline)
11001 MediumLockListMap lockedMediaOffline;
11002 MediumLockListMap *lockedMediaMap;
11003 if (aOnline)
11004 lockedMediaMap = &mData->mSession.mLockedMedia;
11005 else
11006 lockedMediaMap = &lockedMediaOffline;
11007
11008 try
11009 {
11010 if (!aOnline)
11011 {
11012 /* lock all attached hard disks early to detect "in use"
11013 * situations before deleting actual diffs */
11014 for (MediumAttachmentList::const_iterator
11015 it = mMediumAttachments->begin();
11016 it != mMediumAttachments->end();
11017 ++it)
11018 {
11019 MediumAttachment *pAtt = *it;
11020 if (pAtt->i_getType() == DeviceType_HardDisk)
11021 {
11022 Medium *pMedium = pAtt->i_getMedium();
11023 Assert(pMedium);
11024
11025 MediumLockList *pMediumLockList(new MediumLockList());
11026 alock.release();
11027 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11028 NULL /* pToLockWrite */,
11029 false /* fMediumLockWriteAll */,
11030 NULL,
11031 *pMediumLockList);
11032 alock.acquire();
11033
11034 if (FAILED(rc))
11035 {
11036 delete pMediumLockList;
11037 throw rc;
11038 }
11039
11040 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11041 if (FAILED(rc))
11042 throw rc;
11043 }
11044 }
11045
11046 if (FAILED(rc))
11047 throw rc;
11048 } // end of offline
11049
11050 /* Lock lists are now up to date and include implicitly created media */
11051
11052 /* Go through remembered attachments and delete all implicitly created
11053 * diffs and fix up the attachment information */
11054 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11055 MediumAttachmentList implicitAtts;
11056 for (MediumAttachmentList::const_iterator
11057 it = mMediumAttachments->begin();
11058 it != mMediumAttachments->end();
11059 ++it)
11060 {
11061 ComObjPtr<MediumAttachment> pAtt = *it;
11062 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11063 if (pMedium.isNull())
11064 continue;
11065
11066 // Implicit attachments go on the list for deletion and back references are removed.
11067 if (pAtt->i_isImplicit())
11068 {
11069 /* Deassociate and mark for deletion */
11070 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11071 rc = pMedium->i_removeBackReference(mData->mUuid);
11072 if (FAILED(rc))
11073 throw rc;
11074 implicitAtts.push_back(pAtt);
11075 continue;
11076 }
11077
11078 /* Was this medium attached before? */
11079 if (!i_findAttachment(oldAtts, pMedium))
11080 {
11081 /* no: de-associate */
11082 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11083 rc = pMedium->i_removeBackReference(mData->mUuid);
11084 if (FAILED(rc))
11085 throw rc;
11086 continue;
11087 }
11088 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11089 }
11090
11091 /* If there are implicit attachments to delete, throw away the lock
11092 * map contents (which will unlock all media) since the medium
11093 * attachments will be rolled back. Below we need to completely
11094 * recreate the lock map anyway since it is infinitely complex to
11095 * do this incrementally (would need reconstructing each attachment
11096 * change, which would be extremely hairy). */
11097 if (implicitAtts.size() != 0)
11098 {
11099 ErrorInfoKeeper eik;
11100
11101 HRESULT rc1 = lockedMediaMap->Clear();
11102 AssertComRC(rc1);
11103 }
11104
11105 /* rollback hard disk changes */
11106 mMediumAttachments.rollback();
11107
11108 MultiResult mrc(S_OK);
11109
11110 // Delete unused implicit diffs.
11111 if (implicitAtts.size() != 0)
11112 {
11113 alock.release();
11114
11115 for (MediumAttachmentList::const_iterator
11116 it = implicitAtts.begin();
11117 it != implicitAtts.end();
11118 ++it)
11119 {
11120 // Remove medium associated with this attachment.
11121 ComObjPtr<MediumAttachment> pAtt = *it;
11122 Assert(pAtt);
11123 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11124 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11125 Assert(pMedium);
11126
11127 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11128 // continue on delete failure, just collect error messages
11129 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11130 pMedium->i_getLocationFull().c_str() ));
11131 mrc = rc;
11132 }
11133 // Clear the list of deleted implicit attachments now, while not
11134 // holding the lock, as it will ultimately trigger Medium::uninit()
11135 // calls which assume that the media tree lock isn't held.
11136 implicitAtts.clear();
11137
11138 alock.acquire();
11139
11140 /* if there is a VM recreate media lock map as mentioned above,
11141 * otherwise it is a waste of time and we leave things unlocked */
11142 if (aOnline)
11143 {
11144 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11145 /* must never be NULL, but better safe than sorry */
11146 if (!pMachine.isNull())
11147 {
11148 alock.release();
11149 rc = mData->mSession.mMachine->i_lockMedia();
11150 alock.acquire();
11151 if (FAILED(rc))
11152 throw rc;
11153 }
11154 }
11155 }
11156 }
11157 catch (HRESULT aRC) {rc = aRC;}
11158
11159 if (mData->mMachineState == MachineState_SettingUp)
11160 i_setMachineState(oldState);
11161
11162 /* unlock all hard disks we locked when there is no VM */
11163 if (!aOnline)
11164 {
11165 ErrorInfoKeeper eik;
11166
11167 HRESULT rc1 = lockedMediaMap->Clear();
11168 AssertComRC(rc1);
11169 }
11170
11171 return rc;
11172}
11173
11174
11175/**
11176 * Looks through the given list of media attachments for one with the given parameters
11177 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11178 * can be searched as well if needed.
11179 *
11180 * @param ll
11181 * @param aControllerName
11182 * @param aControllerPort
11183 * @param aDevice
11184 * @return
11185 */
11186MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11187 const Utf8Str &aControllerName,
11188 LONG aControllerPort,
11189 LONG aDevice)
11190{
11191 for (MediumAttachmentList::const_iterator
11192 it = ll.begin();
11193 it != ll.end();
11194 ++it)
11195 {
11196 MediumAttachment *pAttach = *it;
11197 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11198 return pAttach;
11199 }
11200
11201 return NULL;
11202}
11203
11204/**
11205 * Looks through the given list of media attachments for one with the given parameters
11206 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11207 * can be searched as well if needed.
11208 *
11209 * @param ll
11210 * @param pMedium
11211 * @return
11212 */
11213MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11214 ComObjPtr<Medium> pMedium)
11215{
11216 for (MediumAttachmentList::const_iterator
11217 it = ll.begin();
11218 it != ll.end();
11219 ++it)
11220 {
11221 MediumAttachment *pAttach = *it;
11222 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11223 if (pMediumThis == pMedium)
11224 return pAttach;
11225 }
11226
11227 return NULL;
11228}
11229
11230/**
11231 * Looks through the given list of media attachments for one with the given parameters
11232 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11233 * can be searched as well if needed.
11234 *
11235 * @param ll
11236 * @param id
11237 * @return
11238 */
11239MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11240 Guid &id)
11241{
11242 for (MediumAttachmentList::const_iterator
11243 it = ll.begin();
11244 it != ll.end();
11245 ++it)
11246 {
11247 MediumAttachment *pAttach = *it;
11248 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11249 if (pMediumThis->i_getId() == id)
11250 return pAttach;
11251 }
11252
11253 return NULL;
11254}
11255
11256/**
11257 * Main implementation for Machine::DetachDevice. This also gets called
11258 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11259 *
11260 * @param pAttach Medium attachment to detach.
11261 * @param writeLock Machine write lock which the caller must have locked once.
11262 * This may be released temporarily in here.
11263 * @param pSnapshot If NULL, then the detachment is for the current machine.
11264 * Otherwise this is for a SnapshotMachine, and this must be
11265 * its snapshot.
11266 * @return
11267 */
11268HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11269 AutoWriteLock &writeLock,
11270 Snapshot *pSnapshot)
11271{
11272 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11273 DeviceType_T mediumType = pAttach->i_getType();
11274
11275 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11276
11277 if (pAttach->i_isImplicit())
11278 {
11279 /* attempt to implicitly delete the implicitly created diff */
11280
11281 /// @todo move the implicit flag from MediumAttachment to Medium
11282 /// and forbid any hard disk operation when it is implicit. Or maybe
11283 /// a special media state for it to make it even more simple.
11284
11285 Assert(mMediumAttachments.isBackedUp());
11286
11287 /* will release the lock before the potentially lengthy operation, so
11288 * protect with the special state */
11289 MachineState_T oldState = mData->mMachineState;
11290 i_setMachineState(MachineState_SettingUp);
11291
11292 writeLock.release();
11293
11294 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11295 true /*aWait*/,
11296 false /*aNotify*/);
11297
11298 writeLock.acquire();
11299
11300 i_setMachineState(oldState);
11301
11302 if (FAILED(rc)) return rc;
11303 }
11304
11305 i_setModified(IsModified_Storage);
11306 mMediumAttachments.backup();
11307 mMediumAttachments->remove(pAttach);
11308
11309 if (!oldmedium.isNull())
11310 {
11311 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11312 if (pSnapshot)
11313 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11314 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11315 else if (mediumType != DeviceType_HardDisk)
11316 oldmedium->i_removeBackReference(mData->mUuid);
11317 }
11318
11319 return S_OK;
11320}
11321
11322/**
11323 * Goes thru all media of the given list and
11324 *
11325 * 1) calls i_detachDevice() on each of them for this machine and
11326 * 2) adds all Medium objects found in the process to the given list,
11327 * depending on cleanupMode.
11328 *
11329 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11330 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11331 * media to the list.
11332 *
11333 * This gets called from Machine::Unregister, both for the actual Machine and
11334 * the SnapshotMachine objects that might be found in the snapshots.
11335 *
11336 * Requires caller and locking. The machine lock must be passed in because it
11337 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11338 *
11339 * @param writeLock Machine lock from top-level caller; this gets passed to
11340 * i_detachDevice.
11341 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11342 * object if called for a SnapshotMachine.
11343 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11344 * added to llMedia; if Full, then all media get added;
11345 * otherwise no media get added.
11346 * @param llMedia Caller's list to receive Medium objects which got detached so
11347 * caller can close() them, depending on cleanupMode.
11348 * @return
11349 */
11350HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11351 Snapshot *pSnapshot,
11352 CleanupMode_T cleanupMode,
11353 MediaList &llMedia)
11354{
11355 Assert(isWriteLockOnCurrentThread());
11356
11357 HRESULT rc;
11358
11359 // make a temporary list because i_detachDevice invalidates iterators into
11360 // mMediumAttachments
11361 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11362
11363 for (MediumAttachmentList::iterator
11364 it = llAttachments2.begin();
11365 it != llAttachments2.end();
11366 ++it)
11367 {
11368 ComObjPtr<MediumAttachment> &pAttach = *it;
11369 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11370
11371 if (!pMedium.isNull())
11372 {
11373 AutoCaller mac(pMedium);
11374 if (FAILED(mac.rc())) return mac.rc();
11375 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11376 DeviceType_T devType = pMedium->i_getDeviceType();
11377 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11378 && devType == DeviceType_HardDisk)
11379 || (cleanupMode == CleanupMode_Full)
11380 )
11381 {
11382 llMedia.push_back(pMedium);
11383 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11384 /* Not allowed to keep this lock as below we need the parent
11385 * medium lock, and the lock order is parent to child. */
11386 lock.release();
11387 /*
11388 * Search for medias which are not attached to any machine, but
11389 * in the chain to an attached disk. Mediums are only consided
11390 * if they are:
11391 * - have only one child
11392 * - no references to any machines
11393 * - are of normal medium type
11394 */
11395 while (!pParent.isNull())
11396 {
11397 AutoCaller mac1(pParent);
11398 if (FAILED(mac1.rc())) return mac1.rc();
11399 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11400 if (pParent->i_getChildren().size() == 1)
11401 {
11402 if ( pParent->i_getMachineBackRefCount() == 0
11403 && pParent->i_getType() == MediumType_Normal
11404 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11405 llMedia.push_back(pParent);
11406 }
11407 else
11408 break;
11409 pParent = pParent->i_getParent();
11410 }
11411 }
11412 }
11413
11414 // real machine: then we need to use the proper method
11415 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11416
11417 if (FAILED(rc))
11418 return rc;
11419 }
11420
11421 return S_OK;
11422}
11423
11424/**
11425 * Perform deferred hard disk detachments.
11426 *
11427 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11428 * changed (not backed up).
11429 *
11430 * If @a aOnline is @c true then this method will also unlock the old hard
11431 * disks for which the new implicit diffs were created and will lock these new
11432 * diffs for writing.
11433 *
11434 * @param aOnline Whether the VM was online prior to this operation.
11435 *
11436 * @note Locks this object for writing!
11437 */
11438void Machine::i_commitMedia(bool aOnline /*= false*/)
11439{
11440 AutoCaller autoCaller(this);
11441 AssertComRCReturnVoid(autoCaller.rc());
11442
11443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11444
11445 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11446
11447 HRESULT rc = S_OK;
11448
11449 /* no attach/detach operations -- nothing to do */
11450 if (!mMediumAttachments.isBackedUp())
11451 return;
11452
11453 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11454 bool fMediaNeedsLocking = false;
11455
11456 /* enumerate new attachments */
11457 for (MediumAttachmentList::const_iterator
11458 it = mMediumAttachments->begin();
11459 it != mMediumAttachments->end();
11460 ++it)
11461 {
11462 MediumAttachment *pAttach = *it;
11463
11464 pAttach->i_commit();
11465
11466 Medium *pMedium = pAttach->i_getMedium();
11467 bool fImplicit = pAttach->i_isImplicit();
11468
11469 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11470 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11471 fImplicit));
11472
11473 /** @todo convert all this Machine-based voodoo to MediumAttachment
11474 * based commit logic. */
11475 if (fImplicit)
11476 {
11477 /* convert implicit attachment to normal */
11478 pAttach->i_setImplicit(false);
11479
11480 if ( aOnline
11481 && pMedium
11482 && pAttach->i_getType() == DeviceType_HardDisk
11483 )
11484 {
11485 /* update the appropriate lock list */
11486 MediumLockList *pMediumLockList;
11487 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11488 AssertComRC(rc);
11489 if (pMediumLockList)
11490 {
11491 /* unlock if there's a need to change the locking */
11492 if (!fMediaNeedsLocking)
11493 {
11494 rc = mData->mSession.mLockedMedia.Unlock();
11495 AssertComRC(rc);
11496 fMediaNeedsLocking = true;
11497 }
11498 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11499 AssertComRC(rc);
11500 rc = pMediumLockList->Append(pMedium, true);
11501 AssertComRC(rc);
11502 }
11503 }
11504
11505 continue;
11506 }
11507
11508 if (pMedium)
11509 {
11510 /* was this medium attached before? */
11511 for (MediumAttachmentList::iterator
11512 oldIt = oldAtts.begin();
11513 oldIt != oldAtts.end();
11514 ++oldIt)
11515 {
11516 MediumAttachment *pOldAttach = *oldIt;
11517 if (pOldAttach->i_getMedium() == pMedium)
11518 {
11519 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11520
11521 /* yes: remove from old to avoid de-association */
11522 oldAtts.erase(oldIt);
11523 break;
11524 }
11525 }
11526 }
11527 }
11528
11529 /* enumerate remaining old attachments and de-associate from the
11530 * current machine state */
11531 for (MediumAttachmentList::const_iterator
11532 it = oldAtts.begin();
11533 it != oldAtts.end();
11534 ++it)
11535 {
11536 MediumAttachment *pAttach = *it;
11537 Medium *pMedium = pAttach->i_getMedium();
11538
11539 /* Detach only hard disks, since DVD/floppy media is detached
11540 * instantly in MountMedium. */
11541 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11542 {
11543 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11544
11545 /* now de-associate from the current machine state */
11546 rc = pMedium->i_removeBackReference(mData->mUuid);
11547 AssertComRC(rc);
11548
11549 if (aOnline)
11550 {
11551 /* unlock since medium is not used anymore */
11552 MediumLockList *pMediumLockList;
11553 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11554 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11555 {
11556 /* this happens for online snapshots, there the attachment
11557 * is changing, but only to a diff image created under
11558 * the old one, so there is no separate lock list */
11559 Assert(!pMediumLockList);
11560 }
11561 else
11562 {
11563 AssertComRC(rc);
11564 if (pMediumLockList)
11565 {
11566 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11567 AssertComRC(rc);
11568 }
11569 }
11570 }
11571 }
11572 }
11573
11574 /* take media locks again so that the locking state is consistent */
11575 if (fMediaNeedsLocking)
11576 {
11577 Assert(aOnline);
11578 rc = mData->mSession.mLockedMedia.Lock();
11579 AssertComRC(rc);
11580 }
11581
11582 /* commit the hard disk changes */
11583 mMediumAttachments.commit();
11584
11585 if (i_isSessionMachine())
11586 {
11587 /*
11588 * Update the parent machine to point to the new owner.
11589 * This is necessary because the stored parent will point to the
11590 * session machine otherwise and cause crashes or errors later
11591 * when the session machine gets invalid.
11592 */
11593 /** @todo Change the MediumAttachment class to behave like any other
11594 * class in this regard by creating peer MediumAttachment
11595 * objects for session machines and share the data with the peer
11596 * machine.
11597 */
11598 for (MediumAttachmentList::const_iterator
11599 it = mMediumAttachments->begin();
11600 it != mMediumAttachments->end();
11601 ++it)
11602 (*it)->i_updateParentMachine(mPeer);
11603
11604 /* attach new data to the primary machine and reshare it */
11605 mPeer->mMediumAttachments.attach(mMediumAttachments);
11606 }
11607
11608 return;
11609}
11610
11611/**
11612 * Perform deferred deletion of implicitly created diffs.
11613 *
11614 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11615 * changed (not backed up).
11616 *
11617 * @note Locks this object for writing!
11618 */
11619void Machine::i_rollbackMedia()
11620{
11621 AutoCaller autoCaller(this);
11622 AssertComRCReturnVoid(autoCaller.rc());
11623
11624 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11625 LogFlowThisFunc(("Entering rollbackMedia\n"));
11626
11627 HRESULT rc = S_OK;
11628
11629 /* no attach/detach operations -- nothing to do */
11630 if (!mMediumAttachments.isBackedUp())
11631 return;
11632
11633 /* enumerate new attachments */
11634 for (MediumAttachmentList::const_iterator
11635 it = mMediumAttachments->begin();
11636 it != mMediumAttachments->end();
11637 ++it)
11638 {
11639 MediumAttachment *pAttach = *it;
11640 /* Fix up the backrefs for DVD/floppy media. */
11641 if (pAttach->i_getType() != DeviceType_HardDisk)
11642 {
11643 Medium *pMedium = pAttach->i_getMedium();
11644 if (pMedium)
11645 {
11646 rc = pMedium->i_removeBackReference(mData->mUuid);
11647 AssertComRC(rc);
11648 }
11649 }
11650
11651 (*it)->i_rollback();
11652
11653 pAttach = *it;
11654 /* Fix up the backrefs for DVD/floppy media. */
11655 if (pAttach->i_getType() != DeviceType_HardDisk)
11656 {
11657 Medium *pMedium = pAttach->i_getMedium();
11658 if (pMedium)
11659 {
11660 rc = pMedium->i_addBackReference(mData->mUuid);
11661 AssertComRC(rc);
11662 }
11663 }
11664 }
11665
11666 /** @todo convert all this Machine-based voodoo to MediumAttachment
11667 * based rollback logic. */
11668 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11669
11670 return;
11671}
11672
11673/**
11674 * Returns true if the settings file is located in the directory named exactly
11675 * as the machine; this means, among other things, that the machine directory
11676 * should be auto-renamed.
11677 *
11678 * @param aSettingsDir if not NULL, the full machine settings file directory
11679 * name will be assigned there.
11680 *
11681 * @note Doesn't lock anything.
11682 * @note Not thread safe (must be called from this object's lock).
11683 */
11684bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11685{
11686 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11687 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11688 if (aSettingsDir)
11689 *aSettingsDir = strMachineDirName;
11690 strMachineDirName.stripPath(); // vmname
11691 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11692 strConfigFileOnly.stripPath() // vmname.vbox
11693 .stripSuffix(); // vmname
11694 /** @todo hack, make somehow use of ComposeMachineFilename */
11695 if (mUserData->s.fDirectoryIncludesUUID)
11696 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11697
11698 AssertReturn(!strMachineDirName.isEmpty(), false);
11699 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11700
11701 return strMachineDirName == strConfigFileOnly;
11702}
11703
11704/**
11705 * Discards all changes to machine settings.
11706 *
11707 * @param aNotify Whether to notify the direct session about changes or not.
11708 *
11709 * @note Locks objects for writing!
11710 */
11711void Machine::i_rollback(bool aNotify)
11712{
11713 AutoCaller autoCaller(this);
11714 AssertComRCReturn(autoCaller.rc(), (void)0);
11715
11716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11717
11718 if (!mStorageControllers.isNull())
11719 {
11720 if (mStorageControllers.isBackedUp())
11721 {
11722 /* unitialize all new devices (absent in the backed up list). */
11723 StorageControllerList *backedList = mStorageControllers.backedUpData();
11724 for (StorageControllerList::const_iterator
11725 it = mStorageControllers->begin();
11726 it != mStorageControllers->end();
11727 ++it)
11728 {
11729 if ( std::find(backedList->begin(), backedList->end(), *it)
11730 == backedList->end()
11731 )
11732 {
11733 (*it)->uninit();
11734 }
11735 }
11736
11737 /* restore the list */
11738 mStorageControllers.rollback();
11739 }
11740
11741 /* rollback any changes to devices after restoring the list */
11742 if (mData->flModifications & IsModified_Storage)
11743 {
11744 for (StorageControllerList::const_iterator
11745 it = mStorageControllers->begin();
11746 it != mStorageControllers->end();
11747 ++it)
11748 {
11749 (*it)->i_rollback();
11750 }
11751 }
11752 }
11753
11754 if (!mUSBControllers.isNull())
11755 {
11756 if (mUSBControllers.isBackedUp())
11757 {
11758 /* unitialize all new devices (absent in the backed up list). */
11759 USBControllerList *backedList = mUSBControllers.backedUpData();
11760 for (USBControllerList::const_iterator
11761 it = mUSBControllers->begin();
11762 it != mUSBControllers->end();
11763 ++it)
11764 {
11765 if ( std::find(backedList->begin(), backedList->end(), *it)
11766 == backedList->end()
11767 )
11768 {
11769 (*it)->uninit();
11770 }
11771 }
11772
11773 /* restore the list */
11774 mUSBControllers.rollback();
11775 }
11776
11777 /* rollback any changes to devices after restoring the list */
11778 if (mData->flModifications & IsModified_USB)
11779 {
11780 for (USBControllerList::const_iterator
11781 it = mUSBControllers->begin();
11782 it != mUSBControllers->end();
11783 ++it)
11784 {
11785 (*it)->i_rollback();
11786 }
11787 }
11788 }
11789
11790 mUserData.rollback();
11791
11792 mHWData.rollback();
11793
11794 if (mData->flModifications & IsModified_Storage)
11795 i_rollbackMedia();
11796
11797 if (mBIOSSettings)
11798 mBIOSSettings->i_rollback();
11799
11800 if (mTrustedPlatformModule)
11801 mTrustedPlatformModule->i_rollback();
11802
11803 if (mNvramStore)
11804 mNvramStore->i_rollback();
11805
11806 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11807 mRecordingSettings->i_rollback();
11808
11809 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11810 mGraphicsAdapter->i_rollback();
11811
11812 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11813 mVRDEServer->i_rollback();
11814
11815 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11816 mAudioAdapter->i_rollback();
11817
11818 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11819 mUSBDeviceFilters->i_rollback();
11820
11821 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11822 mBandwidthControl->i_rollback();
11823
11824 if (!mHWData.isNull())
11825 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11826 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11827 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11828 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11829
11830 if (mData->flModifications & IsModified_NetworkAdapters)
11831 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11832 if ( mNetworkAdapters[slot]
11833 && mNetworkAdapters[slot]->i_isModified())
11834 {
11835 mNetworkAdapters[slot]->i_rollback();
11836 networkAdapters[slot] = mNetworkAdapters[slot];
11837 }
11838
11839 if (mData->flModifications & IsModified_SerialPorts)
11840 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11841 if ( mSerialPorts[slot]
11842 && mSerialPorts[slot]->i_isModified())
11843 {
11844 mSerialPorts[slot]->i_rollback();
11845 serialPorts[slot] = mSerialPorts[slot];
11846 }
11847
11848 if (mData->flModifications & IsModified_ParallelPorts)
11849 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11850 if ( mParallelPorts[slot]
11851 && mParallelPorts[slot]->i_isModified())
11852 {
11853 mParallelPorts[slot]->i_rollback();
11854 parallelPorts[slot] = mParallelPorts[slot];
11855 }
11856
11857 if (aNotify)
11858 {
11859 /* inform the direct session about changes */
11860
11861 ComObjPtr<Machine> that = this;
11862 uint32_t flModifications = mData->flModifications;
11863 alock.release();
11864
11865 if (flModifications & IsModified_SharedFolders)
11866 that->i_onSharedFolderChange();
11867
11868 if (flModifications & IsModified_VRDEServer)
11869 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11870 if (flModifications & IsModified_USB)
11871 that->i_onUSBControllerChange();
11872
11873 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11874 if (networkAdapters[slot])
11875 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11876 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11877 if (serialPorts[slot])
11878 that->i_onSerialPortChange(serialPorts[slot]);
11879 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11880 if (parallelPorts[slot])
11881 that->i_onParallelPortChange(parallelPorts[slot]);
11882
11883 if (flModifications & IsModified_Storage)
11884 {
11885 for (StorageControllerList::const_iterator
11886 it = mStorageControllers->begin();
11887 it != mStorageControllers->end();
11888 ++it)
11889 {
11890 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11891 }
11892 }
11893
11894
11895#if 0
11896 if (flModifications & IsModified_BandwidthControl)
11897 that->onBandwidthControlChange();
11898#endif
11899 }
11900}
11901
11902/**
11903 * Commits all the changes to machine settings.
11904 *
11905 * Note that this operation is supposed to never fail.
11906 *
11907 * @note Locks this object and children for writing.
11908 */
11909void Machine::i_commit()
11910{
11911 AutoCaller autoCaller(this);
11912 AssertComRCReturnVoid(autoCaller.rc());
11913
11914 AutoCaller peerCaller(mPeer);
11915 AssertComRCReturnVoid(peerCaller.rc());
11916
11917 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11918
11919 /*
11920 * use safe commit to ensure Snapshot machines (that share mUserData)
11921 * will still refer to a valid memory location
11922 */
11923 mUserData.commitCopy();
11924
11925 mHWData.commit();
11926
11927 if (mMediumAttachments.isBackedUp())
11928 i_commitMedia(Global::IsOnline(mData->mMachineState));
11929
11930 mBIOSSettings->i_commit();
11931 mTrustedPlatformModule->i_commit();
11932 mNvramStore->i_commit();
11933 mRecordingSettings->i_commit();
11934 mGraphicsAdapter->i_commit();
11935 mVRDEServer->i_commit();
11936 mAudioAdapter->i_commit();
11937 mUSBDeviceFilters->i_commit();
11938 mBandwidthControl->i_commit();
11939
11940 /* Since mNetworkAdapters is a list which might have been changed (resized)
11941 * without using the Backupable<> template we need to handle the copying
11942 * of the list entries manually, including the creation of peers for the
11943 * new objects. */
11944 bool commitNetworkAdapters = false;
11945 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11946 if (mPeer)
11947 {
11948 /* commit everything, even the ones which will go away */
11949 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11950 mNetworkAdapters[slot]->i_commit();
11951 /* copy over the new entries, creating a peer and uninit the original */
11952 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11953 for (size_t slot = 0; slot < newSize; slot++)
11954 {
11955 /* look if this adapter has a peer device */
11956 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11957 if (!peer)
11958 {
11959 /* no peer means the adapter is a newly created one;
11960 * create a peer owning data this data share it with */
11961 peer.createObject();
11962 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11963 }
11964 mPeer->mNetworkAdapters[slot] = peer;
11965 }
11966 /* uninit any no longer needed network adapters */
11967 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11968 mNetworkAdapters[slot]->uninit();
11969 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11970 {
11971 if (mPeer->mNetworkAdapters[slot])
11972 mPeer->mNetworkAdapters[slot]->uninit();
11973 }
11974 /* Keep the original network adapter count until this point, so that
11975 * discarding a chipset type change will not lose settings. */
11976 mNetworkAdapters.resize(newSize);
11977 mPeer->mNetworkAdapters.resize(newSize);
11978 }
11979 else
11980 {
11981 /* we have no peer (our parent is the newly created machine);
11982 * just commit changes to the network adapters */
11983 commitNetworkAdapters = true;
11984 }
11985 if (commitNetworkAdapters)
11986 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11987 mNetworkAdapters[slot]->i_commit();
11988
11989 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11990 mSerialPorts[slot]->i_commit();
11991 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11992 mParallelPorts[slot]->i_commit();
11993
11994 bool commitStorageControllers = false;
11995
11996 if (mStorageControllers.isBackedUp())
11997 {
11998 mStorageControllers.commit();
11999
12000 if (mPeer)
12001 {
12002 /* Commit all changes to new controllers (this will reshare data with
12003 * peers for those who have peers) */
12004 StorageControllerList *newList = new StorageControllerList();
12005 for (StorageControllerList::const_iterator
12006 it = mStorageControllers->begin();
12007 it != mStorageControllers->end();
12008 ++it)
12009 {
12010 (*it)->i_commit();
12011
12012 /* look if this controller has a peer device */
12013 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12014 if (!peer)
12015 {
12016 /* no peer means the device is a newly created one;
12017 * create a peer owning data this device share it with */
12018 peer.createObject();
12019 peer->init(mPeer, *it, true /* aReshare */);
12020 }
12021 else
12022 {
12023 /* remove peer from the old list */
12024 mPeer->mStorageControllers->remove(peer);
12025 }
12026 /* and add it to the new list */
12027 newList->push_back(peer);
12028 }
12029
12030 /* uninit old peer's controllers that are left */
12031 for (StorageControllerList::const_iterator
12032 it = mPeer->mStorageControllers->begin();
12033 it != mPeer->mStorageControllers->end();
12034 ++it)
12035 {
12036 (*it)->uninit();
12037 }
12038
12039 /* attach new list of controllers to our peer */
12040 mPeer->mStorageControllers.attach(newList);
12041 }
12042 else
12043 {
12044 /* we have no peer (our parent is the newly created machine);
12045 * just commit changes to devices */
12046 commitStorageControllers = true;
12047 }
12048 }
12049 else
12050 {
12051 /* the list of controllers itself is not changed,
12052 * just commit changes to controllers themselves */
12053 commitStorageControllers = true;
12054 }
12055
12056 if (commitStorageControllers)
12057 {
12058 for (StorageControllerList::const_iterator
12059 it = mStorageControllers->begin();
12060 it != mStorageControllers->end();
12061 ++it)
12062 {
12063 (*it)->i_commit();
12064 }
12065 }
12066
12067 bool commitUSBControllers = false;
12068
12069 if (mUSBControllers.isBackedUp())
12070 {
12071 mUSBControllers.commit();
12072
12073 if (mPeer)
12074 {
12075 /* Commit all changes to new controllers (this will reshare data with
12076 * peers for those who have peers) */
12077 USBControllerList *newList = new USBControllerList();
12078 for (USBControllerList::const_iterator
12079 it = mUSBControllers->begin();
12080 it != mUSBControllers->end();
12081 ++it)
12082 {
12083 (*it)->i_commit();
12084
12085 /* look if this controller has a peer device */
12086 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12087 if (!peer)
12088 {
12089 /* no peer means the device is a newly created one;
12090 * create a peer owning data this device share it with */
12091 peer.createObject();
12092 peer->init(mPeer, *it, true /* aReshare */);
12093 }
12094 else
12095 {
12096 /* remove peer from the old list */
12097 mPeer->mUSBControllers->remove(peer);
12098 }
12099 /* and add it to the new list */
12100 newList->push_back(peer);
12101 }
12102
12103 /* uninit old peer's controllers that are left */
12104 for (USBControllerList::const_iterator
12105 it = mPeer->mUSBControllers->begin();
12106 it != mPeer->mUSBControllers->end();
12107 ++it)
12108 {
12109 (*it)->uninit();
12110 }
12111
12112 /* attach new list of controllers to our peer */
12113 mPeer->mUSBControllers.attach(newList);
12114 }
12115 else
12116 {
12117 /* we have no peer (our parent is the newly created machine);
12118 * just commit changes to devices */
12119 commitUSBControllers = true;
12120 }
12121 }
12122 else
12123 {
12124 /* the list of controllers itself is not changed,
12125 * just commit changes to controllers themselves */
12126 commitUSBControllers = true;
12127 }
12128
12129 if (commitUSBControllers)
12130 {
12131 for (USBControllerList::const_iterator
12132 it = mUSBControllers->begin();
12133 it != mUSBControllers->end();
12134 ++it)
12135 {
12136 (*it)->i_commit();
12137 }
12138 }
12139
12140 if (i_isSessionMachine())
12141 {
12142 /* attach new data to the primary machine and reshare it */
12143 mPeer->mUserData.attach(mUserData);
12144 mPeer->mHWData.attach(mHWData);
12145 /* mmMediumAttachments is reshared by fixupMedia */
12146 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12147 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12148 }
12149}
12150
12151/**
12152 * Copies all the hardware data from the given machine.
12153 *
12154 * Currently, only called when the VM is being restored from a snapshot. In
12155 * particular, this implies that the VM is not running during this method's
12156 * call.
12157 *
12158 * @note This method must be called from under this object's lock.
12159 *
12160 * @note This method doesn't call #i_commit(), so all data remains backed up and
12161 * unsaved.
12162 */
12163void Machine::i_copyFrom(Machine *aThat)
12164{
12165 AssertReturnVoid(!i_isSnapshotMachine());
12166 AssertReturnVoid(aThat->i_isSnapshotMachine());
12167
12168 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12169
12170 mHWData.assignCopy(aThat->mHWData);
12171
12172 // create copies of all shared folders (mHWData after attaching a copy
12173 // contains just references to original objects)
12174 for (HWData::SharedFolderList::iterator
12175 it = mHWData->mSharedFolders.begin();
12176 it != mHWData->mSharedFolders.end();
12177 ++it)
12178 {
12179 ComObjPtr<SharedFolder> folder;
12180 folder.createObject();
12181 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12182 AssertComRC(rc);
12183 *it = folder;
12184 }
12185
12186 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12187 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12188 mNvramStore->i_copyFrom(aThat->mNvramStore);
12189 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12190 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12191 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12192 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12193 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12194 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12195
12196 /* create private copies of all controllers */
12197 mStorageControllers.backup();
12198 mStorageControllers->clear();
12199 for (StorageControllerList::const_iterator
12200 it = aThat->mStorageControllers->begin();
12201 it != aThat->mStorageControllers->end();
12202 ++it)
12203 {
12204 ComObjPtr<StorageController> ctrl;
12205 ctrl.createObject();
12206 ctrl->initCopy(this, *it);
12207 mStorageControllers->push_back(ctrl);
12208 }
12209
12210 /* create private copies of all USB controllers */
12211 mUSBControllers.backup();
12212 mUSBControllers->clear();
12213 for (USBControllerList::const_iterator
12214 it = aThat->mUSBControllers->begin();
12215 it != aThat->mUSBControllers->end();
12216 ++it)
12217 {
12218 ComObjPtr<USBController> ctrl;
12219 ctrl.createObject();
12220 ctrl->initCopy(this, *it);
12221 mUSBControllers->push_back(ctrl);
12222 }
12223
12224 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12225 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12226 {
12227 if (mNetworkAdapters[slot].isNotNull())
12228 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12229 else
12230 {
12231 unconst(mNetworkAdapters[slot]).createObject();
12232 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12233 }
12234 }
12235 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12236 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12237 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12238 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12239}
12240
12241/**
12242 * Returns whether the given storage controller is hotplug capable.
12243 *
12244 * @returns true if the controller supports hotplugging
12245 * false otherwise.
12246 * @param enmCtrlType The controller type to check for.
12247 */
12248bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12249{
12250 ComPtr<ISystemProperties> systemProperties;
12251 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12252 if (FAILED(rc))
12253 return false;
12254
12255 BOOL aHotplugCapable = FALSE;
12256 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12257
12258 return RT_BOOL(aHotplugCapable);
12259}
12260
12261#ifdef VBOX_WITH_RESOURCE_USAGE_API
12262
12263void Machine::i_getDiskList(MediaList &list)
12264{
12265 for (MediumAttachmentList::const_iterator
12266 it = mMediumAttachments->begin();
12267 it != mMediumAttachments->end();
12268 ++it)
12269 {
12270 MediumAttachment *pAttach = *it;
12271 /* just in case */
12272 AssertContinue(pAttach);
12273
12274 AutoCaller localAutoCallerA(pAttach);
12275 if (FAILED(localAutoCallerA.rc())) continue;
12276
12277 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12278
12279 if (pAttach->i_getType() == DeviceType_HardDisk)
12280 list.push_back(pAttach->i_getMedium());
12281 }
12282}
12283
12284void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12285{
12286 AssertReturnVoid(isWriteLockOnCurrentThread());
12287 AssertPtrReturnVoid(aCollector);
12288
12289 pm::CollectorHAL *hal = aCollector->getHAL();
12290 /* Create sub metrics */
12291 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12292 "Percentage of processor time spent in user mode by the VM process.");
12293 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12294 "Percentage of processor time spent in kernel mode by the VM process.");
12295 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12296 "Size of resident portion of VM process in memory.");
12297 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12298 "Actual size of all VM disks combined.");
12299 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12300 "Network receive rate.");
12301 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12302 "Network transmit rate.");
12303 /* Create and register base metrics */
12304 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12305 cpuLoadUser, cpuLoadKernel);
12306 aCollector->registerBaseMetric(cpuLoad);
12307 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12308 ramUsageUsed);
12309 aCollector->registerBaseMetric(ramUsage);
12310 MediaList disks;
12311 i_getDiskList(disks);
12312 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12313 diskUsageUsed);
12314 aCollector->registerBaseMetric(diskUsage);
12315
12316 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12317 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12318 new pm::AggregateAvg()));
12319 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12320 new pm::AggregateMin()));
12321 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12322 new pm::AggregateMax()));
12323 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12324 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12325 new pm::AggregateAvg()));
12326 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12327 new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12329 new pm::AggregateMax()));
12330
12331 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12332 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12333 new pm::AggregateAvg()));
12334 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12335 new pm::AggregateMin()));
12336 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12337 new pm::AggregateMax()));
12338
12339 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12340 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12341 new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12343 new pm::AggregateMin()));
12344 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12345 new pm::AggregateMax()));
12346
12347
12348 /* Guest metrics collector */
12349 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12350 aCollector->registerGuest(mCollectorGuest);
12351 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12352
12353 /* Create sub metrics */
12354 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12355 "Percentage of processor time spent in user mode as seen by the guest.");
12356 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12357 "Percentage of processor time spent in kernel mode as seen by the guest.");
12358 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12359 "Percentage of processor time spent idling as seen by the guest.");
12360
12361 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12362 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12363 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12364 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12365 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12366 pm::SubMetric *guestMemCache = new pm::SubMetric(
12367 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12368
12369 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12370 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12371
12372 /* Create and register base metrics */
12373 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12374 machineNetRx, machineNetTx);
12375 aCollector->registerBaseMetric(machineNetRate);
12376
12377 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12378 guestLoadUser, guestLoadKernel, guestLoadIdle);
12379 aCollector->registerBaseMetric(guestCpuLoad);
12380
12381 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12382 guestMemTotal, guestMemFree,
12383 guestMemBalloon, guestMemShared,
12384 guestMemCache, guestPagedTotal);
12385 aCollector->registerBaseMetric(guestCpuMem);
12386
12387 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12388 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12389 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12390 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12391
12392 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12393 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12394 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12395 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12396
12397 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12398 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12399 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12400 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12401
12402 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12403 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12404 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12405 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12406
12407 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12408 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12409 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12410 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12411
12412 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12413 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12414 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12415 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12416
12417 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12418 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12419 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12420 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12421
12422 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12423 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12424 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12425 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12426
12427 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12428 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12429 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12430 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12431
12432 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12433 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12434 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12435 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12436
12437 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12438 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12439 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12440 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12441}
12442
12443void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12444{
12445 AssertReturnVoid(isWriteLockOnCurrentThread());
12446
12447 if (aCollector)
12448 {
12449 aCollector->unregisterMetricsFor(aMachine);
12450 aCollector->unregisterBaseMetricsFor(aMachine);
12451 }
12452}
12453
12454#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12455
12456
12457////////////////////////////////////////////////////////////////////////////////
12458
12459DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12460
12461HRESULT SessionMachine::FinalConstruct()
12462{
12463 LogFlowThisFunc(("\n"));
12464
12465 mClientToken = NULL;
12466
12467 return BaseFinalConstruct();
12468}
12469
12470void SessionMachine::FinalRelease()
12471{
12472 LogFlowThisFunc(("\n"));
12473
12474 Assert(!mClientToken);
12475 /* paranoia, should not hang around any more */
12476 if (mClientToken)
12477 {
12478 delete mClientToken;
12479 mClientToken = NULL;
12480 }
12481
12482 uninit(Uninit::Unexpected);
12483
12484 BaseFinalRelease();
12485}
12486
12487/**
12488 * @note Must be called only by Machine::LockMachine() from its own write lock.
12489 */
12490HRESULT SessionMachine::init(Machine *aMachine)
12491{
12492 LogFlowThisFuncEnter();
12493 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12494
12495 AssertReturn(aMachine, E_INVALIDARG);
12496
12497 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12498
12499 /* Enclose the state transition NotReady->InInit->Ready */
12500 AutoInitSpan autoInitSpan(this);
12501 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12502
12503 HRESULT rc = S_OK;
12504
12505 RT_ZERO(mAuthLibCtx);
12506
12507 /* create the machine client token */
12508 try
12509 {
12510 mClientToken = new ClientToken(aMachine, this);
12511 if (!mClientToken->isReady())
12512 {
12513 delete mClientToken;
12514 mClientToken = NULL;
12515 rc = E_FAIL;
12516 }
12517 }
12518 catch (std::bad_alloc &)
12519 {
12520 rc = E_OUTOFMEMORY;
12521 }
12522 if (FAILED(rc))
12523 return rc;
12524
12525 /* memorize the peer Machine */
12526 unconst(mPeer) = aMachine;
12527 /* share the parent pointer */
12528 unconst(mParent) = aMachine->mParent;
12529
12530 /* take the pointers to data to share */
12531 mData.share(aMachine->mData);
12532 mSSData.share(aMachine->mSSData);
12533
12534 mUserData.share(aMachine->mUserData);
12535 mHWData.share(aMachine->mHWData);
12536 mMediumAttachments.share(aMachine->mMediumAttachments);
12537
12538 mStorageControllers.allocate();
12539 for (StorageControllerList::const_iterator
12540 it = aMachine->mStorageControllers->begin();
12541 it != aMachine->mStorageControllers->end();
12542 ++it)
12543 {
12544 ComObjPtr<StorageController> ctl;
12545 ctl.createObject();
12546 ctl->init(this, *it);
12547 mStorageControllers->push_back(ctl);
12548 }
12549
12550 mUSBControllers.allocate();
12551 for (USBControllerList::const_iterator
12552 it = aMachine->mUSBControllers->begin();
12553 it != aMachine->mUSBControllers->end();
12554 ++it)
12555 {
12556 ComObjPtr<USBController> ctl;
12557 ctl.createObject();
12558 ctl->init(this, *it);
12559 mUSBControllers->push_back(ctl);
12560 }
12561
12562 unconst(mBIOSSettings).createObject();
12563 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12564
12565 unconst(mTrustedPlatformModule).createObject();
12566 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12567
12568 unconst(mNvramStore).createObject();
12569 mNvramStore->init(this, aMachine->mNvramStore);
12570
12571 unconst(mRecordingSettings).createObject();
12572 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12573 /* create another GraphicsAdapter object that will be mutable */
12574 unconst(mGraphicsAdapter).createObject();
12575 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12576 /* create another VRDEServer object that will be mutable */
12577 unconst(mVRDEServer).createObject();
12578 mVRDEServer->init(this, aMachine->mVRDEServer);
12579 /* create another audio adapter object that will be mutable */
12580 unconst(mAudioAdapter).createObject();
12581 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12582 /* create a list of serial ports that will be mutable */
12583 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12584 {
12585 unconst(mSerialPorts[slot]).createObject();
12586 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12587 }
12588 /* create a list of parallel ports that will be mutable */
12589 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12590 {
12591 unconst(mParallelPorts[slot]).createObject();
12592 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12593 }
12594
12595 /* create another USB device filters object that will be mutable */
12596 unconst(mUSBDeviceFilters).createObject();
12597 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12598
12599 /* create a list of network adapters that will be mutable */
12600 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12601 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12602 {
12603 unconst(mNetworkAdapters[slot]).createObject();
12604 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12605 }
12606
12607 /* create another bandwidth control object that will be mutable */
12608 unconst(mBandwidthControl).createObject();
12609 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12610
12611 /* default is to delete saved state on Saved -> PoweredOff transition */
12612 mRemoveSavedState = true;
12613
12614 /* Confirm a successful initialization when it's the case */
12615 autoInitSpan.setSucceeded();
12616
12617 miNATNetworksStarted = 0;
12618
12619 LogFlowThisFuncLeave();
12620 return rc;
12621}
12622
12623/**
12624 * Uninitializes this session object. If the reason is other than
12625 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12626 * or the client watcher code.
12627 *
12628 * @param aReason uninitialization reason
12629 *
12630 * @note Locks mParent + this object for writing.
12631 */
12632void SessionMachine::uninit(Uninit::Reason aReason)
12633{
12634 LogFlowThisFuncEnter();
12635 LogFlowThisFunc(("reason=%d\n", aReason));
12636
12637 /*
12638 * Strongly reference ourselves to prevent this object deletion after
12639 * mData->mSession.mMachine.setNull() below (which can release the last
12640 * reference and call the destructor). Important: this must be done before
12641 * accessing any members (and before AutoUninitSpan that does it as well).
12642 * This self reference will be released as the very last step on return.
12643 */
12644 ComObjPtr<SessionMachine> selfRef;
12645 if (aReason != Uninit::Unexpected)
12646 selfRef = this;
12647
12648 /* Enclose the state transition Ready->InUninit->NotReady */
12649 AutoUninitSpan autoUninitSpan(this);
12650 if (autoUninitSpan.uninitDone())
12651 {
12652 LogFlowThisFunc(("Already uninitialized\n"));
12653 LogFlowThisFuncLeave();
12654 return;
12655 }
12656
12657 if (autoUninitSpan.initFailed())
12658 {
12659 /* We've been called by init() because it's failed. It's not really
12660 * necessary (nor it's safe) to perform the regular uninit sequence
12661 * below, the following is enough.
12662 */
12663 LogFlowThisFunc(("Initialization failed.\n"));
12664 /* destroy the machine client token */
12665 if (mClientToken)
12666 {
12667 delete mClientToken;
12668 mClientToken = NULL;
12669 }
12670 uninitDataAndChildObjects();
12671 mData.free();
12672 unconst(mParent) = NULL;
12673 unconst(mPeer) = NULL;
12674 LogFlowThisFuncLeave();
12675 return;
12676 }
12677
12678 MachineState_T lastState;
12679 {
12680 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12681 lastState = mData->mMachineState;
12682 }
12683 NOREF(lastState);
12684
12685#ifdef VBOX_WITH_USB
12686 // release all captured USB devices, but do this before requesting the locks below
12687 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12688 {
12689 /* Console::captureUSBDevices() is called in the VM process only after
12690 * setting the machine state to Starting or Restoring.
12691 * Console::detachAllUSBDevices() will be called upon successful
12692 * termination. So, we need to release USB devices only if there was
12693 * an abnormal termination of a running VM.
12694 *
12695 * This is identical to SessionMachine::DetachAllUSBDevices except
12696 * for the aAbnormal argument. */
12697 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12698 AssertComRC(rc);
12699 NOREF(rc);
12700
12701 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12702 if (service)
12703 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12704 }
12705#endif /* VBOX_WITH_USB */
12706
12707 // we need to lock this object in uninit() because the lock is shared
12708 // with mPeer (as well as data we modify below). mParent lock is needed
12709 // by several calls to it.
12710 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12711
12712#ifdef VBOX_WITH_RESOURCE_USAGE_API
12713 /*
12714 * It is safe to call Machine::i_unregisterMetrics() here because
12715 * PerformanceCollector::samplerCallback no longer accesses guest methods
12716 * holding the lock.
12717 */
12718 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12719 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12720 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12721 if (mCollectorGuest)
12722 {
12723 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12724 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12725 mCollectorGuest = NULL;
12726 }
12727#endif
12728
12729 if (aReason == Uninit::Abnormal)
12730 {
12731 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12732
12733 /*
12734 * Move the VM to the 'Aborted' machine state unless we are restoring a
12735 * VM that was in the 'Saved' machine state. In that case, if the VM
12736 * fails before reaching either the 'Restoring' machine state or the
12737 * 'Running' machine state then we set the machine state to
12738 * 'AbortedSaved' in order to preserve the saved state file so that the
12739 * VM can be restored in the future.
12740 */
12741 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12742 i_setMachineState(MachineState_AbortedSaved);
12743 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12744 i_setMachineState(MachineState_Aborted);
12745 }
12746
12747 // any machine settings modified?
12748 if (mData->flModifications)
12749 {
12750 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12751 i_rollback(false /* aNotify */);
12752 }
12753
12754 mData->mSession.mPID = NIL_RTPROCESS;
12755
12756 if (aReason == Uninit::Unexpected)
12757 {
12758 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12759 * client watcher thread to update the set of machines that have open
12760 * sessions. */
12761 mParent->i_updateClientWatcher();
12762 }
12763
12764 /* uninitialize all remote controls */
12765 if (mData->mSession.mRemoteControls.size())
12766 {
12767 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12768 mData->mSession.mRemoteControls.size()));
12769
12770 /* Always restart a the beginning, since the iterator is invalidated
12771 * by using erase(). */
12772 for (Data::Session::RemoteControlList::iterator
12773 it = mData->mSession.mRemoteControls.begin();
12774 it != mData->mSession.mRemoteControls.end();
12775 it = mData->mSession.mRemoteControls.begin())
12776 {
12777 ComPtr<IInternalSessionControl> pControl = *it;
12778 mData->mSession.mRemoteControls.erase(it);
12779 multilock.release();
12780 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12781 HRESULT rc = pControl->Uninitialize();
12782 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12783 if (FAILED(rc))
12784 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12785 multilock.acquire();
12786 }
12787 mData->mSession.mRemoteControls.clear();
12788 }
12789
12790 /* Remove all references to the NAT network service. The service will stop
12791 * if all references (also from other VMs) are removed. */
12792 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12793 {
12794 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12795 {
12796 BOOL enabled;
12797 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12798 if ( FAILED(hrc)
12799 || !enabled)
12800 continue;
12801
12802 NetworkAttachmentType_T type;
12803 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12804 if ( SUCCEEDED(hrc)
12805 && type == NetworkAttachmentType_NATNetwork)
12806 {
12807 Bstr name;
12808 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12809 if (SUCCEEDED(hrc))
12810 {
12811 multilock.release();
12812 Utf8Str strName(name);
12813 LogRel(("VM '%s' stops using NAT network '%s'\n",
12814 mUserData->s.strName.c_str(), strName.c_str()));
12815 mParent->i_natNetworkRefDec(strName);
12816 multilock.acquire();
12817 }
12818 }
12819 }
12820 }
12821
12822 /*
12823 * An expected uninitialization can come only from #i_checkForDeath().
12824 * Otherwise it means that something's gone really wrong (for example,
12825 * the Session implementation has released the VirtualBox reference
12826 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12827 * etc). However, it's also possible, that the client releases the IPC
12828 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12829 * but the VirtualBox release event comes first to the server process.
12830 * This case is practically possible, so we should not assert on an
12831 * unexpected uninit, just log a warning.
12832 */
12833
12834 if (aReason == Uninit::Unexpected)
12835 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12836
12837 if (aReason != Uninit::Normal)
12838 {
12839 mData->mSession.mDirectControl.setNull();
12840 }
12841 else
12842 {
12843 /* this must be null here (see #OnSessionEnd()) */
12844 Assert(mData->mSession.mDirectControl.isNull());
12845 Assert(mData->mSession.mState == SessionState_Unlocking);
12846 Assert(!mData->mSession.mProgress.isNull());
12847 }
12848 if (mData->mSession.mProgress)
12849 {
12850 if (aReason == Uninit::Normal)
12851 mData->mSession.mProgress->i_notifyComplete(S_OK);
12852 else
12853 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12854 COM_IIDOF(ISession),
12855 getComponentName(),
12856 tr("The VM session was aborted"));
12857 mData->mSession.mProgress.setNull();
12858 }
12859
12860 if (mConsoleTaskData.mProgress)
12861 {
12862 Assert(aReason == Uninit::Abnormal);
12863 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12864 COM_IIDOF(ISession),
12865 getComponentName(),
12866 tr("The VM session was aborted"));
12867 mConsoleTaskData.mProgress.setNull();
12868 }
12869
12870 /* remove the association between the peer machine and this session machine */
12871 Assert( (SessionMachine*)mData->mSession.mMachine == this
12872 || aReason == Uninit::Unexpected);
12873
12874 /* reset the rest of session data */
12875 mData->mSession.mLockType = LockType_Null;
12876 mData->mSession.mMachine.setNull();
12877 mData->mSession.mState = SessionState_Unlocked;
12878 mData->mSession.mName.setNull();
12879
12880 /* destroy the machine client token before leaving the exclusive lock */
12881 if (mClientToken)
12882 {
12883 delete mClientToken;
12884 mClientToken = NULL;
12885 }
12886
12887 /* fire an event */
12888 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12889
12890 uninitDataAndChildObjects();
12891
12892 /* free the essential data structure last */
12893 mData.free();
12894
12895 /* release the exclusive lock before setting the below two to NULL */
12896 multilock.release();
12897
12898 unconst(mParent) = NULL;
12899 unconst(mPeer) = NULL;
12900
12901 AuthLibUnload(&mAuthLibCtx);
12902
12903 LogFlowThisFuncLeave();
12904}
12905
12906// util::Lockable interface
12907////////////////////////////////////////////////////////////////////////////////
12908
12909/**
12910 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12911 * with the primary Machine instance (mPeer).
12912 */
12913RWLockHandle *SessionMachine::lockHandle() const
12914{
12915 AssertReturn(mPeer != NULL, NULL);
12916 return mPeer->lockHandle();
12917}
12918
12919// IInternalMachineControl methods
12920////////////////////////////////////////////////////////////////////////////////
12921
12922/**
12923 * Passes collected guest statistics to performance collector object
12924 */
12925HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12926 ULONG aCpuKernel, ULONG aCpuIdle,
12927 ULONG aMemTotal, ULONG aMemFree,
12928 ULONG aMemBalloon, ULONG aMemShared,
12929 ULONG aMemCache, ULONG aPageTotal,
12930 ULONG aAllocVMM, ULONG aFreeVMM,
12931 ULONG aBalloonedVMM, ULONG aSharedVMM,
12932 ULONG aVmNetRx, ULONG aVmNetTx)
12933{
12934#ifdef VBOX_WITH_RESOURCE_USAGE_API
12935 if (mCollectorGuest)
12936 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12937 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12938 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12939 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12940
12941 return S_OK;
12942#else
12943 NOREF(aValidStats);
12944 NOREF(aCpuUser);
12945 NOREF(aCpuKernel);
12946 NOREF(aCpuIdle);
12947 NOREF(aMemTotal);
12948 NOREF(aMemFree);
12949 NOREF(aMemBalloon);
12950 NOREF(aMemShared);
12951 NOREF(aMemCache);
12952 NOREF(aPageTotal);
12953 NOREF(aAllocVMM);
12954 NOREF(aFreeVMM);
12955 NOREF(aBalloonedVMM);
12956 NOREF(aSharedVMM);
12957 NOREF(aVmNetRx);
12958 NOREF(aVmNetTx);
12959 return E_NOTIMPL;
12960#endif
12961}
12962
12963////////////////////////////////////////////////////////////////////////////////
12964//
12965// SessionMachine task records
12966//
12967////////////////////////////////////////////////////////////////////////////////
12968
12969/**
12970 * Task record for saving the machine state.
12971 */
12972class SessionMachine::SaveStateTask
12973 : public Machine::Task
12974{
12975public:
12976 SaveStateTask(SessionMachine *m,
12977 Progress *p,
12978 const Utf8Str &t,
12979 Reason_T enmReason,
12980 const Utf8Str &strStateFilePath)
12981 : Task(m, p, t),
12982 m_enmReason(enmReason),
12983 m_strStateFilePath(strStateFilePath)
12984 {}
12985
12986private:
12987 void handler()
12988 {
12989 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12990 }
12991
12992 Reason_T m_enmReason;
12993 Utf8Str m_strStateFilePath;
12994
12995 friend class SessionMachine;
12996};
12997
12998/**
12999 * Task thread implementation for SessionMachine::SaveState(), called from
13000 * SessionMachine::taskHandler().
13001 *
13002 * @note Locks this object for writing.
13003 *
13004 * @param task
13005 * @return
13006 */
13007void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13008{
13009 LogFlowThisFuncEnter();
13010
13011 AutoCaller autoCaller(this);
13012 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13013 if (FAILED(autoCaller.rc()))
13014 {
13015 /* we might have been uninitialized because the session was accidentally
13016 * closed by the client, so don't assert */
13017 HRESULT rc = setError(E_FAIL,
13018 tr("The session has been accidentally closed"));
13019 task.m_pProgress->i_notifyComplete(rc);
13020 LogFlowThisFuncLeave();
13021 return;
13022 }
13023
13024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13025
13026 HRESULT rc = S_OK;
13027
13028 try
13029 {
13030 ComPtr<IInternalSessionControl> directControl;
13031 if (mData->mSession.mLockType == LockType_VM)
13032 directControl = mData->mSession.mDirectControl;
13033 if (directControl.isNull())
13034 throw setError(VBOX_E_INVALID_VM_STATE,
13035 tr("Trying to save state without a running VM"));
13036 alock.release();
13037 BOOL fSuspendedBySave;
13038 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13039 Assert(!fSuspendedBySave);
13040 alock.acquire();
13041
13042 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13043 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13044 throw E_FAIL);
13045
13046 if (SUCCEEDED(rc))
13047 {
13048 mSSData->strStateFilePath = task.m_strStateFilePath;
13049
13050 /* save all VM settings */
13051 rc = i_saveSettings(NULL, alock);
13052 // no need to check whether VirtualBox.xml needs saving also since
13053 // we can't have a name change pending at this point
13054 }
13055 else
13056 {
13057 // On failure, set the state to the state we had at the beginning.
13058 i_setMachineState(task.m_machineStateBackup);
13059 i_updateMachineStateOnClient();
13060
13061 // Delete the saved state file (might have been already created).
13062 // No need to check whether this is shared with a snapshot here
13063 // because we certainly created a fresh saved state file here.
13064 RTFileDelete(task.m_strStateFilePath.c_str());
13065 }
13066 }
13067 catch (HRESULT aRC) { rc = aRC; }
13068
13069 task.m_pProgress->i_notifyComplete(rc);
13070
13071 LogFlowThisFuncLeave();
13072}
13073
13074/**
13075 * @note Locks this object for writing.
13076 */
13077HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13078{
13079 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13080}
13081
13082HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13083{
13084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13085
13086 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13087 if (FAILED(rc)) return rc;
13088
13089 if ( mData->mMachineState != MachineState_Running
13090 && mData->mMachineState != MachineState_Paused
13091 )
13092 return setError(VBOX_E_INVALID_VM_STATE,
13093 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13094 Global::stringifyMachineState(mData->mMachineState));
13095
13096 ComObjPtr<Progress> pProgress;
13097 pProgress.createObject();
13098 rc = pProgress->init(i_getVirtualBox(),
13099 static_cast<IMachine *>(this) /* aInitiator */,
13100 tr("Saving the execution state of the virtual machine"),
13101 FALSE /* aCancelable */);
13102 if (FAILED(rc))
13103 return rc;
13104
13105 Utf8Str strStateFilePath;
13106 i_composeSavedStateFilename(strStateFilePath);
13107
13108 /* create and start the task on a separate thread (note that it will not
13109 * start working until we release alock) */
13110 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13111 rc = pTask->createThread();
13112 if (FAILED(rc))
13113 return rc;
13114
13115 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13116 i_setMachineState(MachineState_Saving);
13117 i_updateMachineStateOnClient();
13118
13119 pProgress.queryInterfaceTo(aProgress.asOutParam());
13120
13121 return S_OK;
13122}
13123
13124/**
13125 * @note Locks this object for writing.
13126 */
13127HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13128{
13129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13130
13131 HRESULT rc = i_checkStateDependency(MutableStateDep);
13132 if (FAILED(rc)) return rc;
13133
13134 if ( mData->mMachineState != MachineState_PoweredOff
13135 && mData->mMachineState != MachineState_Teleported
13136 && mData->mMachineState != MachineState_Aborted
13137 )
13138 return setError(VBOX_E_INVALID_VM_STATE,
13139 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13140 Global::stringifyMachineState(mData->mMachineState));
13141
13142 com::Utf8Str stateFilePathFull;
13143 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13144 if (RT_FAILURE(vrc))
13145 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13146 tr("Invalid saved state file path '%s' (%Rrc)"),
13147 aSavedStateFile.c_str(),
13148 vrc);
13149
13150 mSSData->strStateFilePath = stateFilePathFull;
13151
13152 /* The below i_setMachineState() will detect the state transition and will
13153 * update the settings file */
13154
13155 return i_setMachineState(MachineState_Saved);
13156}
13157
13158/**
13159 * @note Locks this object for writing.
13160 */
13161HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13162{
13163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13164
13165 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13166 if (FAILED(rc)) return rc;
13167
13168 if ( mData->mMachineState != MachineState_Saved
13169 && mData->mMachineState != MachineState_AbortedSaved)
13170 return setError(VBOX_E_INVALID_VM_STATE,
13171 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13172 Global::stringifyMachineState(mData->mMachineState));
13173
13174 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13175
13176 /*
13177 * Saved -> PoweredOff transition will be detected in the SessionMachine
13178 * and properly handled.
13179 */
13180 rc = i_setMachineState(MachineState_PoweredOff);
13181 return rc;
13182}
13183
13184
13185/**
13186 * @note Locks the same as #i_setMachineState() does.
13187 */
13188HRESULT SessionMachine::updateState(MachineState_T aState)
13189{
13190 return i_setMachineState(aState);
13191}
13192
13193/**
13194 * @note Locks this object for writing.
13195 */
13196HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13197{
13198 IProgress *pProgress(aProgress);
13199
13200 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13201
13202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13203
13204 if (mData->mSession.mState != SessionState_Locked)
13205 return VBOX_E_INVALID_OBJECT_STATE;
13206
13207 if (!mData->mSession.mProgress.isNull())
13208 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13209
13210 /* If we didn't reference the NAT network service yet, add a reference to
13211 * force a start */
13212 if (miNATNetworksStarted < 1)
13213 {
13214 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13215 {
13216 BOOL enabled;
13217 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13218 if ( FAILED(hrc)
13219 || !enabled)
13220 continue;
13221
13222 NetworkAttachmentType_T type;
13223 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13224 if ( SUCCEEDED(hrc)
13225 && type == NetworkAttachmentType_NATNetwork)
13226 {
13227 Bstr name;
13228 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13229 if (SUCCEEDED(hrc))
13230 {
13231 Utf8Str strName(name);
13232 LogRel(("VM '%s' starts using NAT network '%s'\n",
13233 mUserData->s.strName.c_str(), strName.c_str()));
13234 mPeer->lockHandle()->unlockWrite();
13235 mParent->i_natNetworkRefInc(strName);
13236#ifdef RT_LOCK_STRICT
13237 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13238#else
13239 mPeer->lockHandle()->lockWrite();
13240#endif
13241 }
13242 }
13243 }
13244 miNATNetworksStarted++;
13245 }
13246
13247 LogFlowThisFunc(("returns S_OK.\n"));
13248 return S_OK;
13249}
13250
13251/**
13252 * @note Locks this object for writing.
13253 */
13254HRESULT SessionMachine::endPowerUp(LONG aResult)
13255{
13256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13257
13258 if (mData->mSession.mState != SessionState_Locked)
13259 return VBOX_E_INVALID_OBJECT_STATE;
13260
13261 /* Finalize the LaunchVMProcess progress object. */
13262 if (mData->mSession.mProgress)
13263 {
13264 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13265 mData->mSession.mProgress.setNull();
13266 }
13267
13268 if (SUCCEEDED((HRESULT)aResult))
13269 {
13270#ifdef VBOX_WITH_RESOURCE_USAGE_API
13271 /* The VM has been powered up successfully, so it makes sense
13272 * now to offer the performance metrics for a running machine
13273 * object. Doing it earlier wouldn't be safe. */
13274 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13275 mData->mSession.mPID);
13276#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13277 }
13278
13279 return S_OK;
13280}
13281
13282/**
13283 * @note Locks this object for writing.
13284 */
13285HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13286{
13287 LogFlowThisFuncEnter();
13288
13289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13290
13291 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13292 E_FAIL);
13293
13294 /* create a progress object to track operation completion */
13295 ComObjPtr<Progress> pProgress;
13296 pProgress.createObject();
13297 pProgress->init(i_getVirtualBox(),
13298 static_cast<IMachine *>(this) /* aInitiator */,
13299 tr("Stopping the virtual machine"),
13300 FALSE /* aCancelable */);
13301
13302 /* fill in the console task data */
13303 mConsoleTaskData.mLastState = mData->mMachineState;
13304 mConsoleTaskData.mProgress = pProgress;
13305
13306 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13307 i_setMachineState(MachineState_Stopping);
13308
13309 pProgress.queryInterfaceTo(aProgress.asOutParam());
13310
13311 return S_OK;
13312}
13313
13314/**
13315 * @note Locks this object for writing.
13316 */
13317HRESULT SessionMachine::endPoweringDown(LONG aResult,
13318 const com::Utf8Str &aErrMsg)
13319{
13320 HRESULT const hrcResult = (HRESULT)aResult;
13321 LogFlowThisFuncEnter();
13322
13323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13324
13325 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13326 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13327 && mConsoleTaskData.mLastState != MachineState_Null,
13328 E_FAIL);
13329
13330 /*
13331 * On failure, set the state to the state we had when BeginPoweringDown()
13332 * was called (this is expected by Console::PowerDown() and the associated
13333 * task). On success the VM process already changed the state to
13334 * MachineState_PoweredOff, so no need to do anything.
13335 */
13336 if (FAILED(hrcResult))
13337 i_setMachineState(mConsoleTaskData.mLastState);
13338
13339 /* notify the progress object about operation completion */
13340 Assert(mConsoleTaskData.mProgress);
13341 if (SUCCEEDED(hrcResult))
13342 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13343 else
13344 {
13345 if (aErrMsg.length())
13346 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13347 COM_IIDOF(ISession),
13348 getComponentName(),
13349 aErrMsg.c_str());
13350 else
13351 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13352 }
13353
13354 /* clear out the temporary saved state data */
13355 mConsoleTaskData.mLastState = MachineState_Null;
13356 mConsoleTaskData.mProgress.setNull();
13357
13358 LogFlowThisFuncLeave();
13359 return S_OK;
13360}
13361
13362
13363/**
13364 * Goes through the USB filters of the given machine to see if the given
13365 * device matches any filter or not.
13366 *
13367 * @note Locks the same as USBController::hasMatchingFilter() does.
13368 */
13369HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13370 BOOL *aMatched,
13371 ULONG *aMaskedInterfaces)
13372{
13373 LogFlowThisFunc(("\n"));
13374
13375#ifdef VBOX_WITH_USB
13376 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13377#else
13378 NOREF(aDevice);
13379 NOREF(aMaskedInterfaces);
13380 *aMatched = FALSE;
13381#endif
13382
13383 return S_OK;
13384}
13385
13386/**
13387 * @note Locks the same as Host::captureUSBDevice() does.
13388 */
13389HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13390{
13391 LogFlowThisFunc(("\n"));
13392
13393#ifdef VBOX_WITH_USB
13394 /* if captureDeviceForVM() fails, it must have set extended error info */
13395 clearError();
13396 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13397 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13398 return rc;
13399
13400 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13401 AssertReturn(service, E_FAIL);
13402 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13403#else
13404 RT_NOREF(aId, aCaptureFilename);
13405 return E_NOTIMPL;
13406#endif
13407}
13408
13409/**
13410 * @note Locks the same as Host::detachUSBDevice() does.
13411 */
13412HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13413 BOOL aDone)
13414{
13415 LogFlowThisFunc(("\n"));
13416
13417#ifdef VBOX_WITH_USB
13418 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13419 AssertReturn(service, E_FAIL);
13420 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13421#else
13422 NOREF(aId);
13423 NOREF(aDone);
13424 return E_NOTIMPL;
13425#endif
13426}
13427
13428/**
13429 * Inserts all machine filters to the USB proxy service and then calls
13430 * Host::autoCaptureUSBDevices().
13431 *
13432 * Called by Console from the VM process upon VM startup.
13433 *
13434 * @note Locks what called methods lock.
13435 */
13436HRESULT SessionMachine::autoCaptureUSBDevices()
13437{
13438 LogFlowThisFunc(("\n"));
13439
13440#ifdef VBOX_WITH_USB
13441 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13442 AssertComRC(rc);
13443 NOREF(rc);
13444
13445 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13446 AssertReturn(service, E_FAIL);
13447 return service->autoCaptureDevicesForVM(this);
13448#else
13449 return S_OK;
13450#endif
13451}
13452
13453/**
13454 * Removes all machine filters from the USB proxy service and then calls
13455 * Host::detachAllUSBDevices().
13456 *
13457 * Called by Console from the VM process upon normal VM termination or by
13458 * SessionMachine::uninit() upon abnormal VM termination (from under the
13459 * Machine/SessionMachine lock).
13460 *
13461 * @note Locks what called methods lock.
13462 */
13463HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13464{
13465 LogFlowThisFunc(("\n"));
13466
13467#ifdef VBOX_WITH_USB
13468 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13469 AssertComRC(rc);
13470 NOREF(rc);
13471
13472 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13473 AssertReturn(service, E_FAIL);
13474 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13475#else
13476 NOREF(aDone);
13477 return S_OK;
13478#endif
13479}
13480
13481/**
13482 * @note Locks this object for writing.
13483 */
13484HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13485 ComPtr<IProgress> &aProgress)
13486{
13487 LogFlowThisFuncEnter();
13488
13489 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13490 /*
13491 * We don't assert below because it might happen that a non-direct session
13492 * informs us it is closed right after we've been uninitialized -- it's ok.
13493 */
13494
13495 /* get IInternalSessionControl interface */
13496 ComPtr<IInternalSessionControl> control(aSession);
13497
13498 ComAssertRet(!control.isNull(), E_INVALIDARG);
13499
13500 /* Creating a Progress object requires the VirtualBox lock, and
13501 * thus locking it here is required by the lock order rules. */
13502 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13503
13504 if (control == mData->mSession.mDirectControl)
13505 {
13506 /* The direct session is being normally closed by the client process
13507 * ----------------------------------------------------------------- */
13508
13509 /* go to the closing state (essential for all open*Session() calls and
13510 * for #i_checkForDeath()) */
13511 Assert(mData->mSession.mState == SessionState_Locked);
13512 mData->mSession.mState = SessionState_Unlocking;
13513
13514 /* set direct control to NULL to release the remote instance */
13515 mData->mSession.mDirectControl.setNull();
13516 LogFlowThisFunc(("Direct control is set to NULL\n"));
13517
13518 if (mData->mSession.mProgress)
13519 {
13520 /* finalize the progress, someone might wait if a frontend
13521 * closes the session before powering on the VM. */
13522 mData->mSession.mProgress->notifyComplete(E_FAIL,
13523 COM_IIDOF(ISession),
13524 getComponentName(),
13525 tr("The VM session was closed before any attempt to power it on"));
13526 mData->mSession.mProgress.setNull();
13527 }
13528
13529 /* Create the progress object the client will use to wait until
13530 * #i_checkForDeath() is called to uninitialize this session object after
13531 * it releases the IPC semaphore.
13532 * Note! Because we're "reusing" mProgress here, this must be a proxy
13533 * object just like for LaunchVMProcess. */
13534 Assert(mData->mSession.mProgress.isNull());
13535 ComObjPtr<ProgressProxy> progress;
13536 progress.createObject();
13537 ComPtr<IUnknown> pPeer(mPeer);
13538 progress->init(mParent, pPeer,
13539 Bstr(tr("Closing session")).raw(),
13540 FALSE /* aCancelable */);
13541 progress.queryInterfaceTo(aProgress.asOutParam());
13542 mData->mSession.mProgress = progress;
13543 }
13544 else
13545 {
13546 /* the remote session is being normally closed */
13547 bool found = false;
13548 for (Data::Session::RemoteControlList::iterator
13549 it = mData->mSession.mRemoteControls.begin();
13550 it != mData->mSession.mRemoteControls.end();
13551 ++it)
13552 {
13553 if (control == *it)
13554 {
13555 found = true;
13556 // This MUST be erase(it), not remove(*it) as the latter
13557 // triggers a very nasty use after free due to the place where
13558 // the value "lives".
13559 mData->mSession.mRemoteControls.erase(it);
13560 break;
13561 }
13562 }
13563 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13564 E_INVALIDARG);
13565 }
13566
13567 /* signal the client watcher thread, because the client is going away */
13568 mParent->i_updateClientWatcher();
13569
13570 LogFlowThisFuncLeave();
13571 return S_OK;
13572}
13573
13574HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13575 std::vector<com::Utf8Str> &aValues,
13576 std::vector<LONG64> &aTimestamps,
13577 std::vector<com::Utf8Str> &aFlags)
13578{
13579 LogFlowThisFunc(("\n"));
13580
13581#ifdef VBOX_WITH_GUEST_PROPS
13582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13583
13584 size_t cEntries = mHWData->mGuestProperties.size();
13585 aNames.resize(cEntries);
13586 aValues.resize(cEntries);
13587 aTimestamps.resize(cEntries);
13588 aFlags.resize(cEntries);
13589
13590 size_t i = 0;
13591 for (HWData::GuestPropertyMap::const_iterator
13592 it = mHWData->mGuestProperties.begin();
13593 it != mHWData->mGuestProperties.end();
13594 ++it, ++i)
13595 {
13596 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13597 aNames[i] = it->first;
13598 aValues[i] = it->second.strValue;
13599 aTimestamps[i] = it->second.mTimestamp;
13600
13601 /* If it is NULL, keep it NULL. */
13602 if (it->second.mFlags)
13603 {
13604 GuestPropWriteFlags(it->second.mFlags, szFlags);
13605 aFlags[i] = szFlags;
13606 }
13607 else
13608 aFlags[i] = "";
13609
13610 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13611 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13612 }
13613 return S_OK;
13614#else
13615 ReturnComNotImplemented();
13616#endif
13617}
13618
13619HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13620 const com::Utf8Str &aValue,
13621 LONG64 aTimestamp,
13622 const com::Utf8Str &aFlags,
13623 BOOL fWasDeleted)
13624{
13625 LogFlowThisFunc(("\n"));
13626
13627#ifdef VBOX_WITH_GUEST_PROPS
13628 try
13629 {
13630 /*
13631 * Convert input up front.
13632 */
13633 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13634 if (aFlags.length())
13635 {
13636 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13637 AssertRCReturn(vrc, E_INVALIDARG);
13638 }
13639
13640 /*
13641 * Now grab the object lock, validate the state and do the update.
13642 */
13643
13644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13645
13646 if (!Global::IsOnline(mData->mMachineState))
13647 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13648
13649 i_setModified(IsModified_MachineData);
13650 mHWData.backup();
13651
13652 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13653 if (it != mHWData->mGuestProperties.end())
13654 {
13655 if (!fWasDeleted)
13656 {
13657 it->second.strValue = aValue;
13658 it->second.mTimestamp = aTimestamp;
13659 it->second.mFlags = fFlags;
13660 }
13661 else
13662 mHWData->mGuestProperties.erase(it);
13663
13664 mData->mGuestPropertiesModified = TRUE;
13665 }
13666 else if (!fWasDeleted)
13667 {
13668 HWData::GuestProperty prop;
13669 prop.strValue = aValue;
13670 prop.mTimestamp = aTimestamp;
13671 prop.mFlags = fFlags;
13672
13673 mHWData->mGuestProperties[aName] = prop;
13674 mData->mGuestPropertiesModified = TRUE;
13675 }
13676
13677 alock.release();
13678
13679 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13680 }
13681 catch (...)
13682 {
13683 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13684 }
13685 return S_OK;
13686#else
13687 ReturnComNotImplemented();
13688#endif
13689}
13690
13691
13692HRESULT SessionMachine::lockMedia()
13693{
13694 AutoMultiWriteLock2 alock(this->lockHandle(),
13695 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13696
13697 AssertReturn( mData->mMachineState == MachineState_Starting
13698 || mData->mMachineState == MachineState_Restoring
13699 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13700
13701 clearError();
13702 alock.release();
13703 return i_lockMedia();
13704}
13705
13706HRESULT SessionMachine::unlockMedia()
13707{
13708 HRESULT hrc = i_unlockMedia();
13709 return hrc;
13710}
13711
13712HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13713 ComPtr<IMediumAttachment> &aNewAttachment)
13714{
13715 // request the host lock first, since might be calling Host methods for getting host drives;
13716 // next, protect the media tree all the while we're in here, as well as our member variables
13717 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13718 this->lockHandle(),
13719 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13720
13721 IMediumAttachment *iAttach = aAttachment;
13722 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13723
13724 Utf8Str ctrlName;
13725 LONG lPort;
13726 LONG lDevice;
13727 bool fTempEject;
13728 {
13729 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13730
13731 /* Need to query the details first, as the IMediumAttachment reference
13732 * might be to the original settings, which we are going to change. */
13733 ctrlName = pAttach->i_getControllerName();
13734 lPort = pAttach->i_getPort();
13735 lDevice = pAttach->i_getDevice();
13736 fTempEject = pAttach->i_getTempEject();
13737 }
13738
13739 if (!fTempEject)
13740 {
13741 /* Remember previously mounted medium. The medium before taking the
13742 * backup is not necessarily the same thing. */
13743 ComObjPtr<Medium> oldmedium;
13744 oldmedium = pAttach->i_getMedium();
13745
13746 i_setModified(IsModified_Storage);
13747 mMediumAttachments.backup();
13748
13749 // The backup operation makes the pAttach reference point to the
13750 // old settings. Re-get the correct reference.
13751 pAttach = i_findAttachment(*mMediumAttachments.data(),
13752 ctrlName,
13753 lPort,
13754 lDevice);
13755
13756 {
13757 AutoCaller autoAttachCaller(this);
13758 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13759
13760 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13761 if (!oldmedium.isNull())
13762 oldmedium->i_removeBackReference(mData->mUuid);
13763
13764 pAttach->i_updateMedium(NULL);
13765 pAttach->i_updateEjected();
13766 }
13767
13768 i_setModified(IsModified_Storage);
13769 }
13770 else
13771 {
13772 {
13773 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13774 pAttach->i_updateEjected();
13775 }
13776 }
13777
13778 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13779
13780 return S_OK;
13781}
13782
13783HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13784 com::Utf8Str &aResult)
13785{
13786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13787
13788 HRESULT hr = S_OK;
13789
13790 if (!mAuthLibCtx.hAuthLibrary)
13791 {
13792 /* Load the external authentication library. */
13793 Bstr authLibrary;
13794 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13795
13796 Utf8Str filename = authLibrary;
13797
13798 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13799 if (RT_FAILURE(vrc))
13800 hr = setErrorBoth(E_FAIL, vrc,
13801 tr("Could not load the external authentication library '%s' (%Rrc)"),
13802 filename.c_str(), vrc);
13803 }
13804
13805 /* The auth library might need the machine lock. */
13806 alock.release();
13807
13808 if (FAILED(hr))
13809 return hr;
13810
13811 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13812 {
13813 enum VRDEAuthParams
13814 {
13815 parmUuid = 1,
13816 parmGuestJudgement,
13817 parmUser,
13818 parmPassword,
13819 parmDomain,
13820 parmClientId
13821 };
13822
13823 AuthResult result = AuthResultAccessDenied;
13824
13825 Guid uuid(aAuthParams[parmUuid]);
13826 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13827 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13828
13829 result = AuthLibAuthenticate(&mAuthLibCtx,
13830 uuid.raw(), guestJudgement,
13831 aAuthParams[parmUser].c_str(),
13832 aAuthParams[parmPassword].c_str(),
13833 aAuthParams[parmDomain].c_str(),
13834 u32ClientId);
13835
13836 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13837 size_t cbPassword = aAuthParams[parmPassword].length();
13838 if (cbPassword)
13839 {
13840 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13841 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13842 }
13843
13844 if (result == AuthResultAccessGranted)
13845 aResult = "granted";
13846 else
13847 aResult = "denied";
13848
13849 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13850 aAuthParams[parmUser].c_str(), aResult.c_str()));
13851 }
13852 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13853 {
13854 enum VRDEAuthDisconnectParams
13855 {
13856 parmUuid = 1,
13857 parmClientId
13858 };
13859
13860 Guid uuid(aAuthParams[parmUuid]);
13861 uint32_t u32ClientId = 0;
13862 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13863 }
13864 else
13865 {
13866 hr = E_INVALIDARG;
13867 }
13868
13869 return hr;
13870}
13871
13872// public methods only for internal purposes
13873/////////////////////////////////////////////////////////////////////////////
13874
13875#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13876/**
13877 * Called from the client watcher thread to check for expected or unexpected
13878 * death of the client process that has a direct session to this machine.
13879 *
13880 * On Win32 and on OS/2, this method is called only when we've got the
13881 * mutex (i.e. the client has either died or terminated normally) so it always
13882 * returns @c true (the client is terminated, the session machine is
13883 * uninitialized).
13884 *
13885 * On other platforms, the method returns @c true if the client process has
13886 * terminated normally or abnormally and the session machine was uninitialized,
13887 * and @c false if the client process is still alive.
13888 *
13889 * @note Locks this object for writing.
13890 */
13891bool SessionMachine::i_checkForDeath()
13892{
13893 Uninit::Reason reason;
13894 bool terminated = false;
13895
13896 /* Enclose autoCaller with a block because calling uninit() from under it
13897 * will deadlock. */
13898 {
13899 AutoCaller autoCaller(this);
13900 if (!autoCaller.isOk())
13901 {
13902 /* return true if not ready, to cause the client watcher to exclude
13903 * the corresponding session from watching */
13904 LogFlowThisFunc(("Already uninitialized!\n"));
13905 return true;
13906 }
13907
13908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13909
13910 /* Determine the reason of death: if the session state is Closing here,
13911 * everything is fine. Otherwise it means that the client did not call
13912 * OnSessionEnd() before it released the IPC semaphore. This may happen
13913 * either because the client process has abnormally terminated, or
13914 * because it simply forgot to call ISession::Close() before exiting. We
13915 * threat the latter also as an abnormal termination (see
13916 * Session::uninit() for details). */
13917 reason = mData->mSession.mState == SessionState_Unlocking ?
13918 Uninit::Normal :
13919 Uninit::Abnormal;
13920
13921 if (mClientToken)
13922 terminated = mClientToken->release();
13923 } /* AutoCaller block */
13924
13925 if (terminated)
13926 uninit(reason);
13927
13928 return terminated;
13929}
13930
13931void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13932{
13933 LogFlowThisFunc(("\n"));
13934
13935 strTokenId.setNull();
13936
13937 AutoCaller autoCaller(this);
13938 AssertComRCReturnVoid(autoCaller.rc());
13939
13940 Assert(mClientToken);
13941 if (mClientToken)
13942 mClientToken->getId(strTokenId);
13943}
13944#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13945IToken *SessionMachine::i_getToken()
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.rc(), NULL);
13951
13952 Assert(mClientToken);
13953 if (mClientToken)
13954 return mClientToken->getToken();
13955 else
13956 return NULL;
13957}
13958#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13959
13960Machine::ClientToken *SessionMachine::i_getClientToken()
13961{
13962 LogFlowThisFunc(("\n"));
13963
13964 AutoCaller autoCaller(this);
13965 AssertComRCReturn(autoCaller.rc(), NULL);
13966
13967 return mClientToken;
13968}
13969
13970
13971/**
13972 * @note Locks this object for reading.
13973 */
13974HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 if (mData->mSession.mLockType == LockType_VM)
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13999 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14000 const Utf8Str &aGuestIp, LONG aGuestPort)
14001{
14002 LogFlowThisFunc(("\n"));
14003
14004 AutoCaller autoCaller(this);
14005 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14006
14007 ComPtr<IInternalSessionControl> directControl;
14008 {
14009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14010 if (mData->mSession.mLockType == LockType_VM)
14011 directControl = mData->mSession.mDirectControl;
14012 }
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017 /*
14018 * instead acting like callback we ask IVirtualBox deliver corresponding event
14019 */
14020
14021 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14022 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14023 return S_OK;
14024}
14025
14026/**
14027 * @note Locks this object for reading.
14028 */
14029HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14030{
14031 LogFlowThisFunc(("\n"));
14032
14033 AutoCaller autoCaller(this);
14034 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14035
14036 ComPtr<IInternalSessionControl> directControl;
14037 {
14038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14039 if (mData->mSession.mLockType == LockType_VM)
14040 directControl = mData->mSession.mDirectControl;
14041 }
14042
14043 /* ignore notifications sent after #OnSessionEnd() is called */
14044 if (!directControl)
14045 return S_OK;
14046
14047 return directControl->OnAudioAdapterChange(audioAdapter);
14048}
14049
14050/**
14051 * @note Locks this object for reading.
14052 */
14053HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14054{
14055 LogFlowThisFunc(("\n"));
14056
14057 AutoCaller autoCaller(this);
14058 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14059
14060 ComPtr<IInternalSessionControl> directControl;
14061 {
14062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14063 if (mData->mSession.mLockType == LockType_VM)
14064 directControl = mData->mSession.mDirectControl;
14065 }
14066
14067 /* ignore notifications sent after #OnSessionEnd() is called */
14068 if (!directControl)
14069 return S_OK;
14070
14071 return directControl->OnSerialPortChange(serialPort);
14072}
14073
14074/**
14075 * @note Locks this object for reading.
14076 */
14077HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14078{
14079 LogFlowThisFunc(("\n"));
14080
14081 AutoCaller autoCaller(this);
14082 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14083
14084 ComPtr<IInternalSessionControl> directControl;
14085 {
14086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14087 if (mData->mSession.mLockType == LockType_VM)
14088 directControl = mData->mSession.mDirectControl;
14089 }
14090
14091 /* ignore notifications sent after #OnSessionEnd() is called */
14092 if (!directControl)
14093 return S_OK;
14094
14095 return directControl->OnParallelPortChange(parallelPort);
14096}
14097
14098/**
14099 * @note Locks this object for reading.
14100 */
14101HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14102{
14103 LogFlowThisFunc(("\n"));
14104
14105 AutoCaller autoCaller(this);
14106 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14107
14108 ComPtr<IInternalSessionControl> directControl;
14109 {
14110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14111 if (mData->mSession.mLockType == LockType_VM)
14112 directControl = mData->mSession.mDirectControl;
14113 }
14114
14115 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120
14121 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14122}
14123
14124/**
14125 * @note Locks this object for reading.
14126 */
14127HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14128{
14129 LogFlowThisFunc(("\n"));
14130
14131 AutoCaller autoCaller(this);
14132 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14133
14134 ComPtr<IInternalSessionControl> directControl;
14135 {
14136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14137 if (mData->mSession.mLockType == LockType_VM)
14138 directControl = mData->mSession.mDirectControl;
14139 }
14140
14141 mParent->i_onMediumChanged(aAttachment);
14142
14143 /* ignore notifications sent after #OnSessionEnd() is called */
14144 if (!directControl)
14145 return S_OK;
14146
14147 return directControl->OnMediumChange(aAttachment, aForce);
14148}
14149
14150HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14151{
14152 LogFlowThisFunc(("\n"));
14153
14154 AutoCaller autoCaller(this);
14155 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14156
14157 ComPtr<IInternalSessionControl> directControl;
14158 {
14159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14160 if (mData->mSession.mLockType == LockType_VM)
14161 directControl = mData->mSession.mDirectControl;
14162 }
14163
14164 /* ignore notifications sent after #OnSessionEnd() is called */
14165 if (!directControl)
14166 return S_OK;
14167
14168 return directControl->OnVMProcessPriorityChange(aPriority);
14169}
14170
14171/**
14172 * @note Locks this object for reading.
14173 */
14174HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14175{
14176 LogFlowThisFunc(("\n"));
14177
14178 AutoCaller autoCaller(this);
14179 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14180
14181 ComPtr<IInternalSessionControl> directControl;
14182 {
14183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14184 if (mData->mSession.mLockType == LockType_VM)
14185 directControl = mData->mSession.mDirectControl;
14186 }
14187
14188 /* ignore notifications sent after #OnSessionEnd() is called */
14189 if (!directControl)
14190 return S_OK;
14191
14192 return directControl->OnCPUChange(aCPU, aRemove);
14193}
14194
14195HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14196{
14197 LogFlowThisFunc(("\n"));
14198
14199 AutoCaller autoCaller(this);
14200 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14201
14202 ComPtr<IInternalSessionControl> directControl;
14203 {
14204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14205 if (mData->mSession.mLockType == LockType_VM)
14206 directControl = mData->mSession.mDirectControl;
14207 }
14208
14209 /* ignore notifications sent after #OnSessionEnd() is called */
14210 if (!directControl)
14211 return S_OK;
14212
14213 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14214}
14215
14216/**
14217 * @note Locks this object for reading.
14218 */
14219HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14220{
14221 LogFlowThisFunc(("\n"));
14222
14223 AutoCaller autoCaller(this);
14224 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14225
14226 ComPtr<IInternalSessionControl> directControl;
14227 {
14228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14229 if (mData->mSession.mLockType == LockType_VM)
14230 directControl = mData->mSession.mDirectControl;
14231 }
14232
14233 /* ignore notifications sent after #OnSessionEnd() is called */
14234 if (!directControl)
14235 return S_OK;
14236
14237 return directControl->OnVRDEServerChange(aRestart);
14238}
14239
14240/**
14241 * @note Locks this object for reading.
14242 */
14243HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14244{
14245 LogFlowThisFunc(("\n"));
14246
14247 AutoCaller autoCaller(this);
14248 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14249
14250 ComPtr<IInternalSessionControl> directControl;
14251 {
14252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14253 if (mData->mSession.mLockType == LockType_VM)
14254 directControl = mData->mSession.mDirectControl;
14255 }
14256
14257 /* ignore notifications sent after #OnSessionEnd() is called */
14258 if (!directControl)
14259 return S_OK;
14260
14261 return directControl->OnRecordingChange(aEnable);
14262}
14263
14264/**
14265 * @note Locks this object for reading.
14266 */
14267HRESULT SessionMachine::i_onUSBControllerChange()
14268{
14269 LogFlowThisFunc(("\n"));
14270
14271 AutoCaller autoCaller(this);
14272 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14273
14274 ComPtr<IInternalSessionControl> directControl;
14275 {
14276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14277 if (mData->mSession.mLockType == LockType_VM)
14278 directControl = mData->mSession.mDirectControl;
14279 }
14280
14281 /* ignore notifications sent after #OnSessionEnd() is called */
14282 if (!directControl)
14283 return S_OK;
14284
14285 return directControl->OnUSBControllerChange();
14286}
14287
14288/**
14289 * @note Locks this object for reading.
14290 */
14291HRESULT SessionMachine::i_onSharedFolderChange()
14292{
14293 LogFlowThisFunc(("\n"));
14294
14295 AutoCaller autoCaller(this);
14296 AssertComRCReturnRC(autoCaller.rc());
14297
14298 ComPtr<IInternalSessionControl> directControl;
14299 {
14300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14301 if (mData->mSession.mLockType == LockType_VM)
14302 directControl = mData->mSession.mDirectControl;
14303 }
14304
14305 /* ignore notifications sent after #OnSessionEnd() is called */
14306 if (!directControl)
14307 return S_OK;
14308
14309 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14310}
14311
14312/**
14313 * @note Locks this object for reading.
14314 */
14315HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14316{
14317 LogFlowThisFunc(("\n"));
14318
14319 AutoCaller autoCaller(this);
14320 AssertComRCReturnRC(autoCaller.rc());
14321
14322 ComPtr<IInternalSessionControl> directControl;
14323 {
14324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14325 if (mData->mSession.mLockType == LockType_VM)
14326 directControl = mData->mSession.mDirectControl;
14327 }
14328
14329 /* ignore notifications sent after #OnSessionEnd() is called */
14330 if (!directControl)
14331 return S_OK;
14332
14333 return directControl->OnClipboardModeChange(aClipboardMode);
14334}
14335
14336/**
14337 * @note Locks this object for reading.
14338 */
14339HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14340{
14341 LogFlowThisFunc(("\n"));
14342
14343 AutoCaller autoCaller(this);
14344 AssertComRCReturnRC(autoCaller.rc());
14345
14346 ComPtr<IInternalSessionControl> directControl;
14347 {
14348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14349 if (mData->mSession.mLockType == LockType_VM)
14350 directControl = mData->mSession.mDirectControl;
14351 }
14352
14353 /* ignore notifications sent after #OnSessionEnd() is called */
14354 if (!directControl)
14355 return S_OK;
14356
14357 return directControl->OnClipboardFileTransferModeChange(aEnable);
14358}
14359
14360/**
14361 * @note Locks this object for reading.
14362 */
14363HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14364{
14365 LogFlowThisFunc(("\n"));
14366
14367 AutoCaller autoCaller(this);
14368 AssertComRCReturnRC(autoCaller.rc());
14369
14370 ComPtr<IInternalSessionControl> directControl;
14371 {
14372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14373 if (mData->mSession.mLockType == LockType_VM)
14374 directControl = mData->mSession.mDirectControl;
14375 }
14376
14377 /* ignore notifications sent after #OnSessionEnd() is called */
14378 if (!directControl)
14379 return S_OK;
14380
14381 return directControl->OnDnDModeChange(aDnDMode);
14382}
14383
14384/**
14385 * @note Locks this object for reading.
14386 */
14387HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14388{
14389 LogFlowThisFunc(("\n"));
14390
14391 AutoCaller autoCaller(this);
14392 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14393
14394 ComPtr<IInternalSessionControl> directControl;
14395 {
14396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14397 if (mData->mSession.mLockType == LockType_VM)
14398 directControl = mData->mSession.mDirectControl;
14399 }
14400
14401 /* ignore notifications sent after #OnSessionEnd() is called */
14402 if (!directControl)
14403 return S_OK;
14404
14405 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14406}
14407
14408/**
14409 * @note Locks this object for reading.
14410 */
14411HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14412{
14413 LogFlowThisFunc(("\n"));
14414
14415 AutoCaller autoCaller(this);
14416 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14417
14418 ComPtr<IInternalSessionControl> directControl;
14419 {
14420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14421 if (mData->mSession.mLockType == LockType_VM)
14422 directControl = mData->mSession.mDirectControl;
14423 }
14424
14425 /* ignore notifications sent after #OnSessionEnd() is called */
14426 if (!directControl)
14427 return S_OK;
14428
14429 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14430}
14431
14432/**
14433 * Returns @c true if this machine's USB controller reports it has a matching
14434 * filter for the given USB device and @c false otherwise.
14435 *
14436 * @note locks this object for reading.
14437 */
14438bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14439{
14440 AutoCaller autoCaller(this);
14441 /* silently return if not ready -- this method may be called after the
14442 * direct machine session has been called */
14443 if (!autoCaller.isOk())
14444 return false;
14445
14446#ifdef VBOX_WITH_USB
14447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14448
14449 switch (mData->mMachineState)
14450 {
14451 case MachineState_Starting:
14452 case MachineState_Restoring:
14453 case MachineState_TeleportingIn:
14454 case MachineState_Paused:
14455 case MachineState_Running:
14456 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14457 * elsewhere... */
14458 alock.release();
14459 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14460 default: break;
14461 }
14462#else
14463 NOREF(aDevice);
14464 NOREF(aMaskedIfs);
14465#endif
14466 return false;
14467}
14468
14469/**
14470 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14471 */
14472HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14473 IVirtualBoxErrorInfo *aError,
14474 ULONG aMaskedIfs,
14475 const com::Utf8Str &aCaptureFilename)
14476{
14477 LogFlowThisFunc(("\n"));
14478
14479 AutoCaller autoCaller(this);
14480
14481 /* This notification may happen after the machine object has been
14482 * uninitialized (the session was closed), so don't assert. */
14483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14484
14485 ComPtr<IInternalSessionControl> directControl;
14486 {
14487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14488 if (mData->mSession.mLockType == LockType_VM)
14489 directControl = mData->mSession.mDirectControl;
14490 }
14491
14492 /* fail on notifications sent after #OnSessionEnd() is called, it is
14493 * expected by the caller */
14494 if (!directControl)
14495 return E_FAIL;
14496
14497 /* No locks should be held at this point. */
14498 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14499 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14500
14501 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14502}
14503
14504/**
14505 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14506 */
14507HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14508 IVirtualBoxErrorInfo *aError)
14509{
14510 LogFlowThisFunc(("\n"));
14511
14512 AutoCaller autoCaller(this);
14513
14514 /* This notification may happen after the machine object has been
14515 * uninitialized (the session was closed), so don't assert. */
14516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14517
14518 ComPtr<IInternalSessionControl> directControl;
14519 {
14520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14521 if (mData->mSession.mLockType == LockType_VM)
14522 directControl = mData->mSession.mDirectControl;
14523 }
14524
14525 /* fail on notifications sent after #OnSessionEnd() is called, it is
14526 * expected by the caller */
14527 if (!directControl)
14528 return E_FAIL;
14529
14530 /* No locks should be held at this point. */
14531 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14532 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14533
14534 return directControl->OnUSBDeviceDetach(aId, aError);
14535}
14536
14537// protected methods
14538/////////////////////////////////////////////////////////////////////////////
14539
14540/**
14541 * Deletes the given file if it is no longer in use by either the current machine state
14542 * (if the machine is "saved") or any of the machine's snapshots.
14543 *
14544 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14545 * but is different for each SnapshotMachine. When calling this, the order of calling this
14546 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14547 * is therefore critical. I know, it's all rather messy.
14548 *
14549 * @param strStateFile
14550 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14551 * the test for whether the saved state file is in use.
14552 */
14553void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14554 Snapshot *pSnapshotToIgnore)
14555{
14556 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14557 if ( (strStateFile.isNotEmpty())
14558 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14559 )
14560 // ... and it must also not be shared with other snapshots
14561 if ( !mData->mFirstSnapshot
14562 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14563 // this checks the SnapshotMachine's state file paths
14564 )
14565 RTFileDelete(strStateFile.c_str());
14566}
14567
14568/**
14569 * Locks the attached media.
14570 *
14571 * All attached hard disks are locked for writing and DVD/floppy are locked for
14572 * reading. Parents of attached hard disks (if any) are locked for reading.
14573 *
14574 * This method also performs accessibility check of all media it locks: if some
14575 * media is inaccessible, the method will return a failure and a bunch of
14576 * extended error info objects per each inaccessible medium.
14577 *
14578 * Note that this method is atomic: if it returns a success, all media are
14579 * locked as described above; on failure no media is locked at all (all
14580 * succeeded individual locks will be undone).
14581 *
14582 * The caller is responsible for doing the necessary state sanity checks.
14583 *
14584 * The locks made by this method must be undone by calling #unlockMedia() when
14585 * no more needed.
14586 */
14587HRESULT SessionMachine::i_lockMedia()
14588{
14589 AutoCaller autoCaller(this);
14590 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14591
14592 AutoMultiWriteLock2 alock(this->lockHandle(),
14593 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14594
14595 /* bail out if trying to lock things with already set up locking */
14596 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14597
14598 MultiResult mrc(S_OK);
14599
14600 /* Collect locking information for all medium objects attached to the VM. */
14601 for (MediumAttachmentList::const_iterator
14602 it = mMediumAttachments->begin();
14603 it != mMediumAttachments->end();
14604 ++it)
14605 {
14606 MediumAttachment *pAtt = *it;
14607 DeviceType_T devType = pAtt->i_getType();
14608 Medium *pMedium = pAtt->i_getMedium();
14609
14610 MediumLockList *pMediumLockList(new MediumLockList());
14611 // There can be attachments without a medium (floppy/dvd), and thus
14612 // it's impossible to create a medium lock list. It still makes sense
14613 // to have the empty medium lock list in the map in case a medium is
14614 // attached later.
14615 if (pMedium != NULL)
14616 {
14617 MediumType_T mediumType = pMedium->i_getType();
14618 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14619 || mediumType == MediumType_Shareable;
14620 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14621
14622 alock.release();
14623 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14624 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14625 false /* fMediumLockWriteAll */,
14626 NULL,
14627 *pMediumLockList);
14628 alock.acquire();
14629 if (FAILED(mrc))
14630 {
14631 delete pMediumLockList;
14632 mData->mSession.mLockedMedia.Clear();
14633 break;
14634 }
14635 }
14636
14637 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14638 if (FAILED(rc))
14639 {
14640 mData->mSession.mLockedMedia.Clear();
14641 mrc = setError(rc,
14642 tr("Collecting locking information for all attached media failed"));
14643 break;
14644 }
14645 }
14646
14647 if (SUCCEEDED(mrc))
14648 {
14649 /* Now lock all media. If this fails, nothing is locked. */
14650 alock.release();
14651 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14652 alock.acquire();
14653 if (FAILED(rc))
14654 {
14655 mrc = setError(rc,
14656 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14657 }
14658 }
14659
14660 return mrc;
14661}
14662
14663/**
14664 * Undoes the locks made by by #lockMedia().
14665 */
14666HRESULT SessionMachine::i_unlockMedia()
14667{
14668 AutoCaller autoCaller(this);
14669 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14670
14671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14672
14673 /* we may be holding important error info on the current thread;
14674 * preserve it */
14675 ErrorInfoKeeper eik;
14676
14677 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14678 AssertComRC(rc);
14679 return rc;
14680}
14681
14682/**
14683 * Helper to change the machine state (reimplementation).
14684 *
14685 * @note Locks this object for writing.
14686 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14687 * it can cause crashes in random places due to unexpectedly committing
14688 * the current settings. The caller is responsible for that. The call
14689 * to saveStateSettings is fine, because this method does not commit.
14690 */
14691HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14692{
14693 LogFlowThisFuncEnter();
14694
14695 AutoCaller autoCaller(this);
14696 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14697
14698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14699
14700 MachineState_T oldMachineState = mData->mMachineState;
14701
14702 AssertMsgReturn(oldMachineState != aMachineState,
14703 ("oldMachineState=%s, aMachineState=%s\n",
14704 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14705 E_FAIL);
14706
14707 HRESULT rc = S_OK;
14708
14709 int stsFlags = 0;
14710 bool deleteSavedState = false;
14711
14712 /* detect some state transitions */
14713
14714 if ( ( ( oldMachineState == MachineState_Saved
14715 || oldMachineState == MachineState_AbortedSaved
14716 )
14717 && aMachineState == MachineState_Restoring
14718 )
14719 || ( ( oldMachineState == MachineState_PoweredOff
14720 || oldMachineState == MachineState_Teleported
14721 || oldMachineState == MachineState_Aborted
14722 )
14723 && ( aMachineState == MachineState_TeleportingIn
14724 || aMachineState == MachineState_Starting
14725 )
14726 )
14727 )
14728 {
14729 /* The EMT thread is about to start */
14730
14731 /* Nothing to do here for now... */
14732
14733 /// @todo NEWMEDIA don't let mDVDDrive and other children
14734 /// change anything when in the Starting/Restoring state
14735 }
14736 else if ( ( oldMachineState == MachineState_Running
14737 || oldMachineState == MachineState_Paused
14738 || oldMachineState == MachineState_Teleporting
14739 || oldMachineState == MachineState_OnlineSnapshotting
14740 || oldMachineState == MachineState_LiveSnapshotting
14741 || oldMachineState == MachineState_Stuck
14742 || oldMachineState == MachineState_Starting
14743 || oldMachineState == MachineState_Stopping
14744 || oldMachineState == MachineState_Saving
14745 || oldMachineState == MachineState_Restoring
14746 || oldMachineState == MachineState_TeleportingPausedVM
14747 || oldMachineState == MachineState_TeleportingIn
14748 )
14749 && ( aMachineState == MachineState_PoweredOff
14750 || aMachineState == MachineState_Saved
14751 || aMachineState == MachineState_Teleported
14752 || aMachineState == MachineState_Aborted
14753 || aMachineState == MachineState_AbortedSaved
14754 )
14755 )
14756 {
14757 /* The EMT thread has just stopped, unlock attached media. Note that as
14758 * opposed to locking that is done from Console, we do unlocking here
14759 * because the VM process may have aborted before having a chance to
14760 * properly unlock all media it locked. */
14761
14762 unlockMedia();
14763 }
14764
14765 if (oldMachineState == MachineState_Restoring)
14766 {
14767 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14768 {
14769 /*
14770 * delete the saved state file once the machine has finished
14771 * restoring from it (note that Console sets the state from
14772 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14773 * to give the user an ability to fix an error and retry --
14774 * we keep the saved state file in this case)
14775 */
14776 deleteSavedState = true;
14777 }
14778 }
14779 else if ( oldMachineState == MachineState_Saved
14780 && ( aMachineState == MachineState_PoweredOff
14781 || aMachineState == MachineState_Teleported
14782 )
14783 )
14784 {
14785 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14786 deleteSavedState = true;
14787 mData->mCurrentStateModified = TRUE;
14788 stsFlags |= SaveSTS_CurStateModified;
14789 }
14790 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14791 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14792
14793 if ( aMachineState == MachineState_Starting
14794 || aMachineState == MachineState_Restoring
14795 || aMachineState == MachineState_TeleportingIn
14796 )
14797 {
14798 /* set the current state modified flag to indicate that the current
14799 * state is no more identical to the state in the
14800 * current snapshot */
14801 if (!mData->mCurrentSnapshot.isNull())
14802 {
14803 mData->mCurrentStateModified = TRUE;
14804 stsFlags |= SaveSTS_CurStateModified;
14805 }
14806 }
14807
14808 if (deleteSavedState)
14809 {
14810 if (mRemoveSavedState)
14811 {
14812 Assert(!mSSData->strStateFilePath.isEmpty());
14813
14814 // it is safe to delete the saved state file if ...
14815 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14816 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14817 // ... none of the snapshots share the saved state file
14818 )
14819 RTFileDelete(mSSData->strStateFilePath.c_str());
14820 }
14821
14822 mSSData->strStateFilePath.setNull();
14823 stsFlags |= SaveSTS_StateFilePath;
14824 }
14825
14826 /* redirect to the underlying peer machine */
14827 mPeer->i_setMachineState(aMachineState);
14828
14829 if ( oldMachineState != MachineState_RestoringSnapshot
14830 && ( aMachineState == MachineState_PoweredOff
14831 || aMachineState == MachineState_Teleported
14832 || aMachineState == MachineState_Aborted
14833 || aMachineState == MachineState_AbortedSaved
14834 || aMachineState == MachineState_Saved))
14835 {
14836 /* the machine has stopped execution
14837 * (or the saved state file was adopted) */
14838 stsFlags |= SaveSTS_StateTimeStamp;
14839 }
14840
14841 if ( ( oldMachineState == MachineState_PoweredOff
14842 || oldMachineState == MachineState_Aborted
14843 || oldMachineState == MachineState_Teleported
14844 )
14845 && aMachineState == MachineState_Saved)
14846 {
14847 /* the saved state file was adopted */
14848 Assert(!mSSData->strStateFilePath.isEmpty());
14849 stsFlags |= SaveSTS_StateFilePath;
14850 }
14851
14852#ifdef VBOX_WITH_GUEST_PROPS
14853 if ( aMachineState == MachineState_PoweredOff
14854 || aMachineState == MachineState_Aborted
14855 || aMachineState == MachineState_Teleported)
14856 {
14857 /* Make sure any transient guest properties get removed from the
14858 * property store on shutdown. */
14859 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14860
14861 /* remove it from the settings representation */
14862 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14863 for (settings::GuestPropertiesList::iterator
14864 it = llGuestProperties.begin();
14865 it != llGuestProperties.end();
14866 /*nothing*/)
14867 {
14868 const settings::GuestProperty &prop = *it;
14869 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14870 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14871 {
14872 it = llGuestProperties.erase(it);
14873 fNeedsSaving = true;
14874 }
14875 else
14876 {
14877 ++it;
14878 }
14879 }
14880
14881 /* Additionally remove it from the HWData representation. Required to
14882 * keep everything in sync, as this is what the API keeps using. */
14883 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14884 for (HWData::GuestPropertyMap::iterator
14885 it = llHWGuestProperties.begin();
14886 it != llHWGuestProperties.end();
14887 /*nothing*/)
14888 {
14889 uint32_t fFlags = it->second.mFlags;
14890 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14891 {
14892 /* iterator where we need to continue after the erase call
14893 * (C++03 is a fact still, and it doesn't return the iterator
14894 * which would allow continuing) */
14895 HWData::GuestPropertyMap::iterator it2 = it;
14896 ++it2;
14897 llHWGuestProperties.erase(it);
14898 it = it2;
14899 fNeedsSaving = true;
14900 }
14901 else
14902 {
14903 ++it;
14904 }
14905 }
14906
14907 if (fNeedsSaving)
14908 {
14909 mData->mCurrentStateModified = TRUE;
14910 stsFlags |= SaveSTS_CurStateModified;
14911 }
14912 }
14913#endif /* VBOX_WITH_GUEST_PROPS */
14914
14915 rc = i_saveStateSettings(stsFlags);
14916
14917 if ( ( oldMachineState != MachineState_PoweredOff
14918 && oldMachineState != MachineState_Aborted
14919 && oldMachineState != MachineState_Teleported
14920 )
14921 && ( aMachineState == MachineState_PoweredOff
14922 || aMachineState == MachineState_Aborted
14923 || aMachineState == MachineState_Teleported
14924 )
14925 )
14926 {
14927 /* we've been shut down for any reason */
14928 /* no special action so far */
14929 }
14930
14931 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
14932 LogFlowThisFuncLeave();
14933 return rc;
14934}
14935
14936/**
14937 * Sends the current machine state value to the VM process.
14938 *
14939 * @note Locks this object for reading, then calls a client process.
14940 */
14941HRESULT SessionMachine::i_updateMachineStateOnClient()
14942{
14943 AutoCaller autoCaller(this);
14944 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14945
14946 ComPtr<IInternalSessionControl> directControl;
14947 {
14948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14949 AssertReturn(!!mData, E_FAIL);
14950 if (mData->mSession.mLockType == LockType_VM)
14951 directControl = mData->mSession.mDirectControl;
14952
14953 /* directControl may be already set to NULL here in #OnSessionEnd()
14954 * called too early by the direct session process while there is still
14955 * some operation (like deleting the snapshot) in progress. The client
14956 * process in this case is waiting inside Session::close() for the
14957 * "end session" process object to complete, while #uninit() called by
14958 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14959 * operation to complete. For now, we accept this inconsistent behavior
14960 * and simply do nothing here. */
14961
14962 if (mData->mSession.mState == SessionState_Unlocking)
14963 return S_OK;
14964 }
14965
14966 /* ignore notifications sent after #OnSessionEnd() is called */
14967 if (!directControl)
14968 return S_OK;
14969
14970 return directControl->UpdateMachineState(mData->mMachineState);
14971}
14972
14973
14974/*static*/
14975HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14976{
14977 va_list args;
14978 va_start(args, pcszMsg);
14979 HRESULT rc = setErrorInternalV(aResultCode,
14980 getStaticClassIID(),
14981 getStaticComponentName(),
14982 pcszMsg, args,
14983 false /* aWarning */,
14984 true /* aLogIt */);
14985 va_end(args);
14986 return rc;
14987}
14988
14989
14990HRESULT Machine::updateState(MachineState_T aState)
14991{
14992 NOREF(aState);
14993 ReturnComNotImplemented();
14994}
14995
14996HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14997{
14998 NOREF(aProgress);
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::endPowerUp(LONG aResult)
15003{
15004 NOREF(aResult);
15005 ReturnComNotImplemented();
15006}
15007
15008HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15009{
15010 NOREF(aProgress);
15011 ReturnComNotImplemented();
15012}
15013
15014HRESULT Machine::endPoweringDown(LONG aResult,
15015 const com::Utf8Str &aErrMsg)
15016{
15017 NOREF(aResult);
15018 NOREF(aErrMsg);
15019 ReturnComNotImplemented();
15020}
15021
15022HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15023 BOOL *aMatched,
15024 ULONG *aMaskedInterfaces)
15025{
15026 NOREF(aDevice);
15027 NOREF(aMatched);
15028 NOREF(aMaskedInterfaces);
15029 ReturnComNotImplemented();
15030
15031}
15032
15033HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15034{
15035 NOREF(aId); NOREF(aCaptureFilename);
15036 ReturnComNotImplemented();
15037}
15038
15039HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15040 BOOL aDone)
15041{
15042 NOREF(aId);
15043 NOREF(aDone);
15044 ReturnComNotImplemented();
15045}
15046
15047HRESULT Machine::autoCaptureUSBDevices()
15048{
15049 ReturnComNotImplemented();
15050}
15051
15052HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15053{
15054 NOREF(aDone);
15055 ReturnComNotImplemented();
15056}
15057
15058HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15059 ComPtr<IProgress> &aProgress)
15060{
15061 NOREF(aSession);
15062 NOREF(aProgress);
15063 ReturnComNotImplemented();
15064}
15065
15066HRESULT Machine::finishOnlineMergeMedium()
15067{
15068 ReturnComNotImplemented();
15069}
15070
15071HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15072 std::vector<com::Utf8Str> &aValues,
15073 std::vector<LONG64> &aTimestamps,
15074 std::vector<com::Utf8Str> &aFlags)
15075{
15076 NOREF(aNames);
15077 NOREF(aValues);
15078 NOREF(aTimestamps);
15079 NOREF(aFlags);
15080 ReturnComNotImplemented();
15081}
15082
15083HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15084 const com::Utf8Str &aValue,
15085 LONG64 aTimestamp,
15086 const com::Utf8Str &aFlags,
15087 BOOL fWasDeleted)
15088{
15089 NOREF(aName);
15090 NOREF(aValue);
15091 NOREF(aTimestamp);
15092 NOREF(aFlags);
15093 NOREF(fWasDeleted);
15094 ReturnComNotImplemented();
15095}
15096
15097HRESULT Machine::lockMedia()
15098{
15099 ReturnComNotImplemented();
15100}
15101
15102HRESULT Machine::unlockMedia()
15103{
15104 ReturnComNotImplemented();
15105}
15106
15107HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15108 ComPtr<IMediumAttachment> &aNewAttachment)
15109{
15110 NOREF(aAttachment);
15111 NOREF(aNewAttachment);
15112 ReturnComNotImplemented();
15113}
15114
15115HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15116 ULONG aCpuUser,
15117 ULONG aCpuKernel,
15118 ULONG aCpuIdle,
15119 ULONG aMemTotal,
15120 ULONG aMemFree,
15121 ULONG aMemBalloon,
15122 ULONG aMemShared,
15123 ULONG aMemCache,
15124 ULONG aPagedTotal,
15125 ULONG aMemAllocTotal,
15126 ULONG aMemFreeTotal,
15127 ULONG aMemBalloonTotal,
15128 ULONG aMemSharedTotal,
15129 ULONG aVmNetRx,
15130 ULONG aVmNetTx)
15131{
15132 NOREF(aValidStats);
15133 NOREF(aCpuUser);
15134 NOREF(aCpuKernel);
15135 NOREF(aCpuIdle);
15136 NOREF(aMemTotal);
15137 NOREF(aMemFree);
15138 NOREF(aMemBalloon);
15139 NOREF(aMemShared);
15140 NOREF(aMemCache);
15141 NOREF(aPagedTotal);
15142 NOREF(aMemAllocTotal);
15143 NOREF(aMemFreeTotal);
15144 NOREF(aMemBalloonTotal);
15145 NOREF(aMemSharedTotal);
15146 NOREF(aVmNetRx);
15147 NOREF(aVmNetTx);
15148 ReturnComNotImplemented();
15149}
15150
15151HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15152 com::Utf8Str &aResult)
15153{
15154 NOREF(aAuthParams);
15155 NOREF(aResult);
15156 ReturnComNotImplemented();
15157}
15158
15159com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15160{
15161 com::Utf8Str strControllerName = "Unknown";
15162 switch (aBusType)
15163 {
15164 case StorageBus_IDE:
15165 {
15166 strControllerName = "IDE";
15167 break;
15168 }
15169 case StorageBus_SATA:
15170 {
15171 strControllerName = "SATA";
15172 break;
15173 }
15174 case StorageBus_SCSI:
15175 {
15176 strControllerName = "SCSI";
15177 break;
15178 }
15179 case StorageBus_Floppy:
15180 {
15181 strControllerName = "Floppy";
15182 break;
15183 }
15184 case StorageBus_SAS:
15185 {
15186 strControllerName = "SAS";
15187 break;
15188 }
15189 case StorageBus_USB:
15190 {
15191 strControllerName = "USB";
15192 break;
15193 }
15194 default:
15195 break;
15196 }
15197 return strControllerName;
15198}
15199
15200HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15201{
15202 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15203
15204 AutoCaller autoCaller(this);
15205 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15206
15207 HRESULT rc = S_OK;
15208
15209 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15210 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15211 rc = getUSBDeviceFilters(usbDeviceFilters);
15212 if (FAILED(rc)) return rc;
15213
15214 NOREF(aFlags);
15215 com::Utf8Str osTypeId;
15216 ComObjPtr<GuestOSType> osType = NULL;
15217
15218 /* Get the guest os type as a string from the VB. */
15219 rc = getOSTypeId(osTypeId);
15220 if (FAILED(rc)) return rc;
15221
15222 /* Get the os type obj that coresponds, can be used to get
15223 * the defaults for this guest OS. */
15224 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15225 if (FAILED(rc)) return rc;
15226
15227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15228
15229 /* Let the OS type select 64-bit ness. */
15230 mHWData->mLongMode = osType->i_is64Bit()
15231 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15232
15233 /* Let the OS type enable the X2APIC */
15234 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15235
15236 /* This one covers IOAPICEnabled. */
15237 mBIOSSettings->i_applyDefaults(osType);
15238
15239 /* Initialize default record settings. */
15240 mRecordingSettings->i_applyDefaults();
15241
15242 /* Initialize default BIOS settings here */
15243 /* Hardware virtualization must be ON by default */
15244 mHWData->mAPIC = true;
15245 mHWData->mHWVirtExEnabled = true;
15246
15247 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15248 if (FAILED(rc)) return rc;
15249
15250 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15251 if (FAILED(rc)) return rc;
15252
15253 /* Graphics stuff. */
15254 GraphicsControllerType_T graphicsController;
15255 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15256 if (FAILED(rc)) return rc;
15257
15258 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15259 if (FAILED(rc)) return rc;
15260
15261 ULONG vramSize;
15262 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15263 if (FAILED(rc)) return rc;
15264
15265 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15266 if (FAILED(rc)) return rc;
15267
15268 BOOL fAccelerate2DVideoEnabled;
15269 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15270 if (FAILED(rc)) return rc;
15271
15272 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15273 if (FAILED(rc)) return rc;
15274
15275 BOOL fAccelerate3DEnabled;
15276 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15277 if (FAILED(rc)) return rc;
15278
15279 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15280 if (FAILED(rc)) return rc;
15281
15282 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15283 if (FAILED(rc)) return rc;
15284
15285 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15286 if (FAILED(rc)) return rc;
15287
15288 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15289 if (FAILED(rc)) return rc;
15290
15291 BOOL mRTCUseUTC;
15292 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15293 if (FAILED(rc)) return rc;
15294
15295 setRTCUseUTC(mRTCUseUTC);
15296 if (FAILED(rc)) return rc;
15297
15298 /* the setter does more than just the assignment, so use it */
15299 ChipsetType_T enmChipsetType;
15300 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15301 if (FAILED(rc)) return rc;
15302
15303 rc = COMSETTER(ChipsetType)(enmChipsetType);
15304 if (FAILED(rc)) return rc;
15305
15306 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15307 if (FAILED(rc)) return rc;
15308
15309 /* Apply IOMMU defaults. */
15310 IommuType_T enmIommuType;
15311 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15312 if (FAILED(rc)) return rc;
15313
15314 rc = COMSETTER(IommuType)(enmIommuType);
15315 if (FAILED(rc)) return rc;
15316
15317 /* Apply network adapters defaults */
15318 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15319 mNetworkAdapters[slot]->i_applyDefaults(osType);
15320
15321 /* Apply serial port defaults */
15322 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15323 mSerialPorts[slot]->i_applyDefaults(osType);
15324
15325 /* Apply parallel port defaults - not OS dependent*/
15326 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15327 mParallelPorts[slot]->i_applyDefaults();
15328
15329 /* This one covers the TPM type. */
15330 mTrustedPlatformModule->i_applyDefaults(osType);
15331
15332 /* This one covers secure boot. */
15333 rc = mNvramStore->i_applyDefaults(osType);
15334 if (FAILED(rc)) return rc;
15335
15336 /* Audio stuff. */
15337 AudioControllerType_T audioController;
15338 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15339 if (FAILED(rc)) return rc;
15340
15341 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15342 if (FAILED(rc)) return rc;
15343
15344 AudioCodecType_T audioCodec;
15345 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15346 if (FAILED(rc)) return rc;
15347
15348 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15349 if (FAILED(rc)) return rc;
15350
15351 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15352 if (FAILED(rc)) return rc;
15353
15354 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15355 if (FAILED(rc)) return rc;
15356
15357 /* Storage Controllers */
15358 StorageControllerType_T hdStorageControllerType;
15359 StorageBus_T hdStorageBusType;
15360 StorageControllerType_T dvdStorageControllerType;
15361 StorageBus_T dvdStorageBusType;
15362 BOOL recommendedFloppy;
15363 ComPtr<IStorageController> floppyController;
15364 ComPtr<IStorageController> hdController;
15365 ComPtr<IStorageController> dvdController;
15366 Utf8Str strFloppyName, strDVDName, strHDName;
15367
15368 /* GUI auto generates controller names using bus type. Do the same*/
15369 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15370
15371 /* Floppy recommended? add one. */
15372 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15373 if (FAILED(rc)) return rc;
15374 if (recommendedFloppy)
15375 {
15376 rc = addStorageController(strFloppyName,
15377 StorageBus_Floppy,
15378 floppyController);
15379 if (FAILED(rc)) return rc;
15380 }
15381
15382 /* Setup one DVD storage controller. */
15383 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15384 if (FAILED(rc)) return rc;
15385
15386 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15387 if (FAILED(rc)) return rc;
15388
15389 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15390
15391 rc = addStorageController(strDVDName,
15392 dvdStorageBusType,
15393 dvdController);
15394 if (FAILED(rc)) return rc;
15395
15396 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15397 if (FAILED(rc)) return rc;
15398
15399 /* Setup one HDD storage controller. */
15400 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15401 if (FAILED(rc)) return rc;
15402
15403 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15404 if (FAILED(rc)) return rc;
15405
15406 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15407
15408 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15409 {
15410 rc = addStorageController(strHDName,
15411 hdStorageBusType,
15412 hdController);
15413 if (FAILED(rc)) return rc;
15414
15415 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15416 if (FAILED(rc)) return rc;
15417 }
15418 else
15419 {
15420 /* The HD controller is the same as DVD: */
15421 hdController = dvdController;
15422 }
15423
15424 /* Limit the AHCI port count if it's used because windows has trouble with
15425 * too many ports and other guest (OS X in particular) may take extra long
15426 * boot: */
15427
15428 // pParent = static_cast<Medium*>(aP)
15429 IStorageController *temp = hdController;
15430 ComObjPtr<StorageController> storageController;
15431 storageController = static_cast<StorageController *>(temp);
15432
15433 // tempHDController = aHDController;
15434 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15435 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15436 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15437 storageController->COMSETTER(PortCount)(1);
15438
15439 /* USB stuff */
15440
15441 bool ohciEnabled = false;
15442
15443 ComPtr<IUSBController> usbController;
15444 BOOL recommendedUSB3;
15445 BOOL recommendedUSB;
15446 BOOL usbProxyAvailable;
15447
15448 getUSBProxyAvailable(&usbProxyAvailable);
15449 if (FAILED(rc)) return rc;
15450
15451 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15452 if (FAILED(rc)) return rc;
15453 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15454 if (FAILED(rc)) return rc;
15455
15456 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15457 {
15458#ifdef VBOX_WITH_EXTPACK
15459 /* USB 3.0 is only available if the proper ExtPack is installed. */
15460 ExtPackManager *aManager = mParent->i_getExtPackManager();
15461 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15462 {
15463 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15464 if (FAILED(rc)) return rc;
15465
15466 /* xHci includes OHCI */
15467 ohciEnabled = true;
15468 }
15469#endif
15470 }
15471 if ( !ohciEnabled
15472 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15473 {
15474 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15475 if (FAILED(rc)) return rc;
15476 ohciEnabled = true;
15477
15478#ifdef VBOX_WITH_EXTPACK
15479 /* USB 2.0 is only available if the proper ExtPack is installed.
15480 * Note. Configuring EHCI here and providing messages about
15481 * the missing extpack isn't exactly clean, but it is a
15482 * necessary evil to patch over legacy compatability issues
15483 * introduced by the new distribution model. */
15484 ExtPackManager *manager = mParent->i_getExtPackManager();
15485 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15486 {
15487 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15488 if (FAILED(rc)) return rc;
15489 }
15490#endif
15491 }
15492
15493 /* Set recommended human interface device types: */
15494 BOOL recommendedUSBHID;
15495 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15496 if (FAILED(rc)) return rc;
15497
15498 if (recommendedUSBHID)
15499 {
15500 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15501 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15502 if (!ohciEnabled && !usbDeviceFilters.isNull())
15503 {
15504 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15505 if (FAILED(rc)) return rc;
15506 }
15507 }
15508
15509 BOOL recommendedUSBTablet;
15510 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15511 if (FAILED(rc)) return rc;
15512
15513 if (recommendedUSBTablet)
15514 {
15515 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15516 if (!ohciEnabled && !usbDeviceFilters.isNull())
15517 {
15518 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15519 if (FAILED(rc)) return rc;
15520 }
15521 }
15522
15523 /* Enable the VMMDev testing feature for bootsector VMs: */
15524 if (osTypeId == "VBoxBS_64")
15525 {
15526 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15527 if (FAILED(rc))
15528 return rc;
15529 }
15530
15531 return S_OK;
15532}
15533
15534/* This isn't handled entirely by the wrapper generator yet. */
15535#ifdef VBOX_WITH_XPCOM
15536NS_DECL_CLASSINFO(SessionMachine)
15537NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15538
15539NS_DECL_CLASSINFO(SnapshotMachine)
15540NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15541#endif
Note: See TracBrowser for help on using the repository browser.

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