VirtualBox

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

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

Main/Machine+FE/Qt+VBoxManage/showvminfo: Attaching USB devices to a VM
when it isn't running should default to being marked as 'hot-pluggable'.
Further, attempts to remove the 'hot-pluggable' status of a USB device
via either the API or GUI should be impossible. Also update 'VBoxManage
showvminfo' to include the 'hot-pluggable' status of medium attachments
along with a few other IMediumAttachment attributes which were omitted
previously. bugref:8211

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 544.0 KB
Line 
1/* $Id: MachineImpl.cpp 94394 2022-03-29 15:50:26Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.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 hot-plugging if we attach 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 hot-plugging"),
3554 aName.c_str());
3555
3556 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3557 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3558 fHotplug = true;
3559
3560 // check that the port and device are not out of range
3561 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3562 if (FAILED(rc)) return rc;
3563
3564 /* check if the device slot is already busy */
3565 MediumAttachment *pAttachTemp;
3566 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3567 aName,
3568 aControllerPort,
3569 aDevice)))
3570 {
3571 Medium *pMedium = pAttachTemp->i_getMedium();
3572 if (pMedium)
3573 {
3574 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3575 return setError(VBOX_E_OBJECT_IN_USE,
3576 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3577 pMedium->i_getLocationFull().c_str(),
3578 aControllerPort,
3579 aDevice,
3580 aName.c_str());
3581 }
3582 else
3583 return setError(VBOX_E_OBJECT_IN_USE,
3584 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3585 aControllerPort, aDevice, aName.c_str());
3586 }
3587
3588 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3589 if (aMedium && medium.isNull())
3590 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3591
3592 AutoCaller mediumCaller(medium);
3593 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3594
3595 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3596
3597 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3598 && !medium.isNull()
3599 && ( medium->i_getType() != MediumType_Readonly
3600 || medium->i_getDeviceType() != DeviceType_DVD)
3601 )
3602 return setError(VBOX_E_OBJECT_IN_USE,
3603 tr("Medium '%s' is already attached to this virtual machine"),
3604 medium->i_getLocationFull().c_str());
3605
3606 if (!medium.isNull())
3607 {
3608 MediumType_T mtype = medium->i_getType();
3609 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3610 // For DVDs it's not written to the config file, so needs no global config
3611 // version bump. For floppies it's a new attribute "type", which is ignored
3612 // by older VirtualBox version, so needs no global config version bump either.
3613 // For hard disks this type is not accepted.
3614 if (mtype == MediumType_MultiAttach)
3615 {
3616 // This type is new with VirtualBox 4.0 and therefore requires settings
3617 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3618 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3619 // two reasons: The medium type is a property of the media registry tree, which
3620 // can reside in the global config file (for pre-4.0 media); we would therefore
3621 // possibly need to bump the global config version. We don't want to do that though
3622 // because that might make downgrading to pre-4.0 impossible.
3623 // As a result, we can only use these two new types if the medium is NOT in the
3624 // global registry:
3625 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3626 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3627 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3628 )
3629 return setError(VBOX_E_INVALID_OBJECT_STATE,
3630 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3631 "to machines that were created with VirtualBox 4.0 or later"),
3632 medium->i_getLocationFull().c_str());
3633 }
3634 }
3635
3636 bool fIndirect = false;
3637 if (!medium.isNull())
3638 fIndirect = medium->i_isReadOnly();
3639 bool associate = true;
3640
3641 do
3642 {
3643 if ( aType == DeviceType_HardDisk
3644 && mMediumAttachments.isBackedUp())
3645 {
3646 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3647
3648 /* check if the medium was attached to the VM before we started
3649 * changing attachments in which case the attachment just needs to
3650 * be restored */
3651 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3652 {
3653 AssertReturn(!fIndirect, E_FAIL);
3654
3655 /* see if it's the same bus/channel/device */
3656 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3657 {
3658 /* the simplest case: restore the whole attachment
3659 * and return, nothing else to do */
3660 mMediumAttachments->push_back(pAttachTemp);
3661
3662 /* Reattach the medium to the VM. */
3663 if (fHotplug || fSilent)
3664 {
3665 mediumLock.release();
3666 treeLock.release();
3667 alock.release();
3668
3669 MediumLockList *pMediumLockList(new MediumLockList());
3670
3671 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3672 medium /* pToLockWrite */,
3673 false /* fMediumLockWriteAll */,
3674 NULL,
3675 *pMediumLockList);
3676 alock.acquire();
3677 if (FAILED(rc))
3678 delete pMediumLockList;
3679 else
3680 {
3681 mData->mSession.mLockedMedia.Unlock();
3682 alock.release();
3683 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3684 mData->mSession.mLockedMedia.Lock();
3685 alock.acquire();
3686 }
3687 alock.release();
3688
3689 if (SUCCEEDED(rc))
3690 {
3691 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3692 /* Remove lock list in case of error. */
3693 if (FAILED(rc))
3694 {
3695 mData->mSession.mLockedMedia.Unlock();
3696 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3697 mData->mSession.mLockedMedia.Lock();
3698 }
3699 }
3700 }
3701
3702 return S_OK;
3703 }
3704
3705 /* bus/channel/device differ; we need a new attachment object,
3706 * but don't try to associate it again */
3707 associate = false;
3708 break;
3709 }
3710 }
3711
3712 /* go further only if the attachment is to be indirect */
3713 if (!fIndirect)
3714 break;
3715
3716 /* perform the so called smart attachment logic for indirect
3717 * attachments. Note that smart attachment is only applicable to base
3718 * hard disks. */
3719
3720 if (medium->i_getParent().isNull())
3721 {
3722 /* first, investigate the backup copy of the current hard disk
3723 * attachments to make it possible to re-attach existing diffs to
3724 * another device slot w/o losing their contents */
3725 if (mMediumAttachments.isBackedUp())
3726 {
3727 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3728
3729 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3730 uint32_t foundLevel = 0;
3731
3732 for (MediumAttachmentList::const_iterator
3733 it = oldAtts.begin();
3734 it != oldAtts.end();
3735 ++it)
3736 {
3737 uint32_t level = 0;
3738 MediumAttachment *pAttach = *it;
3739 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3740 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3741 if (pMedium.isNull())
3742 continue;
3743
3744 if (pMedium->i_getBase(&level) == medium)
3745 {
3746 /* skip the hard disk if its currently attached (we
3747 * cannot attach the same hard disk twice) */
3748 if (i_findAttachment(*mMediumAttachments.data(),
3749 pMedium))
3750 continue;
3751
3752 /* matched device, channel and bus (i.e. attached to the
3753 * same place) will win and immediately stop the search;
3754 * otherwise the attachment that has the youngest
3755 * descendant of medium will be used
3756 */
3757 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3758 {
3759 /* the simplest case: restore the whole attachment
3760 * and return, nothing else to do */
3761 mMediumAttachments->push_back(*it);
3762
3763 /* Reattach the medium to the VM. */
3764 if (fHotplug || fSilent)
3765 {
3766 mediumLock.release();
3767 treeLock.release();
3768 alock.release();
3769
3770 MediumLockList *pMediumLockList(new MediumLockList());
3771
3772 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3773 medium /* pToLockWrite */,
3774 false /* fMediumLockWriteAll */,
3775 NULL,
3776 *pMediumLockList);
3777 alock.acquire();
3778 if (FAILED(rc))
3779 delete pMediumLockList;
3780 else
3781 {
3782 mData->mSession.mLockedMedia.Unlock();
3783 alock.release();
3784 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3785 mData->mSession.mLockedMedia.Lock();
3786 alock.acquire();
3787 }
3788 alock.release();
3789
3790 if (SUCCEEDED(rc))
3791 {
3792 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3793 /* Remove lock list in case of error. */
3794 if (FAILED(rc))
3795 {
3796 mData->mSession.mLockedMedia.Unlock();
3797 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3798 mData->mSession.mLockedMedia.Lock();
3799 }
3800 }
3801 }
3802
3803 return S_OK;
3804 }
3805 else if ( foundIt == oldAtts.end()
3806 || level > foundLevel /* prefer younger */
3807 )
3808 {
3809 foundIt = it;
3810 foundLevel = level;
3811 }
3812 }
3813 }
3814
3815 if (foundIt != oldAtts.end())
3816 {
3817 /* use the previously attached hard disk */
3818 medium = (*foundIt)->i_getMedium();
3819 mediumCaller.attach(medium);
3820 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3821 mediumLock.attach(medium);
3822 /* not implicit, doesn't require association with this VM */
3823 fIndirect = false;
3824 associate = false;
3825 /* go right to the MediumAttachment creation */
3826 break;
3827 }
3828 }
3829
3830 /* must give up the medium lock and medium tree lock as below we
3831 * go over snapshots, which needs a lock with higher lock order. */
3832 mediumLock.release();
3833 treeLock.release();
3834
3835 /* then, search through snapshots for the best diff in the given
3836 * hard disk's chain to base the new diff on */
3837
3838 ComObjPtr<Medium> base;
3839 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3840 while (snap)
3841 {
3842 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3843
3844 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3845
3846 MediumAttachment *pAttachFound = NULL;
3847 uint32_t foundLevel = 0;
3848
3849 for (MediumAttachmentList::const_iterator
3850 it = snapAtts.begin();
3851 it != snapAtts.end();
3852 ++it)
3853 {
3854 MediumAttachment *pAttach = *it;
3855 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3856 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3857 if (pMedium.isNull())
3858 continue;
3859
3860 uint32_t level = 0;
3861 if (pMedium->i_getBase(&level) == medium)
3862 {
3863 /* matched device, channel and bus (i.e. attached to the
3864 * same place) will win and immediately stop the search;
3865 * otherwise the attachment that has the youngest
3866 * descendant of medium will be used
3867 */
3868 if ( pAttach->i_getDevice() == aDevice
3869 && pAttach->i_getPort() == aControllerPort
3870 && pAttach->i_getControllerName() == aName
3871 )
3872 {
3873 pAttachFound = pAttach;
3874 break;
3875 }
3876 else if ( !pAttachFound
3877 || level > foundLevel /* prefer younger */
3878 )
3879 {
3880 pAttachFound = pAttach;
3881 foundLevel = level;
3882 }
3883 }
3884 }
3885
3886 if (pAttachFound)
3887 {
3888 base = pAttachFound->i_getMedium();
3889 break;
3890 }
3891
3892 snap = snap->i_getParent();
3893 }
3894
3895 /* re-lock medium tree and the medium, as we need it below */
3896 treeLock.acquire();
3897 mediumLock.acquire();
3898
3899 /* found a suitable diff, use it as a base */
3900 if (!base.isNull())
3901 {
3902 medium = base;
3903 mediumCaller.attach(medium);
3904 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3905 mediumLock.attach(medium);
3906 }
3907 }
3908
3909 Utf8Str strFullSnapshotFolder;
3910 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3911
3912 ComObjPtr<Medium> diff;
3913 diff.createObject();
3914 // store this diff in the same registry as the parent
3915 Guid uuidRegistryParent;
3916 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3917 {
3918 // parent image has no registry: this can happen if we're attaching a new immutable
3919 // image that has not yet been attached (medium then points to the base and we're
3920 // creating the diff image for the immutable, and the parent is not yet registered);
3921 // put the parent in the machine registry then
3922 mediumLock.release();
3923 treeLock.release();
3924 alock.release();
3925 i_addMediumToRegistry(medium);
3926 alock.acquire();
3927 treeLock.acquire();
3928 mediumLock.acquire();
3929 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3930 }
3931 rc = diff->init(mParent,
3932 medium->i_getPreferredDiffFormat(),
3933 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3934 uuidRegistryParent,
3935 DeviceType_HardDisk);
3936 if (FAILED(rc)) return rc;
3937
3938 /* Apply the normal locking logic to the entire chain. */
3939 MediumLockList *pMediumLockList(new MediumLockList());
3940 mediumLock.release();
3941 treeLock.release();
3942 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3943 diff /* pToLockWrite */,
3944 false /* fMediumLockWriteAll */,
3945 medium,
3946 *pMediumLockList);
3947 treeLock.acquire();
3948 mediumLock.acquire();
3949 if (SUCCEEDED(rc))
3950 {
3951 mediumLock.release();
3952 treeLock.release();
3953 rc = pMediumLockList->Lock();
3954 treeLock.acquire();
3955 mediumLock.acquire();
3956 if (FAILED(rc))
3957 setError(rc,
3958 tr("Could not lock medium when creating diff '%s'"),
3959 diff->i_getLocationFull().c_str());
3960 else
3961 {
3962 /* will release the lock before the potentially lengthy
3963 * operation, so protect with the special state */
3964 MachineState_T oldState = mData->mMachineState;
3965 i_setMachineState(MachineState_SettingUp);
3966
3967 mediumLock.release();
3968 treeLock.release();
3969 alock.release();
3970
3971 rc = medium->i_createDiffStorage(diff,
3972 medium->i_getPreferredDiffVariant(),
3973 pMediumLockList,
3974 NULL /* aProgress */,
3975 true /* aWait */,
3976 false /* aNotify */);
3977
3978 alock.acquire();
3979 treeLock.acquire();
3980 mediumLock.acquire();
3981
3982 i_setMachineState(oldState);
3983 }
3984 }
3985
3986 /* Unlock the media and free the associated memory. */
3987 delete pMediumLockList;
3988
3989 if (FAILED(rc)) return rc;
3990
3991 /* use the created diff for the actual attachment */
3992 medium = diff;
3993 mediumCaller.attach(medium);
3994 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3995 mediumLock.attach(medium);
3996 }
3997 while (0);
3998
3999 ComObjPtr<MediumAttachment> attachment;
4000 attachment.createObject();
4001 rc = attachment->init(this,
4002 medium,
4003 aName,
4004 aControllerPort,
4005 aDevice,
4006 aType,
4007 fIndirect,
4008 false /* fPassthrough */,
4009 false /* fTempEject */,
4010 false /* fNonRotational */,
4011 false /* fDiscard */,
4012 fHotplug /* fHotPluggable */,
4013 Utf8Str::Empty);
4014 if (FAILED(rc)) return rc;
4015
4016 if (associate && !medium.isNull())
4017 {
4018 // as the last step, associate the medium to the VM
4019 rc = medium->i_addBackReference(mData->mUuid);
4020 // here we can fail because of Deleting, or being in process of creating a Diff
4021 if (FAILED(rc)) return rc;
4022
4023 mediumLock.release();
4024 treeLock.release();
4025 alock.release();
4026 i_addMediumToRegistry(medium);
4027 alock.acquire();
4028 treeLock.acquire();
4029 mediumLock.acquire();
4030 }
4031
4032 /* success: finally remember the attachment */
4033 i_setModified(IsModified_Storage);
4034 mMediumAttachments.backup();
4035 mMediumAttachments->push_back(attachment);
4036
4037 mediumLock.release();
4038 treeLock.release();
4039 alock.release();
4040
4041 if (fHotplug || fSilent)
4042 {
4043 if (!medium.isNull())
4044 {
4045 MediumLockList *pMediumLockList(new MediumLockList());
4046
4047 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4048 medium /* pToLockWrite */,
4049 false /* fMediumLockWriteAll */,
4050 NULL,
4051 *pMediumLockList);
4052 alock.acquire();
4053 if (FAILED(rc))
4054 delete pMediumLockList;
4055 else
4056 {
4057 mData->mSession.mLockedMedia.Unlock();
4058 alock.release();
4059 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4060 mData->mSession.mLockedMedia.Lock();
4061 alock.acquire();
4062 }
4063 alock.release();
4064 }
4065
4066 if (SUCCEEDED(rc))
4067 {
4068 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4069 /* Remove lock list in case of error. */
4070 if (FAILED(rc))
4071 {
4072 mData->mSession.mLockedMedia.Unlock();
4073 mData->mSession.mLockedMedia.Remove(attachment);
4074 mData->mSession.mLockedMedia.Lock();
4075 }
4076 }
4077 }
4078
4079 /* Save modified registries, but skip this machine as it's the caller's
4080 * job to save its settings like all other settings changes. */
4081 mParent->i_unmarkRegistryModified(i_getId());
4082 mParent->i_saveModifiedRegistries();
4083
4084 if (SUCCEEDED(rc))
4085 {
4086 if (fIndirect && medium != aM)
4087 mParent->i_onMediumConfigChanged(medium);
4088 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4089 }
4090
4091 return rc;
4092}
4093
4094HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4095 LONG aDevice)
4096{
4097 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4098 aName.c_str(), aControllerPort, aDevice));
4099
4100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4101
4102 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4103 if (FAILED(rc)) return rc;
4104
4105 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4106
4107 /* Check for an existing controller. */
4108 ComObjPtr<StorageController> ctl;
4109 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4110 if (FAILED(rc)) return rc;
4111
4112 StorageControllerType_T ctrlType;
4113 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4114 if (FAILED(rc))
4115 return setError(E_FAIL,
4116 tr("Could not get type of controller '%s'"),
4117 aName.c_str());
4118
4119 bool fSilent = false;
4120 Utf8Str strReconfig;
4121
4122 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4123 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4124 if ( mData->mMachineState == MachineState_Paused
4125 && strReconfig == "1")
4126 fSilent = true;
4127
4128 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4129 bool fHotplug = false;
4130 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4131 fHotplug = true;
4132
4133 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4134 return setError(VBOX_E_INVALID_VM_STATE,
4135 tr("Controller '%s' does not support hot-plugging"),
4136 aName.c_str());
4137
4138 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4139 aName,
4140 aControllerPort,
4141 aDevice);
4142 if (!pAttach)
4143 return setError(VBOX_E_OBJECT_NOT_FOUND,
4144 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4145 aDevice, aControllerPort, aName.c_str());
4146
4147 if (fHotplug && !pAttach->i_getHotPluggable())
4148 return setError(VBOX_E_NOT_SUPPORTED,
4149 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4150 aDevice, aControllerPort, aName.c_str());
4151
4152 /*
4153 * The VM has to detach the device before we delete any implicit diffs.
4154 * If this fails we can roll back without loosing data.
4155 */
4156 if (fHotplug || fSilent)
4157 {
4158 alock.release();
4159 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4160 alock.acquire();
4161 }
4162 if (FAILED(rc)) return rc;
4163
4164 /* If we are here everything went well and we can delete the implicit now. */
4165 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4166
4167 alock.release();
4168
4169 /* Save modified registries, but skip this machine as it's the caller's
4170 * job to save its settings like all other settings changes. */
4171 mParent->i_unmarkRegistryModified(i_getId());
4172 mParent->i_saveModifiedRegistries();
4173
4174 if (SUCCEEDED(rc))
4175 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4176
4177 return rc;
4178}
4179
4180HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4181 LONG aDevice, BOOL aPassthrough)
4182{
4183 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4184 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4185
4186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4187
4188 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4189 if (FAILED(rc)) return rc;
4190
4191 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4192
4193 /* Check for an existing controller. */
4194 ComObjPtr<StorageController> ctl;
4195 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4196 if (FAILED(rc)) return rc;
4197
4198 StorageControllerType_T ctrlType;
4199 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4200 if (FAILED(rc))
4201 return setError(E_FAIL,
4202 tr("Could not get type of controller '%s'"),
4203 aName.c_str());
4204
4205 bool fSilent = false;
4206 Utf8Str strReconfig;
4207
4208 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4209 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4210 if ( mData->mMachineState == MachineState_Paused
4211 && strReconfig == "1")
4212 fSilent = true;
4213
4214 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4215 bool fHotplug = false;
4216 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4217 fHotplug = true;
4218
4219 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4220 return setError(VBOX_E_INVALID_VM_STATE,
4221 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4222 aName.c_str());
4223
4224 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4225 aName,
4226 aControllerPort,
4227 aDevice);
4228 if (!pAttach)
4229 return setError(VBOX_E_OBJECT_NOT_FOUND,
4230 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4231 aDevice, aControllerPort, aName.c_str());
4232
4233
4234 i_setModified(IsModified_Storage);
4235 mMediumAttachments.backup();
4236
4237 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4238
4239 if (pAttach->i_getType() != DeviceType_DVD)
4240 return setError(E_INVALIDARG,
4241 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4242 aDevice, aControllerPort, aName.c_str());
4243
4244 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4245
4246 pAttach->i_updatePassthrough(!!aPassthrough);
4247
4248 attLock.release();
4249 alock.release();
4250 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4251 if (SUCCEEDED(rc) && fValueChanged)
4252 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4253
4254 return rc;
4255}
4256
4257HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4258 LONG aDevice, BOOL aTemporaryEject)
4259{
4260
4261 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4262 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4263
4264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4265
4266 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4267 if (FAILED(rc)) return rc;
4268
4269 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4270 aName,
4271 aControllerPort,
4272 aDevice);
4273 if (!pAttach)
4274 return setError(VBOX_E_OBJECT_NOT_FOUND,
4275 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4276 aDevice, aControllerPort, aName.c_str());
4277
4278
4279 i_setModified(IsModified_Storage);
4280 mMediumAttachments.backup();
4281
4282 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4283
4284 if (pAttach->i_getType() != DeviceType_DVD)
4285 return setError(E_INVALIDARG,
4286 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4287 aDevice, aControllerPort, aName.c_str());
4288 pAttach->i_updateTempEject(!!aTemporaryEject);
4289
4290 return S_OK;
4291}
4292
4293HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4294 LONG aDevice, BOOL aNonRotational)
4295{
4296
4297 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4298 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4299
4300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4301
4302 HRESULT rc = i_checkStateDependency(MutableStateDep);
4303 if (FAILED(rc)) return rc;
4304
4305 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4306
4307 if (Global::IsOnlineOrTransient(mData->mMachineState))
4308 return setError(VBOX_E_INVALID_VM_STATE,
4309 tr("Invalid machine state: %s"),
4310 Global::stringifyMachineState(mData->mMachineState));
4311
4312 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4313 aName,
4314 aControllerPort,
4315 aDevice);
4316 if (!pAttach)
4317 return setError(VBOX_E_OBJECT_NOT_FOUND,
4318 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4319 aDevice, aControllerPort, aName.c_str());
4320
4321
4322 i_setModified(IsModified_Storage);
4323 mMediumAttachments.backup();
4324
4325 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4326
4327 if (pAttach->i_getType() != DeviceType_HardDisk)
4328 return setError(E_INVALIDARG,
4329 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"),
4330 aDevice, aControllerPort, aName.c_str());
4331 pAttach->i_updateNonRotational(!!aNonRotational);
4332
4333 return S_OK;
4334}
4335
4336HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4337 LONG aDevice, BOOL aDiscard)
4338{
4339
4340 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4341 aName.c_str(), aControllerPort, aDevice, aDiscard));
4342
4343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4344
4345 HRESULT rc = i_checkStateDependency(MutableStateDep);
4346 if (FAILED(rc)) return rc;
4347
4348 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4349
4350 if (Global::IsOnlineOrTransient(mData->mMachineState))
4351 return setError(VBOX_E_INVALID_VM_STATE,
4352 tr("Invalid machine state: %s"),
4353 Global::stringifyMachineState(mData->mMachineState));
4354
4355 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4356 aName,
4357 aControllerPort,
4358 aDevice);
4359 if (!pAttach)
4360 return setError(VBOX_E_OBJECT_NOT_FOUND,
4361 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4362 aDevice, aControllerPort, aName.c_str());
4363
4364
4365 i_setModified(IsModified_Storage);
4366 mMediumAttachments.backup();
4367
4368 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4369
4370 if (pAttach->i_getType() != DeviceType_HardDisk)
4371 return setError(E_INVALIDARG,
4372 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"),
4373 aDevice, aControllerPort, aName.c_str());
4374 pAttach->i_updateDiscard(!!aDiscard);
4375
4376 return S_OK;
4377}
4378
4379HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4380 LONG aDevice, BOOL aHotPluggable)
4381{
4382 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4383 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4384
4385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4386
4387 HRESULT rc = i_checkStateDependency(MutableStateDep);
4388 if (FAILED(rc)) return rc;
4389
4390 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4391
4392 if (Global::IsOnlineOrTransient(mData->mMachineState))
4393 return setError(VBOX_E_INVALID_VM_STATE,
4394 tr("Invalid machine state: %s"),
4395 Global::stringifyMachineState(mData->mMachineState));
4396
4397 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4398 aName,
4399 aControllerPort,
4400 aDevice);
4401 if (!pAttach)
4402 return setError(VBOX_E_OBJECT_NOT_FOUND,
4403 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4404 aDevice, aControllerPort, aName.c_str());
4405
4406 /* Check for an existing controller. */
4407 ComObjPtr<StorageController> ctl;
4408 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4409 if (FAILED(rc)) return rc;
4410
4411 StorageControllerType_T ctrlType;
4412 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4413 if (FAILED(rc))
4414 return setError(E_FAIL,
4415 tr("Could not get type of controller '%s'"),
4416 aName.c_str());
4417
4418 if (!i_isControllerHotplugCapable(ctrlType))
4419 return setError(VBOX_E_NOT_SUPPORTED,
4420 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4421 aName.c_str());
4422
4423 /* silently ignore attempts to modify the hot-plug status of USB devices */
4424 if (ctrlType == StorageControllerType_USB)
4425 return S_OK;
4426
4427 i_setModified(IsModified_Storage);
4428 mMediumAttachments.backup();
4429
4430 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4431
4432 if (pAttach->i_getType() == DeviceType_Floppy)
4433 return setError(E_INVALIDARG,
4434 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"),
4435 aDevice, aControllerPort, aName.c_str());
4436 pAttach->i_updateHotPluggable(!!aHotPluggable);
4437
4438 return S_OK;
4439}
4440
4441HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4442 LONG aDevice)
4443{
4444 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4445 aName.c_str(), aControllerPort, aDevice));
4446
4447 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4448}
4449
4450HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4451 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4452{
4453 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4454 aName.c_str(), aControllerPort, aDevice));
4455
4456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4457
4458 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4459 if (FAILED(rc)) return rc;
4460
4461 if (Global::IsOnlineOrTransient(mData->mMachineState))
4462 return setError(VBOX_E_INVALID_VM_STATE,
4463 tr("Invalid machine state: %s"),
4464 Global::stringifyMachineState(mData->mMachineState));
4465
4466 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4467 aName,
4468 aControllerPort,
4469 aDevice);
4470 if (!pAttach)
4471 return setError(VBOX_E_OBJECT_NOT_FOUND,
4472 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4473 aDevice, aControllerPort, aName.c_str());
4474
4475
4476 i_setModified(IsModified_Storage);
4477 mMediumAttachments.backup();
4478
4479 IBandwidthGroup *iB = aBandwidthGroup;
4480 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4481 if (aBandwidthGroup && group.isNull())
4482 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4483
4484 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4485
4486 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4487 if (strBandwidthGroupOld.isNotEmpty())
4488 {
4489 /* Get the bandwidth group object and release it - this must not fail. */
4490 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4491 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4492 Assert(SUCCEEDED(rc));
4493
4494 pBandwidthGroupOld->i_release();
4495 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4496 }
4497
4498 if (!group.isNull())
4499 {
4500 group->i_reference();
4501 pAttach->i_updateBandwidthGroup(group->i_getName());
4502 }
4503
4504 return S_OK;
4505}
4506
4507HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4508 LONG aControllerPort,
4509 LONG aDevice,
4510 DeviceType_T aType)
4511{
4512 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4513 aName.c_str(), aControllerPort, aDevice, aType));
4514
4515 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4516}
4517
4518
4519HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4520 LONG aControllerPort,
4521 LONG aDevice,
4522 BOOL aForce)
4523{
4524 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4525 aName.c_str(), aControllerPort, aForce));
4526
4527 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4528}
4529
4530HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4531 LONG aControllerPort,
4532 LONG aDevice,
4533 const ComPtr<IMedium> &aMedium,
4534 BOOL aForce)
4535{
4536 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4537 aName.c_str(), aControllerPort, aDevice, aForce));
4538
4539 // request the host lock first, since might be calling Host methods for getting host drives;
4540 // next, protect the media tree all the while we're in here, as well as our member variables
4541 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4542 this->lockHandle(),
4543 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4544
4545 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4546 if (FAILED(hrc)) return hrc;
4547
4548 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4549 aName,
4550 aControllerPort,
4551 aDevice);
4552 if (pAttach.isNull())
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557 /* Remember previously mounted medium. The medium before taking the
4558 * backup is not necessarily the same thing. */
4559 ComObjPtr<Medium> oldmedium;
4560 oldmedium = pAttach->i_getMedium();
4561
4562 IMedium *iM = aMedium;
4563 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4564 if (aMedium && pMedium.isNull())
4565 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4566
4567 AutoCaller mediumCaller(pMedium);
4568 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4569
4570 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4571 if (pMedium)
4572 {
4573 DeviceType_T mediumType = pAttach->i_getType();
4574 switch (mediumType)
4575 {
4576 case DeviceType_DVD:
4577 case DeviceType_Floppy:
4578 break;
4579
4580 default:
4581 return setError(VBOX_E_INVALID_OBJECT_STATE,
4582 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4583 aControllerPort,
4584 aDevice,
4585 aName.c_str());
4586 }
4587 }
4588
4589 i_setModified(IsModified_Storage);
4590 mMediumAttachments.backup();
4591
4592 {
4593 // The backup operation makes the pAttach reference point to the
4594 // old settings. Re-get the correct reference.
4595 pAttach = i_findAttachment(*mMediumAttachments.data(),
4596 aName,
4597 aControllerPort,
4598 aDevice);
4599 if (!oldmedium.isNull())
4600 oldmedium->i_removeBackReference(mData->mUuid);
4601 if (!pMedium.isNull())
4602 {
4603 pMedium->i_addBackReference(mData->mUuid);
4604
4605 mediumLock.release();
4606 multiLock.release();
4607 i_addMediumToRegistry(pMedium);
4608 multiLock.acquire();
4609 mediumLock.acquire();
4610 }
4611
4612 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4613 pAttach->i_updateMedium(pMedium);
4614 }
4615
4616 i_setModified(IsModified_Storage);
4617
4618 mediumLock.release();
4619 multiLock.release();
4620 HRESULT rc = i_onMediumChange(pAttach, aForce);
4621 multiLock.acquire();
4622 mediumLock.acquire();
4623
4624 /* On error roll back this change only. */
4625 if (FAILED(rc))
4626 {
4627 if (!pMedium.isNull())
4628 pMedium->i_removeBackReference(mData->mUuid);
4629 pAttach = i_findAttachment(*mMediumAttachments.data(),
4630 aName,
4631 aControllerPort,
4632 aDevice);
4633 /* If the attachment is gone in the meantime, bail out. */
4634 if (pAttach.isNull())
4635 return rc;
4636 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4637 if (!oldmedium.isNull())
4638 oldmedium->i_addBackReference(mData->mUuid);
4639 pAttach->i_updateMedium(oldmedium);
4640 }
4641
4642 mediumLock.release();
4643 multiLock.release();
4644
4645 /* Save modified registries, but skip this machine as it's the caller's
4646 * job to save its settings like all other settings changes. */
4647 mParent->i_unmarkRegistryModified(i_getId());
4648 mParent->i_saveModifiedRegistries();
4649
4650 return rc;
4651}
4652HRESULT Machine::getMedium(const com::Utf8Str &aName,
4653 LONG aControllerPort,
4654 LONG aDevice,
4655 ComPtr<IMedium> &aMedium)
4656{
4657 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4658 aName.c_str(), aControllerPort, aDevice));
4659
4660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4661
4662 aMedium = NULL;
4663
4664 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4665 aName,
4666 aControllerPort,
4667 aDevice);
4668 if (pAttach.isNull())
4669 return setError(VBOX_E_OBJECT_NOT_FOUND,
4670 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4671 aDevice, aControllerPort, aName.c_str());
4672
4673 aMedium = pAttach->i_getMedium();
4674
4675 return S_OK;
4676}
4677
4678HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4679{
4680 if (aSlot < RT_ELEMENTS(mSerialPorts))
4681 {
4682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4683 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4684 return S_OK;
4685 }
4686 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4687}
4688
4689HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4690{
4691 if (aSlot < RT_ELEMENTS(mParallelPorts))
4692 {
4693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4694 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4695 return S_OK;
4696 }
4697 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4698}
4699
4700
4701HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4702{
4703 /* Do not assert if slot is out of range, just return the advertised
4704 status. testdriver/vbox.py triggers this in logVmInfo. */
4705 if (aSlot >= mNetworkAdapters.size())
4706 return setError(E_INVALIDARG,
4707 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4708 aSlot, mNetworkAdapters.size());
4709
4710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4711
4712 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4713
4714 return S_OK;
4715}
4716
4717HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4718{
4719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4720
4721 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4722 size_t i = 0;
4723 for (settings::StringsMap::const_iterator
4724 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4725 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4726 ++it, ++i)
4727 aKeys[i] = it->first;
4728
4729 return S_OK;
4730}
4731
4732 /**
4733 * @note Locks this object for reading.
4734 */
4735HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4736 com::Utf8Str &aValue)
4737{
4738 /* start with nothing found */
4739 aValue = "";
4740
4741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4744 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4745 // found:
4746 aValue = it->second; // source is a Utf8Str
4747
4748 /* return the result to caller (may be empty) */
4749 return S_OK;
4750}
4751
4752 /**
4753 * @note Locks mParent for writing + this object for writing.
4754 */
4755HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4756{
4757 /* Because control characters in aKey have caused problems in the settings
4758 * they are rejected unless the key should be deleted. */
4759 if (!aValue.isEmpty())
4760 {
4761 for (size_t i = 0; i < aKey.length(); ++i)
4762 {
4763 char ch = aKey[i];
4764 if (RTLocCIsCntrl(ch))
4765 return E_INVALIDARG;
4766 }
4767 }
4768
4769 Utf8Str strOldValue; // empty
4770
4771 // locking note: we only hold the read lock briefly to look up the old value,
4772 // then release it and call the onExtraCanChange callbacks. There is a small
4773 // chance of a race insofar as the callback might be called twice if two callers
4774 // change the same key at the same time, but that's a much better solution
4775 // than the deadlock we had here before. The actual changing of the extradata
4776 // is then performed under the write lock and race-free.
4777
4778 // look up the old value first; if nothing has changed then we need not do anything
4779 {
4780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4781
4782 // For snapshots don't even think about allowing changes, extradata
4783 // is global for a machine, so there is nothing snapshot specific.
4784 if (i_isSnapshotMachine())
4785 return setError(VBOX_E_INVALID_VM_STATE,
4786 tr("Cannot set extradata for a snapshot"));
4787
4788 // check if the right IMachine instance is used
4789 if (mData->mRegistered && !i_isSessionMachine())
4790 return setError(VBOX_E_INVALID_VM_STATE,
4791 tr("Cannot set extradata for an immutable machine"));
4792
4793 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4794 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4795 strOldValue = it->second;
4796 }
4797
4798 bool fChanged;
4799 if ((fChanged = (strOldValue != aValue)))
4800 {
4801 // ask for permission from all listeners outside the locks;
4802 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4803 // lock to copy the list of callbacks to invoke
4804 Bstr bstrError;
4805 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4806 {
4807 const char *sep = bstrError.isEmpty() ? "" : ": ";
4808 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4809 return setError(E_ACCESSDENIED,
4810 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4811 aKey.c_str(),
4812 aValue.c_str(),
4813 sep,
4814 bstrError.raw());
4815 }
4816
4817 // data is changing and change not vetoed: then write it out under the lock
4818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4819
4820 if (aValue.isEmpty())
4821 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4822 else
4823 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4824 // creates a new key if needed
4825
4826 bool fNeedsGlobalSaveSettings = false;
4827 // This saving of settings is tricky: there is no "old state" for the
4828 // extradata items at all (unlike all other settings), so the old/new
4829 // settings comparison would give a wrong result!
4830 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4831
4832 if (fNeedsGlobalSaveSettings)
4833 {
4834 // save the global settings; for that we should hold only the VirtualBox lock
4835 alock.release();
4836 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4837 mParent->i_saveSettings();
4838 }
4839 }
4840
4841 // fire notification outside the lock
4842 if (fChanged)
4843 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4844
4845 return S_OK;
4846}
4847
4848HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4849{
4850 aProgress = NULL;
4851 NOREF(aSettingsFilePath);
4852 ReturnComNotImplemented();
4853}
4854
4855HRESULT Machine::saveSettings()
4856{
4857 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4858
4859 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4860 if (FAILED(rc)) return rc;
4861
4862 /* the settings file path may never be null */
4863 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4864
4865 /* save all VM data excluding snapshots */
4866 bool fNeedsGlobalSaveSettings = false;
4867 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4868 mlock.release();
4869
4870 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4871 {
4872 // save the global settings; for that we should hold only the VirtualBox lock
4873 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4874 rc = mParent->i_saveSettings();
4875 }
4876
4877 return rc;
4878}
4879
4880
4881HRESULT Machine::discardSettings()
4882{
4883 /*
4884 * We need to take the machine list lock here as well as the machine one
4885 * or we'll get into trouble should any media stuff require rolling back.
4886 *
4887 * Details:
4888 *
4889 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4890 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4891 * 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]
4892 * 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
4893 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4894 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4895 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4896 * 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
4897 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4898 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4899 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4900 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4901 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4902 * 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]
4903 * 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] (*)
4904 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4905 * 0:005> k
4906 * # Child-SP RetAddr Call Site
4907 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4908 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4909 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4910 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4911 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4912 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4913 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4914 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4915 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4916 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4917 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4918 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4919 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4920 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4921 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4922 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4923 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4924 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4925 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4926 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4927 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4928 *
4929 */
4930 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4932
4933 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4934 if (FAILED(rc)) return rc;
4935
4936 /*
4937 * during this rollback, the session will be notified if data has
4938 * been actually changed
4939 */
4940 i_rollback(true /* aNotify */);
4941
4942 return S_OK;
4943}
4944
4945/** @note Locks objects! */
4946HRESULT Machine::unregister(AutoCaller &autoCaller,
4947 CleanupMode_T aCleanupMode,
4948 std::vector<ComPtr<IMedium> > &aMedia)
4949{
4950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4951
4952 Guid id(i_getId());
4953
4954 if (mData->mSession.mState != SessionState_Unlocked)
4955 return setError(VBOX_E_INVALID_OBJECT_STATE,
4956 tr("Cannot unregister the machine '%s' while it is locked"),
4957 mUserData->s.strName.c_str());
4958
4959 // wait for state dependents to drop to zero
4960 i_ensureNoStateDependencies(alock);
4961
4962 if (!mData->mAccessible)
4963 {
4964 // inaccessible machines can only be unregistered; uninitialize ourselves
4965 // here because currently there may be no unregistered that are inaccessible
4966 // (this state combination is not supported). Note releasing the caller and
4967 // leaving the lock before calling uninit()
4968 alock.release();
4969 autoCaller.release();
4970
4971 uninit();
4972
4973 mParent->i_unregisterMachine(this, id);
4974 // calls VirtualBox::i_saveSettings()
4975
4976 return S_OK;
4977 }
4978
4979 HRESULT rc = S_OK;
4980 mData->llFilesToDelete.clear();
4981
4982 if (!mSSData->strStateFilePath.isEmpty())
4983 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4984
4985 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4986 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4987 mData->llFilesToDelete.push_back(strNVRAMFile);
4988
4989 // This list collects the medium objects from all medium attachments
4990 // which we will detach from the machine and its snapshots, in a specific
4991 // order which allows for closing all media without getting "media in use"
4992 // errors, simply by going through the list from the front to the back:
4993 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4994 // and must be closed before the parent media from the snapshots, or closing the parents
4995 // will fail because they still have children);
4996 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4997 // the root ("first") snapshot of the machine.
4998 MediaList llMedia;
4999
5000 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5001 && mMediumAttachments->size()
5002 )
5003 {
5004 // we have media attachments: detach them all and add the Medium objects to our list
5005 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5006 }
5007
5008 if (mData->mFirstSnapshot)
5009 {
5010 // add the media from the medium attachments of the snapshots to llMedia
5011 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5012 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5013 // into the children first
5014
5015 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5016 MachineState_T oldState = mData->mMachineState;
5017 mData->mMachineState = MachineState_DeletingSnapshot;
5018
5019 // make a copy of the first snapshot reference so the refcount does not
5020 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5021 // (would hang due to the AutoCaller voodoo)
5022 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5023
5024 // GO!
5025 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5026
5027 mData->mMachineState = oldState;
5028 }
5029
5030 if (FAILED(rc))
5031 {
5032 i_rollbackMedia();
5033 return rc;
5034 }
5035
5036 // commit all the media changes made above
5037 i_commitMedia();
5038
5039 mData->mRegistered = false;
5040
5041 // machine lock no longer needed
5042 alock.release();
5043
5044 /* Make sure that the settings of the current VM are not saved, because
5045 * they are rather crippled at this point to meet the cleanup expectations
5046 * and there's no point destroying the VM config on disk just because. */
5047 mParent->i_unmarkRegistryModified(id);
5048
5049 // return media to caller
5050 aMedia.resize(llMedia.size());
5051 size_t i = 0;
5052 for (MediaList::const_iterator
5053 it = llMedia.begin();
5054 it != llMedia.end();
5055 ++it, ++i)
5056 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5057
5058 mParent->i_unregisterMachine(this, id);
5059 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5060
5061 return S_OK;
5062}
5063
5064/**
5065 * Task record for deleting a machine config.
5066 */
5067class Machine::DeleteConfigTask
5068 : public Machine::Task
5069{
5070public:
5071 DeleteConfigTask(Machine *m,
5072 Progress *p,
5073 const Utf8Str &t,
5074 const RTCList<ComPtr<IMedium> > &llMediums,
5075 const StringsList &llFilesToDelete)
5076 : Task(m, p, t),
5077 m_llMediums(llMediums),
5078 m_llFilesToDelete(llFilesToDelete)
5079 {}
5080
5081private:
5082 void handler()
5083 {
5084 try
5085 {
5086 m_pMachine->i_deleteConfigHandler(*this);
5087 }
5088 catch (...)
5089 {
5090 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5091 }
5092 }
5093
5094 RTCList<ComPtr<IMedium> > m_llMediums;
5095 StringsList m_llFilesToDelete;
5096
5097 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5098};
5099
5100/**
5101 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5102 * SessionMachine::taskHandler().
5103 *
5104 * @note Locks this object for writing.
5105 *
5106 * @param task
5107 * @return
5108 */
5109void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5110{
5111 LogFlowThisFuncEnter();
5112
5113 AutoCaller autoCaller(this);
5114 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5115 if (FAILED(autoCaller.rc()))
5116 {
5117 /* we might have been uninitialized because the session was accidentally
5118 * closed by the client, so don't assert */
5119 HRESULT rc = setError(E_FAIL,
5120 tr("The session has been accidentally closed"));
5121 task.m_pProgress->i_notifyComplete(rc);
5122 LogFlowThisFuncLeave();
5123 return;
5124 }
5125
5126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5127
5128 HRESULT rc = S_OK;
5129
5130 try
5131 {
5132 ULONG uLogHistoryCount = 3;
5133 ComPtr<ISystemProperties> systemProperties;
5134 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5135 if (FAILED(rc)) throw rc;
5136
5137 if (!systemProperties.isNull())
5138 {
5139 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5140 if (FAILED(rc)) throw rc;
5141 }
5142
5143 MachineState_T oldState = mData->mMachineState;
5144 i_setMachineState(MachineState_SettingUp);
5145 alock.release();
5146 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5147 {
5148 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5149 {
5150 AutoCaller mac(pMedium);
5151 if (FAILED(mac.rc())) throw mac.rc();
5152 Utf8Str strLocation = pMedium->i_getLocationFull();
5153 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5154 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5155 if (FAILED(rc)) throw rc;
5156 }
5157 if (pMedium->i_isMediumFormatFile())
5158 {
5159 ComPtr<IProgress> pProgress2;
5160 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5161 if (FAILED(rc)) throw rc;
5162 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5163 if (FAILED(rc)) throw rc;
5164 }
5165
5166 /* Close the medium, deliberately without checking the return
5167 * code, and without leaving any trace in the error info, as
5168 * a failure here is a very minor issue, which shouldn't happen
5169 * as above we even managed to delete the medium. */
5170 {
5171 ErrorInfoKeeper eik;
5172 pMedium->Close();
5173 }
5174 }
5175 i_setMachineState(oldState);
5176 alock.acquire();
5177
5178 // delete the files pushed on the task list by Machine::Delete()
5179 // (this includes saved states of the machine and snapshots and
5180 // medium storage files from the IMedium list passed in, and the
5181 // machine XML file)
5182 for (StringsList::const_iterator
5183 it = task.m_llFilesToDelete.begin();
5184 it != task.m_llFilesToDelete.end();
5185 ++it)
5186 {
5187 const Utf8Str &strFile = *it;
5188 LogFunc(("Deleting file %s\n", strFile.c_str()));
5189 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5190 if (FAILED(rc)) throw rc;
5191
5192 int vrc = RTFileDelete(strFile.c_str());
5193 if (RT_FAILURE(vrc))
5194 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5195 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5196 }
5197
5198 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5199 if (FAILED(rc)) throw rc;
5200
5201 /* delete the settings only when the file actually exists */
5202 if (mData->pMachineConfigFile->fileExists())
5203 {
5204 /* Delete any backup or uncommitted XML files. Ignore failures.
5205 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5206 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5207 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5208 RTFileDelete(otherXml.c_str());
5209 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5210 RTFileDelete(otherXml.c_str());
5211
5212 /* delete the Logs folder, nothing important should be left
5213 * there (we don't check for errors because the user might have
5214 * some private files there that we don't want to delete) */
5215 Utf8Str logFolder;
5216 getLogFolder(logFolder);
5217 Assert(logFolder.length());
5218 if (RTDirExists(logFolder.c_str()))
5219 {
5220 /* Delete all VBox.log[.N] files from the Logs folder
5221 * (this must be in sync with the rotation logic in
5222 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5223 * files that may have been created by the GUI. */
5224 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5225 RTFileDelete(log.c_str());
5226 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5227 RTFileDelete(log.c_str());
5228 for (ULONG i = uLogHistoryCount; i > 0; i--)
5229 {
5230 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5231 RTFileDelete(log.c_str());
5232 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5233 RTFileDelete(log.c_str());
5234 }
5235 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5236 RTFileDelete(log.c_str());
5237#if defined(RT_OS_WINDOWS)
5238 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5239 RTFileDelete(log.c_str());
5240 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5241 RTFileDelete(log.c_str());
5242#endif
5243
5244 RTDirRemove(logFolder.c_str());
5245 }
5246
5247 /* delete the Snapshots folder, nothing important should be left
5248 * there (we don't check for errors because the user might have
5249 * some private files there that we don't want to delete) */
5250 Utf8Str strFullSnapshotFolder;
5251 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5252 Assert(!strFullSnapshotFolder.isEmpty());
5253 if (RTDirExists(strFullSnapshotFolder.c_str()))
5254 RTDirRemove(strFullSnapshotFolder.c_str());
5255
5256 // delete the directory that contains the settings file, but only
5257 // if it matches the VM name
5258 Utf8Str settingsDir;
5259 if (i_isInOwnDir(&settingsDir))
5260 RTDirRemove(settingsDir.c_str());
5261 }
5262
5263 alock.release();
5264
5265 mParent->i_saveModifiedRegistries();
5266 }
5267 catch (HRESULT aRC) { rc = aRC; }
5268
5269 task.m_pProgress->i_notifyComplete(rc);
5270
5271 LogFlowThisFuncLeave();
5272}
5273
5274HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5275{
5276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5277
5278 HRESULT rc = i_checkStateDependency(MutableStateDep);
5279 if (FAILED(rc)) return rc;
5280
5281 if (mData->mRegistered)
5282 return setError(VBOX_E_INVALID_VM_STATE,
5283 tr("Cannot delete settings of a registered machine"));
5284
5285 // collect files to delete
5286 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5287 // machine config file
5288 if (mData->pMachineConfigFile->fileExists())
5289 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5290 // backup of machine config file
5291 Utf8Str strTmp(mData->m_strConfigFileFull);
5292 strTmp.append("-prev");
5293 if (RTFileExists(strTmp.c_str()))
5294 llFilesToDelete.push_back(strTmp);
5295
5296 RTCList<ComPtr<IMedium> > llMediums;
5297 for (size_t i = 0; i < aMedia.size(); ++i)
5298 {
5299 IMedium *pIMedium(aMedia[i]);
5300 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5301 if (pMedium.isNull())
5302 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5303 SafeArray<BSTR> ids;
5304 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5305 if (FAILED(rc)) return rc;
5306 /* At this point the medium should not have any back references
5307 * anymore. If it has it is attached to another VM and *must* not
5308 * deleted. */
5309 if (ids.size() < 1)
5310 llMediums.append(pMedium);
5311 }
5312
5313 ComObjPtr<Progress> pProgress;
5314 pProgress.createObject();
5315 rc = pProgress->init(i_getVirtualBox(),
5316 static_cast<IMachine*>(this) /* aInitiator */,
5317 tr("Deleting files"),
5318 true /* fCancellable */,
5319 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5320 tr("Collecting file inventory"));
5321 if (FAILED(rc))
5322 return rc;
5323
5324 /* create and start the task on a separate thread (note that it will not
5325 * start working until we release alock) */
5326 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5327 rc = pTask->createThread();
5328 pTask = NULL;
5329 if (FAILED(rc))
5330 return rc;
5331
5332 pProgress.queryInterfaceTo(aProgress.asOutParam());
5333
5334 LogFlowFuncLeave();
5335
5336 return S_OK;
5337}
5338
5339HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5340{
5341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5342
5343 ComObjPtr<Snapshot> pSnapshot;
5344 HRESULT rc;
5345
5346 if (aNameOrId.isEmpty())
5347 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5348 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5349 else
5350 {
5351 Guid uuid(aNameOrId);
5352 if (uuid.isValid())
5353 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5354 else
5355 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5356 }
5357 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5358
5359 return rc;
5360}
5361
5362HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5363 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5364{
5365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5366
5367 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5368 if (FAILED(rc)) return rc;
5369
5370 ComObjPtr<SharedFolder> sharedFolder;
5371 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5372 if (SUCCEEDED(rc))
5373 return setError(VBOX_E_OBJECT_IN_USE,
5374 tr("Shared folder named '%s' already exists"),
5375 aName.c_str());
5376
5377 sharedFolder.createObject();
5378 rc = sharedFolder->init(i_getMachine(),
5379 aName,
5380 aHostPath,
5381 !!aWritable,
5382 !!aAutomount,
5383 aAutoMountPoint,
5384 true /* fFailOnError */);
5385 if (FAILED(rc)) return rc;
5386
5387 i_setModified(IsModified_SharedFolders);
5388 mHWData.backup();
5389 mHWData->mSharedFolders.push_back(sharedFolder);
5390
5391 /* inform the direct session if any */
5392 alock.release();
5393 i_onSharedFolderChange();
5394
5395 return S_OK;
5396}
5397
5398HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5399{
5400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5401
5402 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5403 if (FAILED(rc)) return rc;
5404
5405 ComObjPtr<SharedFolder> sharedFolder;
5406 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5407 if (FAILED(rc)) return rc;
5408
5409 i_setModified(IsModified_SharedFolders);
5410 mHWData.backup();
5411 mHWData->mSharedFolders.remove(sharedFolder);
5412
5413 /* inform the direct session if any */
5414 alock.release();
5415 i_onSharedFolderChange();
5416
5417 return S_OK;
5418}
5419
5420HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5421{
5422 /* start with No */
5423 *aCanShow = FALSE;
5424
5425 ComPtr<IInternalSessionControl> directControl;
5426 {
5427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5428
5429 if (mData->mSession.mState != SessionState_Locked)
5430 return setError(VBOX_E_INVALID_VM_STATE,
5431 tr("Machine is not locked for session (session state: %s)"),
5432 Global::stringifySessionState(mData->mSession.mState));
5433
5434 if (mData->mSession.mLockType == LockType_VM)
5435 directControl = mData->mSession.mDirectControl;
5436 }
5437
5438 /* ignore calls made after #OnSessionEnd() is called */
5439 if (!directControl)
5440 return S_OK;
5441
5442 LONG64 dummy;
5443 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5444}
5445
5446HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5447{
5448 ComPtr<IInternalSessionControl> directControl;
5449 {
5450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5451
5452 if (mData->mSession.mState != SessionState_Locked)
5453 return setError(E_FAIL,
5454 tr("Machine is not locked for session (session state: %s)"),
5455 Global::stringifySessionState(mData->mSession.mState));
5456
5457 if (mData->mSession.mLockType == LockType_VM)
5458 directControl = mData->mSession.mDirectControl;
5459 }
5460
5461 /* ignore calls made after #OnSessionEnd() is called */
5462 if (!directControl)
5463 return S_OK;
5464
5465 BOOL dummy;
5466 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5467}
5468
5469#ifdef VBOX_WITH_GUEST_PROPS
5470/**
5471 * Look up a guest property in VBoxSVC's internal structures.
5472 */
5473HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5474 com::Utf8Str &aValue,
5475 LONG64 *aTimestamp,
5476 com::Utf8Str &aFlags) const
5477{
5478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5479
5480 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5481 if (it != mHWData->mGuestProperties.end())
5482 {
5483 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5484 aValue = it->second.strValue;
5485 *aTimestamp = it->second.mTimestamp;
5486 GuestPropWriteFlags(it->second.mFlags, szFlags);
5487 aFlags = Utf8Str(szFlags);
5488 }
5489
5490 return S_OK;
5491}
5492
5493/**
5494 * Query the VM that a guest property belongs to for the property.
5495 * @returns E_ACCESSDENIED if the VM process is not available or not
5496 * currently handling queries and the lookup should then be done in
5497 * VBoxSVC.
5498 */
5499HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5500 com::Utf8Str &aValue,
5501 LONG64 *aTimestamp,
5502 com::Utf8Str &aFlags) const
5503{
5504 HRESULT rc = S_OK;
5505 Bstr bstrValue;
5506 Bstr bstrFlags;
5507
5508 ComPtr<IInternalSessionControl> directControl;
5509 {
5510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5511 if (mData->mSession.mLockType == LockType_VM)
5512 directControl = mData->mSession.mDirectControl;
5513 }
5514
5515 /* ignore calls made after #OnSessionEnd() is called */
5516 if (!directControl)
5517 rc = E_ACCESSDENIED;
5518 else
5519 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5520 0 /* accessMode */,
5521 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5522
5523 aValue = bstrValue;
5524 aFlags = bstrFlags;
5525
5526 return rc;
5527}
5528#endif // VBOX_WITH_GUEST_PROPS
5529
5530HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5531 com::Utf8Str &aValue,
5532 LONG64 *aTimestamp,
5533 com::Utf8Str &aFlags)
5534{
5535#ifndef VBOX_WITH_GUEST_PROPS
5536 ReturnComNotImplemented();
5537#else // VBOX_WITH_GUEST_PROPS
5538
5539 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5540
5541 if (rc == E_ACCESSDENIED)
5542 /* The VM is not running or the service is not (yet) accessible */
5543 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5544 return rc;
5545#endif // VBOX_WITH_GUEST_PROPS
5546}
5547
5548HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5549{
5550 LONG64 dummyTimestamp;
5551 com::Utf8Str dummyFlags;
5552 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5553 return rc;
5554
5555}
5556HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5557{
5558 com::Utf8Str dummyFlags;
5559 com::Utf8Str dummyValue;
5560 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5561 return rc;
5562}
5563
5564#ifdef VBOX_WITH_GUEST_PROPS
5565/**
5566 * Set a guest property in VBoxSVC's internal structures.
5567 */
5568HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5569 const com::Utf8Str &aFlags, bool fDelete)
5570{
5571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5572 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5573 if (FAILED(rc)) return rc;
5574
5575 try
5576 {
5577 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5578 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5579 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5580
5581 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5582 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5583
5584 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5585 if (it == mHWData->mGuestProperties.end())
5586 {
5587 if (!fDelete)
5588 {
5589 i_setModified(IsModified_MachineData);
5590 mHWData.backupEx();
5591
5592 RTTIMESPEC time;
5593 HWData::GuestProperty prop;
5594 prop.strValue = aValue;
5595 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5596 prop.mFlags = fFlags;
5597 mHWData->mGuestProperties[aName] = prop;
5598 }
5599 }
5600 else
5601 {
5602 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5603 {
5604 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5605 }
5606 else
5607 {
5608 i_setModified(IsModified_MachineData);
5609 mHWData.backupEx();
5610
5611 /* The backupEx() operation invalidates our iterator,
5612 * so get a new one. */
5613 it = mHWData->mGuestProperties.find(aName);
5614 Assert(it != mHWData->mGuestProperties.end());
5615
5616 if (!fDelete)
5617 {
5618 RTTIMESPEC time;
5619 it->second.strValue = aValue;
5620 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5621 it->second.mFlags = fFlags;
5622 }
5623 else
5624 mHWData->mGuestProperties.erase(it);
5625 }
5626 }
5627
5628 if (SUCCEEDED(rc))
5629 {
5630 alock.release();
5631
5632 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5633 }
5634 }
5635 catch (std::bad_alloc &)
5636 {
5637 rc = E_OUTOFMEMORY;
5638 }
5639
5640 return rc;
5641}
5642
5643/**
5644 * Set a property on the VM that that property belongs to.
5645 * @returns E_ACCESSDENIED if the VM process is not available or not
5646 * currently handling queries and the setting should then be done in
5647 * VBoxSVC.
5648 */
5649HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5650 const com::Utf8Str &aFlags, bool fDelete)
5651{
5652 HRESULT rc;
5653
5654 try
5655 {
5656 ComPtr<IInternalSessionControl> directControl;
5657 {
5658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5659 if (mData->mSession.mLockType == LockType_VM)
5660 directControl = mData->mSession.mDirectControl;
5661 }
5662
5663 Bstr dummy1; /* will not be changed (setter) */
5664 Bstr dummy2; /* will not be changed (setter) */
5665 LONG64 dummy64;
5666 if (!directControl)
5667 rc = E_ACCESSDENIED;
5668 else
5669 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5670 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5671 fDelete ? 2 : 1 /* accessMode */,
5672 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5673 }
5674 catch (std::bad_alloc &)
5675 {
5676 rc = E_OUTOFMEMORY;
5677 }
5678
5679 return rc;
5680}
5681#endif // VBOX_WITH_GUEST_PROPS
5682
5683HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5684 const com::Utf8Str &aFlags)
5685{
5686#ifndef VBOX_WITH_GUEST_PROPS
5687 ReturnComNotImplemented();
5688#else // VBOX_WITH_GUEST_PROPS
5689 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5690 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5691
5692 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5693 if (rc == E_ACCESSDENIED)
5694 /* The VM is not running or the service is not (yet) accessible */
5695 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5696 return rc;
5697#endif // VBOX_WITH_GUEST_PROPS
5698}
5699
5700HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5701{
5702 return setGuestProperty(aProperty, aValue, "");
5703}
5704
5705HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5706{
5707#ifndef VBOX_WITH_GUEST_PROPS
5708 ReturnComNotImplemented();
5709#else // VBOX_WITH_GUEST_PROPS
5710 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5711 if (rc == E_ACCESSDENIED)
5712 /* The VM is not running or the service is not (yet) accessible */
5713 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5714 return rc;
5715#endif // VBOX_WITH_GUEST_PROPS
5716}
5717
5718#ifdef VBOX_WITH_GUEST_PROPS
5719/**
5720 * Enumerate the guest properties in VBoxSVC's internal structures.
5721 */
5722HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5723 std::vector<com::Utf8Str> &aNames,
5724 std::vector<com::Utf8Str> &aValues,
5725 std::vector<LONG64> &aTimestamps,
5726 std::vector<com::Utf8Str> &aFlags)
5727{
5728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5729 Utf8Str strPatterns(aPatterns);
5730
5731 /*
5732 * Look for matching patterns and build up a list.
5733 */
5734 HWData::GuestPropertyMap propMap;
5735 for (HWData::GuestPropertyMap::const_iterator
5736 it = mHWData->mGuestProperties.begin();
5737 it != mHWData->mGuestProperties.end();
5738 ++it)
5739 {
5740 if ( strPatterns.isEmpty()
5741 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5742 RTSTR_MAX,
5743 it->first.c_str(),
5744 RTSTR_MAX,
5745 NULL)
5746 )
5747 propMap.insert(*it);
5748 }
5749
5750 alock.release();
5751
5752 /*
5753 * And build up the arrays for returning the property information.
5754 */
5755 size_t cEntries = propMap.size();
5756
5757 aNames.resize(cEntries);
5758 aValues.resize(cEntries);
5759 aTimestamps.resize(cEntries);
5760 aFlags.resize(cEntries);
5761
5762 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5763 size_t i = 0;
5764 for (HWData::GuestPropertyMap::const_iterator
5765 it = propMap.begin();
5766 it != propMap.end();
5767 ++it, ++i)
5768 {
5769 aNames[i] = it->first;
5770 aValues[i] = it->second.strValue;
5771 aTimestamps[i] = it->second.mTimestamp;
5772 GuestPropWriteFlags(it->second.mFlags, szFlags);
5773 aFlags[i] = Utf8Str(szFlags);
5774
5775 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5776 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5777 }
5778
5779 return S_OK;
5780}
5781
5782/**
5783 * Enumerate the properties managed by a VM.
5784 * @returns E_ACCESSDENIED if the VM process is not available or not
5785 * currently handling queries and the setting should then be done in
5786 * VBoxSVC.
5787 */
5788HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5789 std::vector<com::Utf8Str> &aNames,
5790 std::vector<com::Utf8Str> &aValues,
5791 std::vector<LONG64> &aTimestamps,
5792 std::vector<com::Utf8Str> &aFlags)
5793{
5794 HRESULT rc;
5795 ComPtr<IInternalSessionControl> directControl;
5796 {
5797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5798 if (mData->mSession.mLockType == LockType_VM)
5799 directControl = mData->mSession.mDirectControl;
5800 }
5801
5802 com::SafeArray<BSTR> bNames;
5803 com::SafeArray<BSTR> bValues;
5804 com::SafeArray<LONG64> bTimestamps;
5805 com::SafeArray<BSTR> bFlags;
5806
5807 if (!directControl)
5808 rc = E_ACCESSDENIED;
5809 else
5810 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5811 ComSafeArrayAsOutParam(bNames),
5812 ComSafeArrayAsOutParam(bValues),
5813 ComSafeArrayAsOutParam(bTimestamps),
5814 ComSafeArrayAsOutParam(bFlags));
5815 size_t i;
5816 aNames.resize(bNames.size());
5817 for (i = 0; i < bNames.size(); ++i)
5818 aNames[i] = Utf8Str(bNames[i]);
5819 aValues.resize(bValues.size());
5820 for (i = 0; i < bValues.size(); ++i)
5821 aValues[i] = Utf8Str(bValues[i]);
5822 aTimestamps.resize(bTimestamps.size());
5823 for (i = 0; i < bTimestamps.size(); ++i)
5824 aTimestamps[i] = bTimestamps[i];
5825 aFlags.resize(bFlags.size());
5826 for (i = 0; i < bFlags.size(); ++i)
5827 aFlags[i] = Utf8Str(bFlags[i]);
5828
5829 return rc;
5830}
5831#endif // VBOX_WITH_GUEST_PROPS
5832HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5833 std::vector<com::Utf8Str> &aNames,
5834 std::vector<com::Utf8Str> &aValues,
5835 std::vector<LONG64> &aTimestamps,
5836 std::vector<com::Utf8Str> &aFlags)
5837{
5838#ifndef VBOX_WITH_GUEST_PROPS
5839 ReturnComNotImplemented();
5840#else // VBOX_WITH_GUEST_PROPS
5841
5842 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5843
5844 if (rc == E_ACCESSDENIED)
5845 /* The VM is not running or the service is not (yet) accessible */
5846 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5847 return rc;
5848#endif // VBOX_WITH_GUEST_PROPS
5849}
5850
5851HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5852 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5853{
5854 MediumAttachmentList atts;
5855
5856 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5857 if (FAILED(rc)) return rc;
5858
5859 aMediumAttachments.resize(atts.size());
5860 size_t i = 0;
5861 for (MediumAttachmentList::const_iterator
5862 it = atts.begin();
5863 it != atts.end();
5864 ++it, ++i)
5865 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5866
5867 return S_OK;
5868}
5869
5870HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5871 LONG aControllerPort,
5872 LONG aDevice,
5873 ComPtr<IMediumAttachment> &aAttachment)
5874{
5875 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5876 aName.c_str(), aControllerPort, aDevice));
5877
5878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5879
5880 aAttachment = NULL;
5881
5882 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5883 aName,
5884 aControllerPort,
5885 aDevice);
5886 if (pAttach.isNull())
5887 return setError(VBOX_E_OBJECT_NOT_FOUND,
5888 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5889 aDevice, aControllerPort, aName.c_str());
5890
5891 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5892
5893 return S_OK;
5894}
5895
5896
5897HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5898 StorageBus_T aConnectionType,
5899 ComPtr<IStorageController> &aController)
5900{
5901 if ( (aConnectionType <= StorageBus_Null)
5902 || (aConnectionType > StorageBus_VirtioSCSI))
5903 return setError(E_INVALIDARG,
5904 tr("Invalid connection type: %d"),
5905 aConnectionType);
5906
5907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5908
5909 HRESULT rc = i_checkStateDependency(MutableStateDep);
5910 if (FAILED(rc)) return rc;
5911
5912 /* try to find one with the name first. */
5913 ComObjPtr<StorageController> ctrl;
5914
5915 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5916 if (SUCCEEDED(rc))
5917 return setError(VBOX_E_OBJECT_IN_USE,
5918 tr("Storage controller named '%s' already exists"),
5919 aName.c_str());
5920
5921 ctrl.createObject();
5922
5923 /* get a new instance number for the storage controller */
5924 ULONG ulInstance = 0;
5925 bool fBootable = true;
5926 for (StorageControllerList::const_iterator
5927 it = mStorageControllers->begin();
5928 it != mStorageControllers->end();
5929 ++it)
5930 {
5931 if ((*it)->i_getStorageBus() == aConnectionType)
5932 {
5933 ULONG ulCurInst = (*it)->i_getInstance();
5934
5935 if (ulCurInst >= ulInstance)
5936 ulInstance = ulCurInst + 1;
5937
5938 /* Only one controller of each type can be marked as bootable. */
5939 if ((*it)->i_getBootable())
5940 fBootable = false;
5941 }
5942 }
5943
5944 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5945 if (FAILED(rc)) return rc;
5946
5947 i_setModified(IsModified_Storage);
5948 mStorageControllers.backup();
5949 mStorageControllers->push_back(ctrl);
5950
5951 ctrl.queryInterfaceTo(aController.asOutParam());
5952
5953 /* inform the direct session if any */
5954 alock.release();
5955 i_onStorageControllerChange(i_getId(), aName);
5956
5957 return S_OK;
5958}
5959
5960HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5961 ComPtr<IStorageController> &aStorageController)
5962{
5963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5964
5965 ComObjPtr<StorageController> ctrl;
5966
5967 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5968 if (SUCCEEDED(rc))
5969 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5970
5971 return rc;
5972}
5973
5974HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5975 ULONG aInstance,
5976 ComPtr<IStorageController> &aStorageController)
5977{
5978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5979
5980 for (StorageControllerList::const_iterator
5981 it = mStorageControllers->begin();
5982 it != mStorageControllers->end();
5983 ++it)
5984 {
5985 if ( (*it)->i_getStorageBus() == aConnectionType
5986 && (*it)->i_getInstance() == aInstance)
5987 {
5988 (*it).queryInterfaceTo(aStorageController.asOutParam());
5989 return S_OK;
5990 }
5991 }
5992
5993 return setError(VBOX_E_OBJECT_NOT_FOUND,
5994 tr("Could not find a storage controller with instance number '%lu'"),
5995 aInstance);
5996}
5997
5998HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5999{
6000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6001
6002 HRESULT rc = i_checkStateDependency(MutableStateDep);
6003 if (FAILED(rc)) return rc;
6004
6005 ComObjPtr<StorageController> ctrl;
6006
6007 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6008 if (SUCCEEDED(rc))
6009 {
6010 /* Ensure that only one controller of each type is marked as bootable. */
6011 if (aBootable == TRUE)
6012 {
6013 for (StorageControllerList::const_iterator
6014 it = mStorageControllers->begin();
6015 it != mStorageControllers->end();
6016 ++it)
6017 {
6018 ComObjPtr<StorageController> aCtrl = (*it);
6019
6020 if ( (aCtrl->i_getName() != aName)
6021 && aCtrl->i_getBootable() == TRUE
6022 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6023 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6024 {
6025 aCtrl->i_setBootable(FALSE);
6026 break;
6027 }
6028 }
6029 }
6030
6031 if (SUCCEEDED(rc))
6032 {
6033 ctrl->i_setBootable(aBootable);
6034 i_setModified(IsModified_Storage);
6035 }
6036 }
6037
6038 if (SUCCEEDED(rc))
6039 {
6040 /* inform the direct session if any */
6041 alock.release();
6042 i_onStorageControllerChange(i_getId(), aName);
6043 }
6044
6045 return rc;
6046}
6047
6048HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6049{
6050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6051
6052 HRESULT rc = i_checkStateDependency(MutableStateDep);
6053 if (FAILED(rc)) return rc;
6054
6055 ComObjPtr<StorageController> ctrl;
6056 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6057 if (FAILED(rc)) return rc;
6058
6059 MediumAttachmentList llDetachedAttachments;
6060 {
6061 /* find all attached devices to the appropriate storage controller and detach them all */
6062 // make a temporary list because detachDevice invalidates iterators into
6063 // mMediumAttachments
6064 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6065
6066 for (MediumAttachmentList::const_iterator
6067 it = llAttachments2.begin();
6068 it != llAttachments2.end();
6069 ++it)
6070 {
6071 MediumAttachment *pAttachTemp = *it;
6072
6073 AutoCaller localAutoCaller(pAttachTemp);
6074 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6075
6076 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6077
6078 if (pAttachTemp->i_getControllerName() == aName)
6079 {
6080 llDetachedAttachments.push_back(pAttachTemp);
6081 rc = i_detachDevice(pAttachTemp, alock, NULL);
6082 if (FAILED(rc)) return rc;
6083 }
6084 }
6085 }
6086
6087 /* send event about detached devices before removing parent controller */
6088 for (MediumAttachmentList::const_iterator
6089 it = llDetachedAttachments.begin();
6090 it != llDetachedAttachments.end();
6091 ++it)
6092 {
6093 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6094 }
6095
6096 /* We can remove it now. */
6097 i_setModified(IsModified_Storage);
6098 mStorageControllers.backup();
6099
6100 ctrl->i_unshare();
6101
6102 mStorageControllers->remove(ctrl);
6103
6104 /* inform the direct session if any */
6105 alock.release();
6106 i_onStorageControllerChange(i_getId(), aName);
6107
6108 return S_OK;
6109}
6110
6111HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6112 ComPtr<IUSBController> &aController)
6113{
6114 if ( (aType <= USBControllerType_Null)
6115 || (aType >= USBControllerType_Last))
6116 return setError(E_INVALIDARG,
6117 tr("Invalid USB controller type: %d"),
6118 aType);
6119
6120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6121
6122 HRESULT rc = i_checkStateDependency(MutableStateDep);
6123 if (FAILED(rc)) return rc;
6124
6125 /* try to find one with the same type first. */
6126 ComObjPtr<USBController> ctrl;
6127
6128 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6129 if (SUCCEEDED(rc))
6130 return setError(VBOX_E_OBJECT_IN_USE,
6131 tr("USB controller named '%s' already exists"),
6132 aName.c_str());
6133
6134 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6135 ULONG maxInstances;
6136 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6137 if (FAILED(rc))
6138 return rc;
6139
6140 ULONG cInstances = i_getUSBControllerCountByType(aType);
6141 if (cInstances >= maxInstances)
6142 return setError(E_INVALIDARG,
6143 tr("Too many USB controllers of this type"));
6144
6145 ctrl.createObject();
6146
6147 rc = ctrl->init(this, aName, aType);
6148 if (FAILED(rc)) return rc;
6149
6150 i_setModified(IsModified_USB);
6151 mUSBControllers.backup();
6152 mUSBControllers->push_back(ctrl);
6153
6154 ctrl.queryInterfaceTo(aController.asOutParam());
6155
6156 /* inform the direct session if any */
6157 alock.release();
6158 i_onUSBControllerChange();
6159
6160 return S_OK;
6161}
6162
6163HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6164{
6165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6166
6167 ComObjPtr<USBController> ctrl;
6168
6169 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6170 if (SUCCEEDED(rc))
6171 ctrl.queryInterfaceTo(aController.asOutParam());
6172
6173 return rc;
6174}
6175
6176HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6177 ULONG *aControllers)
6178{
6179 if ( (aType <= USBControllerType_Null)
6180 || (aType >= USBControllerType_Last))
6181 return setError(E_INVALIDARG,
6182 tr("Invalid USB controller type: %d"),
6183 aType);
6184
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 ComObjPtr<USBController> ctrl;
6188
6189 *aControllers = i_getUSBControllerCountByType(aType);
6190
6191 return S_OK;
6192}
6193
6194HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6195{
6196
6197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6198
6199 HRESULT rc = i_checkStateDependency(MutableStateDep);
6200 if (FAILED(rc)) return rc;
6201
6202 ComObjPtr<USBController> ctrl;
6203 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6204 if (FAILED(rc)) return rc;
6205
6206 i_setModified(IsModified_USB);
6207 mUSBControllers.backup();
6208
6209 ctrl->i_unshare();
6210
6211 mUSBControllers->remove(ctrl);
6212
6213 /* inform the direct session if any */
6214 alock.release();
6215 i_onUSBControllerChange();
6216
6217 return S_OK;
6218}
6219
6220HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6221 ULONG *aOriginX,
6222 ULONG *aOriginY,
6223 ULONG *aWidth,
6224 ULONG *aHeight,
6225 BOOL *aEnabled)
6226{
6227 uint32_t u32OriginX= 0;
6228 uint32_t u32OriginY= 0;
6229 uint32_t u32Width = 0;
6230 uint32_t u32Height = 0;
6231 uint16_t u16Flags = 0;
6232
6233 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6234 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6235 if (RT_FAILURE(vrc))
6236 {
6237#ifdef RT_OS_WINDOWS
6238 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6239 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6240 * So just assign fEnable to TRUE again.
6241 * The right fix would be to change GUI API wrappers to make sure that parameters
6242 * are changed only if API succeeds.
6243 */
6244 *aEnabled = TRUE;
6245#endif
6246 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6247 tr("Saved guest size is not available (%Rrc)"),
6248 vrc);
6249 }
6250
6251 *aOriginX = u32OriginX;
6252 *aOriginY = u32OriginY;
6253 *aWidth = u32Width;
6254 *aHeight = u32Height;
6255 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6256
6257 return S_OK;
6258}
6259
6260HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6261 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6262{
6263 if (aScreenId != 0)
6264 return E_NOTIMPL;
6265
6266 if ( aBitmapFormat != BitmapFormat_BGR0
6267 && aBitmapFormat != BitmapFormat_BGRA
6268 && aBitmapFormat != BitmapFormat_RGBA
6269 && aBitmapFormat != BitmapFormat_PNG)
6270 return setError(E_NOTIMPL,
6271 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6272
6273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6274
6275 uint8_t *pu8Data = NULL;
6276 uint32_t cbData = 0;
6277 uint32_t u32Width = 0;
6278 uint32_t u32Height = 0;
6279
6280 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6281
6282 if (RT_FAILURE(vrc))
6283 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6284 tr("Saved thumbnail data is not available (%Rrc)"),
6285 vrc);
6286
6287 HRESULT hr = S_OK;
6288
6289 *aWidth = u32Width;
6290 *aHeight = u32Height;
6291
6292 if (cbData > 0)
6293 {
6294 /* Convert pixels to the format expected by the API caller. */
6295 if (aBitmapFormat == BitmapFormat_BGR0)
6296 {
6297 /* [0] B, [1] G, [2] R, [3] 0. */
6298 aData.resize(cbData);
6299 memcpy(&aData.front(), pu8Data, cbData);
6300 }
6301 else if (aBitmapFormat == BitmapFormat_BGRA)
6302 {
6303 /* [0] B, [1] G, [2] R, [3] A. */
6304 aData.resize(cbData);
6305 for (uint32_t i = 0; i < cbData; i += 4)
6306 {
6307 aData[i] = pu8Data[i];
6308 aData[i + 1] = pu8Data[i + 1];
6309 aData[i + 2] = pu8Data[i + 2];
6310 aData[i + 3] = 0xff;
6311 }
6312 }
6313 else if (aBitmapFormat == BitmapFormat_RGBA)
6314 {
6315 /* [0] R, [1] G, [2] B, [3] A. */
6316 aData.resize(cbData);
6317 for (uint32_t i = 0; i < cbData; i += 4)
6318 {
6319 aData[i] = pu8Data[i + 2];
6320 aData[i + 1] = pu8Data[i + 1];
6321 aData[i + 2] = pu8Data[i];
6322 aData[i + 3] = 0xff;
6323 }
6324 }
6325 else if (aBitmapFormat == BitmapFormat_PNG)
6326 {
6327 uint8_t *pu8PNG = NULL;
6328 uint32_t cbPNG = 0;
6329 uint32_t cxPNG = 0;
6330 uint32_t cyPNG = 0;
6331
6332 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6333
6334 if (RT_SUCCESS(vrc))
6335 {
6336 aData.resize(cbPNG);
6337 if (cbPNG)
6338 memcpy(&aData.front(), pu8PNG, cbPNG);
6339 }
6340 else
6341 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6342 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6343 vrc);
6344
6345 RTMemFree(pu8PNG);
6346 }
6347 }
6348
6349 freeSavedDisplayScreenshot(pu8Data);
6350
6351 return hr;
6352}
6353
6354HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6355 ULONG *aWidth,
6356 ULONG *aHeight,
6357 std::vector<BitmapFormat_T> &aBitmapFormats)
6358{
6359 if (aScreenId != 0)
6360 return E_NOTIMPL;
6361
6362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6363
6364 uint8_t *pu8Data = NULL;
6365 uint32_t cbData = 0;
6366 uint32_t u32Width = 0;
6367 uint32_t u32Height = 0;
6368
6369 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6370
6371 if (RT_FAILURE(vrc))
6372 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6373 tr("Saved screenshot data is not available (%Rrc)"),
6374 vrc);
6375
6376 *aWidth = u32Width;
6377 *aHeight = u32Height;
6378 aBitmapFormats.resize(1);
6379 aBitmapFormats[0] = BitmapFormat_PNG;
6380
6381 freeSavedDisplayScreenshot(pu8Data);
6382
6383 return S_OK;
6384}
6385
6386HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6387 BitmapFormat_T aBitmapFormat,
6388 ULONG *aWidth,
6389 ULONG *aHeight,
6390 std::vector<BYTE> &aData)
6391{
6392 if (aScreenId != 0)
6393 return E_NOTIMPL;
6394
6395 if (aBitmapFormat != BitmapFormat_PNG)
6396 return E_NOTIMPL;
6397
6398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6399
6400 uint8_t *pu8Data = NULL;
6401 uint32_t cbData = 0;
6402 uint32_t u32Width = 0;
6403 uint32_t u32Height = 0;
6404
6405 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6406
6407 if (RT_FAILURE(vrc))
6408 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6409 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6410 vrc);
6411
6412 *aWidth = u32Width;
6413 *aHeight = u32Height;
6414
6415 aData.resize(cbData);
6416 if (cbData)
6417 memcpy(&aData.front(), pu8Data, cbData);
6418
6419 freeSavedDisplayScreenshot(pu8Data);
6420
6421 return S_OK;
6422}
6423
6424HRESULT Machine::hotPlugCPU(ULONG aCpu)
6425{
6426 HRESULT rc = S_OK;
6427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6428
6429 if (!mHWData->mCPUHotPlugEnabled)
6430 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6431
6432 if (aCpu >= mHWData->mCPUCount)
6433 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6434
6435 if (mHWData->mCPUAttached[aCpu])
6436 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6437
6438 rc = i_checkStateDependency(MutableOrRunningStateDep);
6439 if (FAILED(rc)) return rc;
6440
6441 alock.release();
6442 rc = i_onCPUChange(aCpu, false);
6443 alock.acquire();
6444 if (FAILED(rc)) return rc;
6445
6446 i_setModified(IsModified_MachineData);
6447 mHWData.backup();
6448 mHWData->mCPUAttached[aCpu] = true;
6449
6450 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6451 if (Global::IsOnline(mData->mMachineState))
6452 i_saveSettings(NULL, alock);
6453
6454 return S_OK;
6455}
6456
6457HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6458{
6459 HRESULT rc = S_OK;
6460
6461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6462
6463 if (!mHWData->mCPUHotPlugEnabled)
6464 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6465
6466 if (aCpu >= SchemaDefs::MaxCPUCount)
6467 return setError(E_INVALIDARG,
6468 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6469 SchemaDefs::MaxCPUCount);
6470
6471 if (!mHWData->mCPUAttached[aCpu])
6472 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6473
6474 /* CPU 0 can't be detached */
6475 if (aCpu == 0)
6476 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6477
6478 rc = i_checkStateDependency(MutableOrRunningStateDep);
6479 if (FAILED(rc)) return rc;
6480
6481 alock.release();
6482 rc = i_onCPUChange(aCpu, true);
6483 alock.acquire();
6484 if (FAILED(rc)) return rc;
6485
6486 i_setModified(IsModified_MachineData);
6487 mHWData.backup();
6488 mHWData->mCPUAttached[aCpu] = false;
6489
6490 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6491 if (Global::IsOnline(mData->mMachineState))
6492 i_saveSettings(NULL, alock);
6493
6494 return S_OK;
6495}
6496
6497HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6498{
6499 *aAttached = false;
6500
6501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6502
6503 /* If hotplug is enabled the CPU is always enabled. */
6504 if (!mHWData->mCPUHotPlugEnabled)
6505 {
6506 if (aCpu < mHWData->mCPUCount)
6507 *aAttached = true;
6508 }
6509 else
6510 {
6511 if (aCpu < SchemaDefs::MaxCPUCount)
6512 *aAttached = mHWData->mCPUAttached[aCpu];
6513 }
6514
6515 return S_OK;
6516}
6517
6518HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6519{
6520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6521
6522 Utf8Str log = i_getLogFilename(aIdx);
6523 if (!RTFileExists(log.c_str()))
6524 log.setNull();
6525 aFilename = log;
6526
6527 return S_OK;
6528}
6529
6530HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6531{
6532 if (aSize < 0)
6533 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6534
6535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6536
6537 HRESULT rc = S_OK;
6538 Utf8Str log = i_getLogFilename(aIdx);
6539
6540 /* do not unnecessarily hold the lock while doing something which does
6541 * not need the lock and potentially takes a long time. */
6542 alock.release();
6543
6544 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6545 * keeps the SOAP reply size under 1M for the webservice (we're using
6546 * base64 encoded strings for binary data for years now, avoiding the
6547 * expansion of each byte array element to approx. 25 bytes of XML. */
6548 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6549 aData.resize(cbData);
6550
6551 RTFILE LogFile;
6552 int vrc = RTFileOpen(&LogFile, log.c_str(),
6553 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6554 if (RT_SUCCESS(vrc))
6555 {
6556 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6557 if (RT_SUCCESS(vrc))
6558 aData.resize(cbData);
6559 else
6560 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6561 tr("Could not read log file '%s' (%Rrc)"),
6562 log.c_str(), vrc);
6563 RTFileClose(LogFile);
6564 }
6565 else
6566 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6567 tr("Could not open log file '%s' (%Rrc)"),
6568 log.c_str(), vrc);
6569
6570 if (FAILED(rc))
6571 aData.resize(0);
6572
6573 return rc;
6574}
6575
6576
6577/**
6578 * Currently this method doesn't attach device to the running VM,
6579 * just makes sure it's plugged on next VM start.
6580 */
6581HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6582{
6583 // lock scope
6584 {
6585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 HRESULT rc = i_checkStateDependency(MutableStateDep);
6588 if (FAILED(rc)) return rc;
6589
6590 ChipsetType_T aChipset = ChipsetType_PIIX3;
6591 COMGETTER(ChipsetType)(&aChipset);
6592
6593 if (aChipset != ChipsetType_ICH9)
6594 {
6595 return setError(E_INVALIDARG,
6596 tr("Host PCI attachment only supported with ICH9 chipset"));
6597 }
6598
6599 // check if device with this host PCI address already attached
6600 for (HWData::PCIDeviceAssignmentList::const_iterator
6601 it = mHWData->mPCIDeviceAssignments.begin();
6602 it != mHWData->mPCIDeviceAssignments.end();
6603 ++it)
6604 {
6605 LONG iHostAddress = -1;
6606 ComPtr<PCIDeviceAttachment> pAttach;
6607 pAttach = *it;
6608 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6609 if (iHostAddress == aHostAddress)
6610 return setError(E_INVALIDARG,
6611 tr("Device with host PCI address already attached to this VM"));
6612 }
6613
6614 ComObjPtr<PCIDeviceAttachment> pda;
6615 char name[32];
6616
6617 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6618 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6619 pda.createObject();
6620 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6621 i_setModified(IsModified_MachineData);
6622 mHWData.backup();
6623 mHWData->mPCIDeviceAssignments.push_back(pda);
6624 }
6625
6626 return S_OK;
6627}
6628
6629/**
6630 * Currently this method doesn't detach device from the running VM,
6631 * just makes sure it's not plugged on next VM start.
6632 */
6633HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6634{
6635 ComObjPtr<PCIDeviceAttachment> pAttach;
6636 bool fRemoved = false;
6637 HRESULT rc;
6638
6639 // lock scope
6640 {
6641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6642
6643 rc = i_checkStateDependency(MutableStateDep);
6644 if (FAILED(rc)) return rc;
6645
6646 for (HWData::PCIDeviceAssignmentList::const_iterator
6647 it = mHWData->mPCIDeviceAssignments.begin();
6648 it != mHWData->mPCIDeviceAssignments.end();
6649 ++it)
6650 {
6651 LONG iHostAddress = -1;
6652 pAttach = *it;
6653 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6654 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6655 {
6656 i_setModified(IsModified_MachineData);
6657 mHWData.backup();
6658 mHWData->mPCIDeviceAssignments.remove(pAttach);
6659 fRemoved = true;
6660 break;
6661 }
6662 }
6663 }
6664
6665
6666 /* Fire event outside of the lock */
6667 if (fRemoved)
6668 {
6669 Assert(!pAttach.isNull());
6670 ComPtr<IEventSource> es;
6671 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6672 Assert(SUCCEEDED(rc));
6673 Bstr mid;
6674 rc = this->COMGETTER(Id)(mid.asOutParam());
6675 Assert(SUCCEEDED(rc));
6676 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6677 }
6678
6679 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6680 tr("No host PCI device %08x attached"),
6681 aHostAddress
6682 );
6683}
6684
6685HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6686{
6687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6688
6689 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6690 size_t i = 0;
6691 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6692 it = mHWData->mPCIDeviceAssignments.begin();
6693 it != mHWData->mPCIDeviceAssignments.end();
6694 ++it, ++i)
6695 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6696
6697 return S_OK;
6698}
6699
6700HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6701{
6702 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6703
6704 return S_OK;
6705}
6706
6707HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6708{
6709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6710
6711 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6712
6713 return S_OK;
6714}
6715
6716HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6717{
6718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6719 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6720 if (SUCCEEDED(hrc))
6721 {
6722 hrc = mHWData.backupEx();
6723 if (SUCCEEDED(hrc))
6724 {
6725 i_setModified(IsModified_MachineData);
6726 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6727 }
6728 }
6729 return hrc;
6730}
6731
6732HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6733{
6734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6735 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6736 return S_OK;
6737}
6738
6739HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6740{
6741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6742 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6743 if (SUCCEEDED(hrc))
6744 {
6745 hrc = mHWData.backupEx();
6746 if (SUCCEEDED(hrc))
6747 {
6748 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6749 if (SUCCEEDED(hrc))
6750 i_setModified(IsModified_MachineData);
6751 }
6752 }
6753 return hrc;
6754}
6755
6756HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6757{
6758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6759
6760 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6761
6762 return S_OK;
6763}
6764
6765HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6766{
6767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6768 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6769 if (SUCCEEDED(hrc))
6770 {
6771 hrc = mHWData.backupEx();
6772 if (SUCCEEDED(hrc))
6773 {
6774 i_setModified(IsModified_MachineData);
6775 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6776 }
6777 }
6778 return hrc;
6779}
6780
6781HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6782{
6783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6784
6785 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6786
6787 return S_OK;
6788}
6789
6790HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6791{
6792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6793
6794 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6795 if ( SUCCEEDED(hrc)
6796 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6797 {
6798 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6799 int vrc;
6800
6801 if (aAutostartEnabled)
6802 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6803 else
6804 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6805
6806 if (RT_SUCCESS(vrc))
6807 {
6808 hrc = mHWData.backupEx();
6809 if (SUCCEEDED(hrc))
6810 {
6811 i_setModified(IsModified_MachineData);
6812 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6813 }
6814 }
6815 else if (vrc == VERR_NOT_SUPPORTED)
6816 hrc = setError(VBOX_E_NOT_SUPPORTED,
6817 tr("The VM autostart feature is not supported on this platform"));
6818 else if (vrc == VERR_PATH_NOT_FOUND)
6819 hrc = setError(E_FAIL,
6820 tr("The path to the autostart database is not set"));
6821 else
6822 hrc = setError(E_UNEXPECTED,
6823 aAutostartEnabled ?
6824 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6825 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6826 mUserData->s.strName.c_str(), vrc);
6827 }
6828 return hrc;
6829}
6830
6831HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6832{
6833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6836
6837 return S_OK;
6838}
6839
6840HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6841{
6842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6843 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6844 if (SUCCEEDED(hrc))
6845 {
6846 hrc = mHWData.backupEx();
6847 if (SUCCEEDED(hrc))
6848 {
6849 i_setModified(IsModified_MachineData);
6850 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6851 }
6852 }
6853 return hrc;
6854}
6855
6856HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6857{
6858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6861
6862 return S_OK;
6863}
6864
6865HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6866{
6867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6868 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6869 if ( SUCCEEDED(hrc)
6870 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6871 {
6872 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6873 int vrc;
6874
6875 if (aAutostopType != AutostopType_Disabled)
6876 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6877 else
6878 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6879
6880 if (RT_SUCCESS(vrc))
6881 {
6882 hrc = mHWData.backupEx();
6883 if (SUCCEEDED(hrc))
6884 {
6885 i_setModified(IsModified_MachineData);
6886 mHWData->mAutostart.enmAutostopType = aAutostopType;
6887 }
6888 }
6889 else if (vrc == VERR_NOT_SUPPORTED)
6890 hrc = setError(VBOX_E_NOT_SUPPORTED,
6891 tr("The VM autostop feature is not supported on this platform"));
6892 else if (vrc == VERR_PATH_NOT_FOUND)
6893 hrc = setError(E_FAIL,
6894 tr("The path to the autostart database is not set"));
6895 else
6896 hrc = setError(E_UNEXPECTED,
6897 aAutostopType != AutostopType_Disabled ?
6898 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6899 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6900 mUserData->s.strName.c_str(), vrc);
6901 }
6902 return hrc;
6903}
6904
6905HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6906{
6907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6908
6909 aDefaultFrontend = mHWData->mDefaultFrontend;
6910
6911 return S_OK;
6912}
6913
6914HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6915{
6916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6917 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6918 if (SUCCEEDED(hrc))
6919 {
6920 hrc = mHWData.backupEx();
6921 if (SUCCEEDED(hrc))
6922 {
6923 i_setModified(IsModified_MachineData);
6924 mHWData->mDefaultFrontend = aDefaultFrontend;
6925 }
6926 }
6927 return hrc;
6928}
6929
6930HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6931{
6932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6933 size_t cbIcon = mUserData->s.ovIcon.size();
6934 aIcon.resize(cbIcon);
6935 if (cbIcon)
6936 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6937 return S_OK;
6938}
6939
6940HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6941{
6942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6943 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6944 if (SUCCEEDED(hrc))
6945 {
6946 i_setModified(IsModified_MachineData);
6947 mUserData.backup();
6948 size_t cbIcon = aIcon.size();
6949 mUserData->s.ovIcon.resize(cbIcon);
6950 if (cbIcon)
6951 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6952 }
6953 return hrc;
6954}
6955
6956HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6957{
6958#ifdef VBOX_WITH_USB
6959 *aUSBProxyAvailable = true;
6960#else
6961 *aUSBProxyAvailable = false;
6962#endif
6963 return S_OK;
6964}
6965
6966HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6967{
6968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6969
6970 *aVMProcessPriority = mUserData->s.enmVMPriority;
6971
6972 return S_OK;
6973}
6974
6975HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6976{
6977 RT_NOREF(aVMProcessPriority);
6978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6979 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6980 if (SUCCEEDED(hrc))
6981 {
6982 hrc = mUserData.backupEx();
6983 if (SUCCEEDED(hrc))
6984 {
6985 i_setModified(IsModified_MachineData);
6986 mUserData->s.enmVMPriority = aVMProcessPriority;
6987 }
6988 }
6989 alock.release();
6990 if (SUCCEEDED(hrc))
6991 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6992 return hrc;
6993}
6994
6995HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6996 ComPtr<IProgress> &aProgress)
6997{
6998 ComObjPtr<Progress> pP;
6999 Progress *ppP = pP;
7000 IProgress *iP = static_cast<IProgress *>(ppP);
7001 IProgress **pProgress = &iP;
7002
7003 IMachine *pTarget = aTarget;
7004
7005 /* Convert the options. */
7006 RTCList<CloneOptions_T> optList;
7007 if (aOptions.size())
7008 for (size_t i = 0; i < aOptions.size(); ++i)
7009 optList.append(aOptions[i]);
7010
7011 if (optList.contains(CloneOptions_Link))
7012 {
7013 if (!i_isSnapshotMachine())
7014 return setError(E_INVALIDARG,
7015 tr("Linked clone can only be created from a snapshot"));
7016 if (aMode != CloneMode_MachineState)
7017 return setError(E_INVALIDARG,
7018 tr("Linked clone can only be created for a single machine state"));
7019 }
7020 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7021
7022 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7023
7024 HRESULT rc = pWorker->start(pProgress);
7025
7026 pP = static_cast<Progress *>(*pProgress);
7027 pP.queryInterfaceTo(aProgress.asOutParam());
7028
7029 return rc;
7030
7031}
7032
7033HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7034 const com::Utf8Str &aType,
7035 ComPtr<IProgress> &aProgress)
7036{
7037 LogFlowThisFuncEnter();
7038
7039 ComObjPtr<Progress> ptrProgress;
7040 HRESULT hrc = ptrProgress.createObject();
7041 if (SUCCEEDED(hrc))
7042 {
7043 com::Utf8Str strDefaultPath;
7044 if (aTargetPath.isEmpty())
7045 i_calculateFullPath(".", strDefaultPath);
7046
7047 /* Initialize our worker task */
7048 MachineMoveVM *pTask = NULL;
7049 try
7050 {
7051 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7052 }
7053 catch (std::bad_alloc &)
7054 {
7055 return E_OUTOFMEMORY;
7056 }
7057
7058 hrc = pTask->init();//no exceptions are thrown
7059
7060 if (SUCCEEDED(hrc))
7061 {
7062 hrc = pTask->createThread();
7063 pTask = NULL; /* Consumed by createThread(). */
7064 if (SUCCEEDED(hrc))
7065 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7066 else
7067 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7068 }
7069 else
7070 delete pTask;
7071 }
7072
7073 LogFlowThisFuncLeave();
7074 return hrc;
7075
7076}
7077
7078HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7079{
7080 NOREF(aProgress);
7081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7082
7083 // This check should always fail.
7084 HRESULT rc = i_checkStateDependency(MutableStateDep);
7085 if (FAILED(rc)) return rc;
7086
7087 AssertFailedReturn(E_NOTIMPL);
7088}
7089
7090HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7091{
7092 NOREF(aSavedStateFile);
7093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7094
7095 // This check should always fail.
7096 HRESULT rc = i_checkStateDependency(MutableStateDep);
7097 if (FAILED(rc)) return rc;
7098
7099 AssertFailedReturn(E_NOTIMPL);
7100}
7101
7102HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7103{
7104 NOREF(aFRemoveFile);
7105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7106
7107 // This check should always fail.
7108 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7109 if (FAILED(rc)) return rc;
7110
7111 AssertFailedReturn(E_NOTIMPL);
7112}
7113
7114// public methods for internal purposes
7115/////////////////////////////////////////////////////////////////////////////
7116
7117/**
7118 * Adds the given IsModified_* flag to the dirty flags of the machine.
7119 * This must be called either during i_loadSettings or under the machine write lock.
7120 * @param fl Flag
7121 * @param fAllowStateModification If state modifications are allowed.
7122 */
7123void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7124{
7125 mData->flModifications |= fl;
7126 if (fAllowStateModification && i_isStateModificationAllowed())
7127 mData->mCurrentStateModified = true;
7128}
7129
7130/**
7131 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7132 * care of the write locking.
7133 *
7134 * @param fModification The flag to add.
7135 * @param fAllowStateModification If state modifications are allowed.
7136 */
7137void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7138{
7139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7140 i_setModified(fModification, fAllowStateModification);
7141}
7142
7143/**
7144 * Saves the registry entry of this machine to the given configuration node.
7145 *
7146 * @param data Machine registry data.
7147 *
7148 * @note locks this object for reading.
7149 */
7150HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7151{
7152 AutoLimitedCaller autoCaller(this);
7153 AssertComRCReturnRC(autoCaller.rc());
7154
7155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7156
7157 data.uuid = mData->mUuid;
7158 data.strSettingsFile = mData->m_strConfigFile;
7159
7160 return S_OK;
7161}
7162
7163/**
7164 * Calculates the absolute path of the given path taking the directory of the
7165 * machine settings file as the current directory.
7166 *
7167 * @param strPath Path to calculate the absolute path for.
7168 * @param aResult Where to put the result (used only on success, can be the
7169 * same Utf8Str instance as passed in @a aPath).
7170 * @return IPRT result.
7171 *
7172 * @note Locks this object for reading.
7173 */
7174int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7175{
7176 AutoCaller autoCaller(this);
7177 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7178
7179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7180
7181 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7182
7183 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7184
7185 strSettingsDir.stripFilename();
7186 char szFolder[RTPATH_MAX];
7187 size_t cbFolder = sizeof(szFolder);
7188 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7189 if (RT_SUCCESS(vrc))
7190 aResult = szFolder;
7191
7192 return vrc;
7193}
7194
7195/**
7196 * Copies strSource to strTarget, making it relative to the machine folder
7197 * if it is a subdirectory thereof, or simply copying it otherwise.
7198 *
7199 * @param strSource Path to evaluate and copy.
7200 * @param strTarget Buffer to receive target path.
7201 *
7202 * @note Locks this object for reading.
7203 */
7204void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7205 Utf8Str &strTarget)
7206{
7207 AutoCaller autoCaller(this);
7208 AssertComRCReturn(autoCaller.rc(), (void)0);
7209
7210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7211
7212 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7213 // use strTarget as a temporary buffer to hold the machine settings dir
7214 strTarget = mData->m_strConfigFileFull;
7215 strTarget.stripFilename();
7216 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7217 {
7218 // is relative: then append what's left
7219 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7220 // for empty paths (only possible for subdirs) use "." to avoid
7221 // triggering default settings for not present config attributes.
7222 if (strTarget.isEmpty())
7223 strTarget = ".";
7224 }
7225 else
7226 // is not relative: then overwrite
7227 strTarget = strSource;
7228}
7229
7230/**
7231 * Returns the full path to the machine's log folder in the
7232 * \a aLogFolder argument.
7233 */
7234void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7235{
7236 AutoCaller autoCaller(this);
7237 AssertComRCReturnVoid(autoCaller.rc());
7238
7239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7240
7241 char szTmp[RTPATH_MAX];
7242 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7243 if (RT_SUCCESS(vrc))
7244 {
7245 if (szTmp[0] && !mUserData.isNull())
7246 {
7247 char szTmp2[RTPATH_MAX];
7248 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7249 if (RT_SUCCESS(vrc))
7250 aLogFolder.printf("%s%c%s",
7251 szTmp2,
7252 RTPATH_DELIMITER,
7253 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7254 }
7255 else
7256 vrc = VERR_PATH_IS_RELATIVE;
7257 }
7258
7259 if (RT_FAILURE(vrc))
7260 {
7261 // fallback if VBOX_USER_LOGHOME is not set or invalid
7262 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7263 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7264 aLogFolder.append(RTPATH_DELIMITER);
7265 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7266 }
7267}
7268
7269/**
7270 * Returns the full path to the machine's log file for an given index.
7271 */
7272Utf8Str Machine::i_getLogFilename(ULONG idx)
7273{
7274 Utf8Str logFolder;
7275 getLogFolder(logFolder);
7276 Assert(logFolder.length());
7277
7278 Utf8Str log;
7279 if (idx == 0)
7280 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7281#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7282 else if (idx == 1)
7283 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7284 else
7285 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7286#else
7287 else
7288 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7289#endif
7290 return log;
7291}
7292
7293/**
7294 * Returns the full path to the machine's hardened log file.
7295 */
7296Utf8Str Machine::i_getHardeningLogFilename(void)
7297{
7298 Utf8Str strFilename;
7299 getLogFolder(strFilename);
7300 Assert(strFilename.length());
7301 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7302 return strFilename;
7303}
7304
7305/**
7306 * Returns the default NVRAM filename based on the location of the VM config.
7307 * Note that this is a relative path.
7308 */
7309Utf8Str Machine::i_getDefaultNVRAMFilename()
7310{
7311 AutoCaller autoCaller(this);
7312 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7313
7314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7315
7316 if (i_isSnapshotMachine())
7317 return Utf8Str::Empty;
7318
7319 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7320 strNVRAMFilePath.stripPath();
7321 strNVRAMFilePath.stripSuffix();
7322 strNVRAMFilePath += ".nvram";
7323
7324 return strNVRAMFilePath;
7325}
7326
7327/**
7328 * Returns the NVRAM filename for a new snapshot. This intentionally works
7329 * similarly to the saved state file naming. Note that this is usually
7330 * a relative path, unless the snapshot folder is absolute.
7331 */
7332Utf8Str Machine::i_getSnapshotNVRAMFilename()
7333{
7334 AutoCaller autoCaller(this);
7335 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7336
7337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7338
7339 RTTIMESPEC ts;
7340 RTTimeNow(&ts);
7341 RTTIME time;
7342 RTTimeExplode(&time, &ts);
7343
7344 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7345 strNVRAMFilePath += RTPATH_DELIMITER;
7346 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7347 time.i32Year, time.u8Month, time.u8MonthDay,
7348 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7349
7350 return strNVRAMFilePath;
7351}
7352
7353/**
7354 * Returns the version of the settings file.
7355 */
7356SettingsVersion_T Machine::i_getSettingsVersion(void)
7357{
7358 AutoCaller autoCaller(this);
7359 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7360
7361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7362
7363 return mData->pMachineConfigFile->getSettingsVersion();
7364}
7365
7366/**
7367 * Composes a unique saved state filename based on the current system time. The filename is
7368 * granular to the second so this will work so long as no more than one snapshot is taken on
7369 * a machine per second.
7370 *
7371 * Before version 4.1, we used this formula for saved state files:
7372 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7373 * which no longer works because saved state files can now be shared between the saved state of the
7374 * "saved" machine and an online snapshot, and the following would cause problems:
7375 * 1) save machine
7376 * 2) create online snapshot from that machine state --> reusing saved state file
7377 * 3) save machine again --> filename would be reused, breaking the online snapshot
7378 *
7379 * So instead we now use a timestamp.
7380 *
7381 * @param strStateFilePath
7382 */
7383
7384void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7385{
7386 AutoCaller autoCaller(this);
7387 AssertComRCReturnVoid(autoCaller.rc());
7388
7389 {
7390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7391 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7392 }
7393
7394 RTTIMESPEC ts;
7395 RTTimeNow(&ts);
7396 RTTIME time;
7397 RTTimeExplode(&time, &ts);
7398
7399 strStateFilePath += RTPATH_DELIMITER;
7400 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7401 time.i32Year, time.u8Month, time.u8MonthDay,
7402 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7403}
7404
7405/**
7406 * Returns whether at least one USB controller is present for the VM.
7407 */
7408bool Machine::i_isUSBControllerPresent()
7409{
7410 AutoCaller autoCaller(this);
7411 AssertComRCReturn(autoCaller.rc(), false);
7412
7413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7414
7415 return (mUSBControllers->size() > 0);
7416}
7417
7418
7419/**
7420 * @note Locks this object for writing, calls the client process
7421 * (inside the lock).
7422 */
7423HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7424 const Utf8Str &strFrontend,
7425 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7426 ProgressProxy *aProgress)
7427{
7428 LogFlowThisFuncEnter();
7429
7430 AssertReturn(aControl, E_FAIL);
7431 AssertReturn(aProgress, E_FAIL);
7432 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7433
7434 AutoCaller autoCaller(this);
7435 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7436
7437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7438
7439 if (!mData->mRegistered)
7440 return setError(E_UNEXPECTED,
7441 tr("The machine '%s' is not registered"),
7442 mUserData->s.strName.c_str());
7443
7444 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7445
7446 /* The process started when launching a VM with separate UI/VM processes is always
7447 * the UI process, i.e. needs special handling as it won't claim the session. */
7448 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7449
7450 if (fSeparate)
7451 {
7452 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7453 return setError(VBOX_E_INVALID_OBJECT_STATE,
7454 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7455 mUserData->s.strName.c_str());
7456 }
7457 else
7458 {
7459 if ( mData->mSession.mState == SessionState_Locked
7460 || mData->mSession.mState == SessionState_Spawning
7461 || mData->mSession.mState == SessionState_Unlocking)
7462 return setError(VBOX_E_INVALID_OBJECT_STATE,
7463 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7464 mUserData->s.strName.c_str());
7465
7466 /* may not be busy */
7467 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7468 }
7469
7470 /* Hardening logging */
7471#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7472 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7473 {
7474 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7475 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7476 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7477 {
7478 Utf8Str strStartupLogDir = strHardeningLogFile;
7479 strStartupLogDir.stripFilename();
7480 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7481 file without stripping the file. */
7482 }
7483 strSupHardeningLogArg.append(strHardeningLogFile);
7484
7485 /* Remove legacy log filename to avoid confusion. */
7486 Utf8Str strOldStartupLogFile;
7487 getLogFolder(strOldStartupLogFile);
7488 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7489 RTFileDelete(strOldStartupLogFile.c_str());
7490 }
7491#else
7492 Utf8Str strSupHardeningLogArg;
7493#endif
7494
7495 Utf8Str strAppOverride;
7496#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7497 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7498#endif
7499
7500 bool fUseVBoxSDS = false;
7501 Utf8Str strCanonicalName;
7502 if (false)
7503 { }
7504#ifdef VBOX_WITH_QTGUI
7505 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7506 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7507 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7508 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7509 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7510 {
7511 strCanonicalName = "GUI/Qt";
7512 fUseVBoxSDS = true;
7513 }
7514#endif
7515#ifdef VBOX_WITH_VBOXSDL
7516 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7517 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7518 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7519 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7520 {
7521 strCanonicalName = "GUI/SDL";
7522 fUseVBoxSDS = true;
7523 }
7524#endif
7525#ifdef VBOX_WITH_HEADLESS
7526 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7527 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7528 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7529 {
7530 strCanonicalName = "headless";
7531 }
7532#endif
7533 else
7534 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7535
7536 Utf8Str idStr = mData->mUuid.toString();
7537 Utf8Str const &strMachineName = mUserData->s.strName;
7538 RTPROCESS pid = NIL_RTPROCESS;
7539
7540#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7541 RT_NOREF(fUseVBoxSDS);
7542#else
7543 DWORD idCallerSession = ~(DWORD)0;
7544 if (fUseVBoxSDS)
7545 {
7546 /*
7547 * The VBoxSDS should be used for process launching the VM with
7548 * GUI only if the caller and the VBoxSDS are in different Windows
7549 * sessions and the caller in the interactive one.
7550 */
7551 fUseVBoxSDS = false;
7552
7553 /* Get windows session of the current process. The process token used
7554 due to several reasons:
7555 1. The token is absent for the current thread except someone set it
7556 for us.
7557 2. Needs to get the id of the session where the process is started.
7558 We only need to do this once, though. */
7559 static DWORD s_idCurrentSession = ~(DWORD)0;
7560 DWORD idCurrentSession = s_idCurrentSession;
7561 if (idCurrentSession == ~(DWORD)0)
7562 {
7563 HANDLE hCurrentProcessToken = NULL;
7564 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7565 {
7566 DWORD cbIgn = 0;
7567 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7568 s_idCurrentSession = idCurrentSession;
7569 else
7570 {
7571 idCurrentSession = ~(DWORD)0;
7572 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7573 }
7574 CloseHandle(hCurrentProcessToken);
7575 }
7576 else
7577 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7578 }
7579
7580 /* get the caller's session */
7581 HRESULT hrc = CoImpersonateClient();
7582 if (SUCCEEDED(hrc))
7583 {
7584 HANDLE hCallerThreadToken;
7585 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7586 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7587 &hCallerThreadToken))
7588 {
7589 SetLastError(NO_ERROR);
7590 DWORD cbIgn = 0;
7591 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7592 {
7593 /* Only need to use SDS if the session ID differs: */
7594 if (idCurrentSession != idCallerSession)
7595 {
7596 fUseVBoxSDS = false;
7597
7598 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7599 DWORD cbTokenGroups = 0;
7600 PTOKEN_GROUPS pTokenGroups = NULL;
7601 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7602 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7603 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7604 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7605 {
7606 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7607 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7608 PSID pInteractiveSid = NULL;
7609 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7610 {
7611 /* Iterate over the groups looking for the interactive SID: */
7612 fUseVBoxSDS = false;
7613 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7614 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7615 {
7616 fUseVBoxSDS = true;
7617 break;
7618 }
7619 FreeSid(pInteractiveSid);
7620 }
7621 }
7622 else
7623 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7624 RTMemTmpFree(pTokenGroups);
7625 }
7626 }
7627 else
7628 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7629 CloseHandle(hCallerThreadToken);
7630 }
7631 else
7632 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7633 CoRevertToSelf();
7634 }
7635 else
7636 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7637 }
7638 if (fUseVBoxSDS)
7639 {
7640 /* connect to VBoxSDS */
7641 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7642 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7643 if (FAILED(rc))
7644 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7645 strMachineName.c_str());
7646
7647 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7648 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7649 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7650 service to access the files. */
7651 rc = CoSetProxyBlanket(pVBoxSDS,
7652 RPC_C_AUTHN_DEFAULT,
7653 RPC_C_AUTHZ_DEFAULT,
7654 COLE_DEFAULT_PRINCIPAL,
7655 RPC_C_AUTHN_LEVEL_DEFAULT,
7656 RPC_C_IMP_LEVEL_IMPERSONATE,
7657 NULL,
7658 EOAC_DEFAULT);
7659 if (FAILED(rc))
7660 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7661
7662 size_t const cEnvVars = aEnvironmentChanges.size();
7663 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7664 for (size_t i = 0; i < cEnvVars; i++)
7665 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7666
7667 ULONG uPid = 0;
7668 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7669 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7670 idCallerSession, &uPid);
7671 if (FAILED(rc))
7672 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7673 pid = (RTPROCESS)uPid;
7674 }
7675 else
7676#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7677 {
7678 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7679 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7680 if (RT_FAILURE(vrc))
7681 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7682 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7683 }
7684
7685 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7686 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7687
7688 if (!fSeparate)
7689 {
7690 /*
7691 * Note that we don't release the lock here before calling the client,
7692 * because it doesn't need to call us back if called with a NULL argument.
7693 * Releasing the lock here is dangerous because we didn't prepare the
7694 * launch data yet, but the client we've just started may happen to be
7695 * too fast and call LockMachine() that will fail (because of PID, etc.),
7696 * so that the Machine will never get out of the Spawning session state.
7697 */
7698
7699 /* inform the session that it will be a remote one */
7700 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7701#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7702 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7703#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7704 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7705#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7706 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7707
7708 if (FAILED(rc))
7709 {
7710 /* restore the session state */
7711 mData->mSession.mState = SessionState_Unlocked;
7712 alock.release();
7713 mParent->i_addProcessToReap(pid);
7714 /* The failure may occur w/o any error info (from RPC), so provide one */
7715 return setError(VBOX_E_VM_ERROR,
7716 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7717 }
7718
7719 /* attach launch data to the machine */
7720 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7721 mData->mSession.mRemoteControls.push_back(aControl);
7722 mData->mSession.mProgress = aProgress;
7723 mData->mSession.mPID = pid;
7724 mData->mSession.mState = SessionState_Spawning;
7725 Assert(strCanonicalName.isNotEmpty());
7726 mData->mSession.mName = strCanonicalName;
7727 }
7728 else
7729 {
7730 /* For separate UI process we declare the launch as completed instantly, as the
7731 * actual headless VM start may or may not come. No point in remembering anything
7732 * yet, as what matters for us is when the headless VM gets started. */
7733 aProgress->i_notifyComplete(S_OK);
7734 }
7735
7736 alock.release();
7737 mParent->i_addProcessToReap(pid);
7738
7739 LogFlowThisFuncLeave();
7740 return S_OK;
7741}
7742
7743/**
7744 * Returns @c true if the given session machine instance has an open direct
7745 * session (and optionally also for direct sessions which are closing) and
7746 * returns the session control machine instance if so.
7747 *
7748 * Note that when the method returns @c false, the arguments remain unchanged.
7749 *
7750 * @param aMachine Session machine object.
7751 * @param aControl Direct session control object (optional).
7752 * @param aRequireVM If true then only allow VM sessions.
7753 * @param aAllowClosing If true then additionally a session which is currently
7754 * being closed will also be allowed.
7755 *
7756 * @note locks this object for reading.
7757 */
7758bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7759 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7760 bool aRequireVM /*= false*/,
7761 bool aAllowClosing /*= false*/)
7762{
7763 AutoLimitedCaller autoCaller(this);
7764 AssertComRCReturn(autoCaller.rc(), false);
7765
7766 /* just return false for inaccessible machines */
7767 if (getObjectState().getState() != ObjectState::Ready)
7768 return false;
7769
7770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7771
7772 if ( ( mData->mSession.mState == SessionState_Locked
7773 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7774 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7775 )
7776 {
7777 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7778
7779 aMachine = mData->mSession.mMachine;
7780
7781 if (aControl != NULL)
7782 *aControl = mData->mSession.mDirectControl;
7783
7784 return true;
7785 }
7786
7787 return false;
7788}
7789
7790/**
7791 * Returns @c true if the given machine has an spawning direct session.
7792 *
7793 * @note locks this object for reading.
7794 */
7795bool Machine::i_isSessionSpawning()
7796{
7797 AutoLimitedCaller autoCaller(this);
7798 AssertComRCReturn(autoCaller.rc(), false);
7799
7800 /* just return false for inaccessible machines */
7801 if (getObjectState().getState() != ObjectState::Ready)
7802 return false;
7803
7804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7805
7806 if (mData->mSession.mState == SessionState_Spawning)
7807 return true;
7808
7809 return false;
7810}
7811
7812/**
7813 * Called from the client watcher thread to check for unexpected client process
7814 * death during Session_Spawning state (e.g. before it successfully opened a
7815 * direct session).
7816 *
7817 * On Win32 and on OS/2, this method is called only when we've got the
7818 * direct client's process termination notification, so it always returns @c
7819 * true.
7820 *
7821 * On other platforms, this method returns @c true if the client process is
7822 * terminated and @c false if it's still alive.
7823 *
7824 * @note Locks this object for writing.
7825 */
7826bool Machine::i_checkForSpawnFailure()
7827{
7828 AutoCaller autoCaller(this);
7829 if (!autoCaller.isOk())
7830 {
7831 /* nothing to do */
7832 LogFlowThisFunc(("Already uninitialized!\n"));
7833 return true;
7834 }
7835
7836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7837
7838 if (mData->mSession.mState != SessionState_Spawning)
7839 {
7840 /* nothing to do */
7841 LogFlowThisFunc(("Not spawning any more!\n"));
7842 return true;
7843 }
7844
7845 HRESULT rc = S_OK;
7846
7847 /* PID not yet initialized, skip check. */
7848 if (mData->mSession.mPID == NIL_RTPROCESS)
7849 return false;
7850
7851 RTPROCSTATUS status;
7852 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7853
7854 if (vrc != VERR_PROCESS_RUNNING)
7855 {
7856 Utf8Str strExtraInfo;
7857
7858#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7859 /* If the startup logfile exists and is of non-zero length, tell the
7860 user to look there for more details to encourage them to attach it
7861 when reporting startup issues. */
7862 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7863 uint64_t cbStartupLogFile = 0;
7864 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7865 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7866 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7867#endif
7868
7869 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7870 rc = setError(E_FAIL,
7871 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7872 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7873 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7874 rc = setError(E_FAIL,
7875 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7876 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7877 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7878 rc = setError(E_FAIL,
7879 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7880 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7881 else
7882 rc = setErrorBoth(E_FAIL, vrc,
7883 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7884 i_getName().c_str(), vrc, strExtraInfo.c_str());
7885 }
7886
7887 if (FAILED(rc))
7888 {
7889 /* Close the remote session, remove the remote control from the list
7890 * and reset session state to Closed (@note keep the code in sync with
7891 * the relevant part in LockMachine()). */
7892
7893 Assert(mData->mSession.mRemoteControls.size() == 1);
7894 if (mData->mSession.mRemoteControls.size() == 1)
7895 {
7896 ErrorInfoKeeper eik;
7897 mData->mSession.mRemoteControls.front()->Uninitialize();
7898 }
7899
7900 mData->mSession.mRemoteControls.clear();
7901 mData->mSession.mState = SessionState_Unlocked;
7902
7903 /* finalize the progress after setting the state */
7904 if (!mData->mSession.mProgress.isNull())
7905 {
7906 mData->mSession.mProgress->notifyComplete(rc);
7907 mData->mSession.mProgress.setNull();
7908 }
7909
7910 mData->mSession.mPID = NIL_RTPROCESS;
7911
7912 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7913 return true;
7914 }
7915
7916 return false;
7917}
7918
7919/**
7920 * Checks whether the machine can be registered. If so, commits and saves
7921 * all settings.
7922 *
7923 * @note Must be called from mParent's write lock. Locks this object and
7924 * children for writing.
7925 */
7926HRESULT Machine::i_prepareRegister()
7927{
7928 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7929
7930 AutoLimitedCaller autoCaller(this);
7931 AssertComRCReturnRC(autoCaller.rc());
7932
7933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7934
7935 /* wait for state dependents to drop to zero */
7936 i_ensureNoStateDependencies(alock);
7937
7938 if (!mData->mAccessible)
7939 return setError(VBOX_E_INVALID_OBJECT_STATE,
7940 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7941 mUserData->s.strName.c_str(),
7942 mData->mUuid.toString().c_str());
7943
7944 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7945
7946 if (mData->mRegistered)
7947 return setError(VBOX_E_INVALID_OBJECT_STATE,
7948 tr("The machine '%s' with UUID {%s} is already registered"),
7949 mUserData->s.strName.c_str(),
7950 mData->mUuid.toString().c_str());
7951
7952 HRESULT rc = S_OK;
7953
7954 // Ensure the settings are saved. If we are going to be registered and
7955 // no config file exists yet, create it by calling i_saveSettings() too.
7956 if ( (mData->flModifications)
7957 || (!mData->pMachineConfigFile->fileExists())
7958 )
7959 {
7960 rc = i_saveSettings(NULL, alock);
7961 // no need to check whether VirtualBox.xml needs saving too since
7962 // we can't have a machine XML file rename pending
7963 if (FAILED(rc)) return rc;
7964 }
7965
7966 /* more config checking goes here */
7967
7968 if (SUCCEEDED(rc))
7969 {
7970 /* we may have had implicit modifications we want to fix on success */
7971 i_commit();
7972
7973 mData->mRegistered = true;
7974 }
7975 else
7976 {
7977 /* we may have had implicit modifications we want to cancel on failure*/
7978 i_rollback(false /* aNotify */);
7979 }
7980
7981 return rc;
7982}
7983
7984/**
7985 * Increases the number of objects dependent on the machine state or on the
7986 * registered state. Guarantees that these two states will not change at least
7987 * until #i_releaseStateDependency() is called.
7988 *
7989 * Depending on the @a aDepType value, additional state checks may be made.
7990 * These checks will set extended error info on failure. See
7991 * #i_checkStateDependency() for more info.
7992 *
7993 * If this method returns a failure, the dependency is not added and the caller
7994 * is not allowed to rely on any particular machine state or registration state
7995 * value and may return the failed result code to the upper level.
7996 *
7997 * @param aDepType Dependency type to add.
7998 * @param aState Current machine state (NULL if not interested).
7999 * @param aRegistered Current registered state (NULL if not interested).
8000 *
8001 * @note Locks this object for writing.
8002 */
8003HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8004 MachineState_T *aState /* = NULL */,
8005 BOOL *aRegistered /* = NULL */)
8006{
8007 AutoCaller autoCaller(this);
8008 AssertComRCReturnRC(autoCaller.rc());
8009
8010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8011
8012 HRESULT rc = i_checkStateDependency(aDepType);
8013 if (FAILED(rc)) return rc;
8014
8015 {
8016 if (mData->mMachineStateChangePending != 0)
8017 {
8018 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8019 * drop to zero so don't add more. It may make sense to wait a bit
8020 * and retry before reporting an error (since the pending state
8021 * transition should be really quick) but let's just assert for
8022 * now to see if it ever happens on practice. */
8023
8024 AssertFailed();
8025
8026 return setError(E_ACCESSDENIED,
8027 tr("Machine state change is in progress. Please retry the operation later."));
8028 }
8029
8030 ++mData->mMachineStateDeps;
8031 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8032 }
8033
8034 if (aState)
8035 *aState = mData->mMachineState;
8036 if (aRegistered)
8037 *aRegistered = mData->mRegistered;
8038
8039 return S_OK;
8040}
8041
8042/**
8043 * Decreases the number of objects dependent on the machine state.
8044 * Must always complete the #i_addStateDependency() call after the state
8045 * dependency is no more necessary.
8046 */
8047void Machine::i_releaseStateDependency()
8048{
8049 AutoCaller autoCaller(this);
8050 AssertComRCReturnVoid(autoCaller.rc());
8051
8052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8053
8054 /* releaseStateDependency() w/o addStateDependency()? */
8055 AssertReturnVoid(mData->mMachineStateDeps != 0);
8056 -- mData->mMachineStateDeps;
8057
8058 if (mData->mMachineStateDeps == 0)
8059 {
8060 /* inform i_ensureNoStateDependencies() that there are no more deps */
8061 if (mData->mMachineStateChangePending != 0)
8062 {
8063 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8064 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8065 }
8066 }
8067}
8068
8069Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8070{
8071 /* start with nothing found */
8072 Utf8Str strResult("");
8073
8074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8075
8076 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8077 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8078 // found:
8079 strResult = it->second; // source is a Utf8Str
8080
8081 return strResult;
8082}
8083
8084// protected methods
8085/////////////////////////////////////////////////////////////////////////////
8086
8087/**
8088 * Performs machine state checks based on the @a aDepType value. If a check
8089 * fails, this method will set extended error info, otherwise it will return
8090 * S_OK. It is supposed, that on failure, the caller will immediately return
8091 * the return value of this method to the upper level.
8092 *
8093 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8094 *
8095 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8096 * current state of this machine object allows to change settings of the
8097 * machine (i.e. the machine is not registered, or registered but not running
8098 * and not saved). It is useful to call this method from Machine setters
8099 * before performing any change.
8100 *
8101 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8102 * as for MutableStateDep except that if the machine is saved, S_OK is also
8103 * returned. This is useful in setters which allow changing machine
8104 * properties when it is in the saved state.
8105 *
8106 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8107 * if the current state of this machine object allows to change runtime
8108 * changeable settings of the machine (i.e. the machine is not registered, or
8109 * registered but either running or not running and not saved). It is useful
8110 * to call this method from Machine setters before performing any changes to
8111 * runtime changeable settings.
8112 *
8113 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8114 * the same as for MutableOrRunningStateDep except that if the machine is
8115 * saved, S_OK is also returned. This is useful in setters which allow
8116 * changing runtime and saved state changeable machine properties.
8117 *
8118 * @param aDepType Dependency type to check.
8119 *
8120 * @note Non Machine based classes should use #i_addStateDependency() and
8121 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8122 * template.
8123 *
8124 * @note This method must be called from under this object's read or write
8125 * lock.
8126 */
8127HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8128{
8129 switch (aDepType)
8130 {
8131 case AnyStateDep:
8132 {
8133 break;
8134 }
8135 case MutableStateDep:
8136 {
8137 if ( mData->mRegistered
8138 && ( !i_isSessionMachine()
8139 || ( mData->mMachineState != MachineState_Aborted
8140 && mData->mMachineState != MachineState_Teleported
8141 && mData->mMachineState != MachineState_PoweredOff
8142 )
8143 )
8144 )
8145 return setError(VBOX_E_INVALID_VM_STATE,
8146 tr("The machine is not mutable (state is %s)"),
8147 Global::stringifyMachineState(mData->mMachineState));
8148 break;
8149 }
8150 case MutableOrSavedStateDep:
8151 {
8152 if ( mData->mRegistered
8153 && ( !i_isSessionMachine()
8154 || ( mData->mMachineState != MachineState_Aborted
8155 && mData->mMachineState != MachineState_Teleported
8156 && mData->mMachineState != MachineState_Saved
8157 && mData->mMachineState != MachineState_AbortedSaved
8158 && mData->mMachineState != MachineState_PoweredOff
8159 )
8160 )
8161 )
8162 return setError(VBOX_E_INVALID_VM_STATE,
8163 tr("The machine is not mutable or saved (state is %s)"),
8164 Global::stringifyMachineState(mData->mMachineState));
8165 break;
8166 }
8167 case MutableOrRunningStateDep:
8168 {
8169 if ( mData->mRegistered
8170 && ( !i_isSessionMachine()
8171 || ( mData->mMachineState != MachineState_Aborted
8172 && mData->mMachineState != MachineState_Teleported
8173 && mData->mMachineState != MachineState_PoweredOff
8174 && !Global::IsOnline(mData->mMachineState)
8175 )
8176 )
8177 )
8178 return setError(VBOX_E_INVALID_VM_STATE,
8179 tr("The machine is not mutable or running (state is %s)"),
8180 Global::stringifyMachineState(mData->mMachineState));
8181 break;
8182 }
8183 case MutableOrSavedOrRunningStateDep:
8184 {
8185 if ( mData->mRegistered
8186 && ( !i_isSessionMachine()
8187 || ( mData->mMachineState != MachineState_Aborted
8188 && mData->mMachineState != MachineState_Teleported
8189 && mData->mMachineState != MachineState_Saved
8190 && mData->mMachineState != MachineState_AbortedSaved
8191 && mData->mMachineState != MachineState_PoweredOff
8192 && !Global::IsOnline(mData->mMachineState)
8193 )
8194 )
8195 )
8196 return setError(VBOX_E_INVALID_VM_STATE,
8197 tr("The machine is not mutable, saved or running (state is %s)"),
8198 Global::stringifyMachineState(mData->mMachineState));
8199 break;
8200 }
8201 }
8202
8203 return S_OK;
8204}
8205
8206/**
8207 * Helper to initialize all associated child objects and allocate data
8208 * structures.
8209 *
8210 * This method must be called as a part of the object's initialization procedure
8211 * (usually done in the #init() method).
8212 *
8213 * @note Must be called only from #init() or from #i_registeredInit().
8214 */
8215HRESULT Machine::initDataAndChildObjects()
8216{
8217 AutoCaller autoCaller(this);
8218 AssertComRCReturnRC(autoCaller.rc());
8219 AssertReturn( getObjectState().getState() == ObjectState::InInit
8220 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8221
8222 AssertReturn(!mData->mAccessible, E_FAIL);
8223
8224 /* allocate data structures */
8225 mSSData.allocate();
8226 mUserData.allocate();
8227 mHWData.allocate();
8228 mMediumAttachments.allocate();
8229 mStorageControllers.allocate();
8230 mUSBControllers.allocate();
8231
8232 /* initialize mOSTypeId */
8233 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8234
8235/** @todo r=bird: init() methods never fails, right? Why don't we make them
8236 * return void then! */
8237
8238 /* create associated BIOS settings object */
8239 unconst(mBIOSSettings).createObject();
8240 mBIOSSettings->init(this);
8241
8242 /* create associated trusted platform module object */
8243 unconst(mTrustedPlatformModule).createObject();
8244 mTrustedPlatformModule->init(this);
8245
8246 /* create associated NVRAM store object */
8247 unconst(mNvramStore).createObject();
8248 mNvramStore->init(this);
8249
8250 /* create associated record settings object */
8251 unconst(mRecordingSettings).createObject();
8252 mRecordingSettings->init(this);
8253
8254 /* create the graphics adapter object (always present) */
8255 unconst(mGraphicsAdapter).createObject();
8256 mGraphicsAdapter->init(this);
8257
8258 /* create an associated VRDE object (default is disabled) */
8259 unconst(mVRDEServer).createObject();
8260 mVRDEServer->init(this);
8261
8262 /* create associated serial port objects */
8263 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8264 {
8265 unconst(mSerialPorts[slot]).createObject();
8266 mSerialPorts[slot]->init(this, slot);
8267 }
8268
8269 /* create associated parallel port objects */
8270 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8271 {
8272 unconst(mParallelPorts[slot]).createObject();
8273 mParallelPorts[slot]->init(this, slot);
8274 }
8275
8276 /* create the audio adapter object (always present, default is disabled) */
8277 unconst(mAudioAdapter).createObject();
8278 mAudioAdapter->init(this);
8279
8280 /* create the USB device filters object (always present) */
8281 unconst(mUSBDeviceFilters).createObject();
8282 mUSBDeviceFilters->init(this);
8283
8284 /* create associated network adapter objects */
8285 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8286 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8287 {
8288 unconst(mNetworkAdapters[slot]).createObject();
8289 mNetworkAdapters[slot]->init(this, slot);
8290 }
8291
8292 /* create the bandwidth control */
8293 unconst(mBandwidthControl).createObject();
8294 mBandwidthControl->init(this);
8295
8296 return S_OK;
8297}
8298
8299/**
8300 * Helper to uninitialize all associated child objects and to free all data
8301 * structures.
8302 *
8303 * This method must be called as a part of the object's uninitialization
8304 * procedure (usually done in the #uninit() method).
8305 *
8306 * @note Must be called only from #uninit() or from #i_registeredInit().
8307 */
8308void Machine::uninitDataAndChildObjects()
8309{
8310 AutoCaller autoCaller(this);
8311 AssertComRCReturnVoid(autoCaller.rc());
8312 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8313 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8314 || getObjectState().getState() == ObjectState::InUninit
8315 || getObjectState().getState() == ObjectState::Limited);
8316
8317 /* tell all our other child objects we've been uninitialized */
8318 if (mBandwidthControl)
8319 {
8320 mBandwidthControl->uninit();
8321 unconst(mBandwidthControl).setNull();
8322 }
8323
8324 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8325 {
8326 if (mNetworkAdapters[slot])
8327 {
8328 mNetworkAdapters[slot]->uninit();
8329 unconst(mNetworkAdapters[slot]).setNull();
8330 }
8331 }
8332
8333 if (mUSBDeviceFilters)
8334 {
8335 mUSBDeviceFilters->uninit();
8336 unconst(mUSBDeviceFilters).setNull();
8337 }
8338
8339 if (mAudioAdapter)
8340 {
8341 mAudioAdapter->uninit();
8342 unconst(mAudioAdapter).setNull();
8343 }
8344
8345 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8346 {
8347 if (mParallelPorts[slot])
8348 {
8349 mParallelPorts[slot]->uninit();
8350 unconst(mParallelPorts[slot]).setNull();
8351 }
8352 }
8353
8354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8355 {
8356 if (mSerialPorts[slot])
8357 {
8358 mSerialPorts[slot]->uninit();
8359 unconst(mSerialPorts[slot]).setNull();
8360 }
8361 }
8362
8363 if (mVRDEServer)
8364 {
8365 mVRDEServer->uninit();
8366 unconst(mVRDEServer).setNull();
8367 }
8368
8369 if (mGraphicsAdapter)
8370 {
8371 mGraphicsAdapter->uninit();
8372 unconst(mGraphicsAdapter).setNull();
8373 }
8374
8375 if (mBIOSSettings)
8376 {
8377 mBIOSSettings->uninit();
8378 unconst(mBIOSSettings).setNull();
8379 }
8380
8381 if (mTrustedPlatformModule)
8382 {
8383 mTrustedPlatformModule->uninit();
8384 unconst(mTrustedPlatformModule).setNull();
8385 }
8386
8387 if (mNvramStore)
8388 {
8389 mNvramStore->uninit();
8390 unconst(mNvramStore).setNull();
8391 }
8392
8393 if (mRecordingSettings)
8394 {
8395 mRecordingSettings->uninit();
8396 unconst(mRecordingSettings).setNull();
8397 }
8398
8399 /* Deassociate media (only when a real Machine or a SnapshotMachine
8400 * instance is uninitialized; SessionMachine instances refer to real
8401 * Machine media). This is necessary for a clean re-initialization of
8402 * the VM after successfully re-checking the accessibility state. Note
8403 * that in case of normal Machine or SnapshotMachine uninitialization (as
8404 * a result of unregistering or deleting the snapshot), outdated media
8405 * attachments will already be uninitialized and deleted, so this
8406 * code will not affect them. */
8407 if ( !mMediumAttachments.isNull()
8408 && !i_isSessionMachine()
8409 )
8410 {
8411 for (MediumAttachmentList::const_iterator
8412 it = mMediumAttachments->begin();
8413 it != mMediumAttachments->end();
8414 ++it)
8415 {
8416 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8417 if (pMedium.isNull())
8418 continue;
8419 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8420 AssertComRC(rc);
8421 }
8422 }
8423
8424 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8425 {
8426 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8427 if (mData->mFirstSnapshot)
8428 {
8429 // snapshots tree is protected by machine write lock; strictly
8430 // this isn't necessary here since we're deleting the entire
8431 // machine, but otherwise we assert in Snapshot::uninit()
8432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8433 mData->mFirstSnapshot->uninit();
8434 mData->mFirstSnapshot.setNull();
8435 }
8436
8437 mData->mCurrentSnapshot.setNull();
8438 }
8439
8440 /* free data structures (the essential mData structure is not freed here
8441 * since it may be still in use) */
8442 mMediumAttachments.free();
8443 mStorageControllers.free();
8444 mUSBControllers.free();
8445 mHWData.free();
8446 mUserData.free();
8447 mSSData.free();
8448}
8449
8450/**
8451 * Returns a pointer to the Machine object for this machine that acts like a
8452 * parent for complex machine data objects such as shared folders, etc.
8453 *
8454 * For primary Machine objects and for SnapshotMachine objects, returns this
8455 * object's pointer itself. For SessionMachine objects, returns the peer
8456 * (primary) machine pointer.
8457 */
8458Machine *Machine::i_getMachine()
8459{
8460 if (i_isSessionMachine())
8461 return (Machine*)mPeer;
8462 return this;
8463}
8464
8465/**
8466 * Makes sure that there are no machine state dependents. If necessary, waits
8467 * for the number of dependents to drop to zero.
8468 *
8469 * Make sure this method is called from under this object's write lock to
8470 * guarantee that no new dependents may be added when this method returns
8471 * control to the caller.
8472 *
8473 * @note Receives a lock to this object for writing. The lock will be released
8474 * while waiting (if necessary).
8475 *
8476 * @warning To be used only in methods that change the machine state!
8477 */
8478void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8479{
8480 AssertReturnVoid(isWriteLockOnCurrentThread());
8481
8482 /* Wait for all state dependents if necessary */
8483 if (mData->mMachineStateDeps != 0)
8484 {
8485 /* lazy semaphore creation */
8486 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8487 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8488
8489 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8490 mData->mMachineStateDeps));
8491
8492 ++mData->mMachineStateChangePending;
8493
8494 /* reset the semaphore before waiting, the last dependent will signal
8495 * it */
8496 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8497
8498 alock.release();
8499
8500 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8501
8502 alock.acquire();
8503
8504 -- mData->mMachineStateChangePending;
8505 }
8506}
8507
8508/**
8509 * Changes the machine state and informs callbacks.
8510 *
8511 * This method is not intended to fail so it either returns S_OK or asserts (and
8512 * returns a failure).
8513 *
8514 * @note Locks this object for writing.
8515 */
8516HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8517{
8518 LogFlowThisFuncEnter();
8519 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8520 Assert(aMachineState != MachineState_Null);
8521
8522 AutoCaller autoCaller(this);
8523 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8524
8525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8526
8527 /* wait for state dependents to drop to zero */
8528 i_ensureNoStateDependencies(alock);
8529
8530 MachineState_T const enmOldState = mData->mMachineState;
8531 if (enmOldState != aMachineState)
8532 {
8533 mData->mMachineState = aMachineState;
8534 RTTimeNow(&mData->mLastStateChange);
8535
8536#ifdef VBOX_WITH_DTRACE_R3_MAIN
8537 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8538#endif
8539 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8540 }
8541
8542 LogFlowThisFuncLeave();
8543 return S_OK;
8544}
8545
8546/**
8547 * Searches for a shared folder with the given logical name
8548 * in the collection of shared folders.
8549 *
8550 * @param aName logical name of the shared folder
8551 * @param aSharedFolder where to return the found object
8552 * @param aSetError whether to set the error info if the folder is
8553 * not found
8554 * @return
8555 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8556 *
8557 * @note
8558 * must be called from under the object's lock!
8559 */
8560HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8561 ComObjPtr<SharedFolder> &aSharedFolder,
8562 bool aSetError /* = false */)
8563{
8564 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8565 for (HWData::SharedFolderList::const_iterator
8566 it = mHWData->mSharedFolders.begin();
8567 it != mHWData->mSharedFolders.end();
8568 ++it)
8569 {
8570 SharedFolder *pSF = *it;
8571 AutoCaller autoCaller(pSF);
8572 if (pSF->i_getName() == aName)
8573 {
8574 aSharedFolder = pSF;
8575 rc = S_OK;
8576 break;
8577 }
8578 }
8579
8580 if (aSetError && FAILED(rc))
8581 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8582
8583 return rc;
8584}
8585
8586/**
8587 * Initializes all machine instance data from the given settings structures
8588 * from XML. The exception is the machine UUID which needs special handling
8589 * depending on the caller's use case, so the caller needs to set that herself.
8590 *
8591 * This gets called in several contexts during machine initialization:
8592 *
8593 * -- When machine XML exists on disk already and needs to be loaded into memory,
8594 * for example, from #i_registeredInit() to load all registered machines on
8595 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8596 * attached to the machine should be part of some media registry already.
8597 *
8598 * -- During OVF import, when a machine config has been constructed from an
8599 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8600 * ensure that the media listed as attachments in the config (which have
8601 * been imported from the OVF) receive the correct registry ID.
8602 *
8603 * -- During VM cloning.
8604 *
8605 * @param config Machine settings from XML.
8606 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8607 * for each attached medium in the config.
8608 * @return
8609 */
8610HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8611 const Guid *puuidRegistry)
8612{
8613 // copy name, description, OS type, teleporter, UTC etc.
8614 mUserData->s = config.machineUserData;
8615
8616 // look up the object by Id to check it is valid
8617 ComObjPtr<GuestOSType> pGuestOSType;
8618 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8619 if (!pGuestOSType.isNull())
8620 mUserData->s.strOsType = pGuestOSType->i_id();
8621
8622 // stateFile (optional)
8623 if (config.strStateFile.isEmpty())
8624 mSSData->strStateFilePath.setNull();
8625 else
8626 {
8627 Utf8Str stateFilePathFull(config.strStateFile);
8628 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8629 if (RT_FAILURE(vrc))
8630 return setErrorBoth(E_FAIL, vrc,
8631 tr("Invalid saved state file path '%s' (%Rrc)"),
8632 config.strStateFile.c_str(),
8633 vrc);
8634 mSSData->strStateFilePath = stateFilePathFull;
8635 }
8636
8637 // snapshot folder needs special processing so set it again
8638 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8639 if (FAILED(rc)) return rc;
8640
8641 /* Copy the extra data items (config may or may not be the same as
8642 * mData->pMachineConfigFile) if necessary. When loading the XML files
8643 * from disk they are the same, but not for OVF import. */
8644 if (mData->pMachineConfigFile != &config)
8645 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8646
8647 /* currentStateModified (optional, default is true) */
8648 mData->mCurrentStateModified = config.fCurrentStateModified;
8649
8650 mData->mLastStateChange = config.timeLastStateChange;
8651
8652 /*
8653 * note: all mUserData members must be assigned prior this point because
8654 * we need to commit changes in order to let mUserData be shared by all
8655 * snapshot machine instances.
8656 */
8657 mUserData.commitCopy();
8658
8659 // machine registry, if present (must be loaded before snapshots)
8660 if (config.canHaveOwnMediaRegistry())
8661 {
8662 // determine machine folder
8663 Utf8Str strMachineFolder = i_getSettingsFileFull();
8664 strMachineFolder.stripFilename();
8665 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8666 config.mediaRegistry,
8667 strMachineFolder);
8668 if (FAILED(rc)) return rc;
8669 }
8670
8671 /* Snapshot node (optional) */
8672 size_t cRootSnapshots;
8673 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8674 {
8675 // there must be only one root snapshot
8676 Assert(cRootSnapshots == 1);
8677
8678 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8679
8680 rc = i_loadSnapshot(snap,
8681 config.uuidCurrentSnapshot,
8682 NULL); // no parent == first snapshot
8683 if (FAILED(rc)) return rc;
8684 }
8685
8686 // hardware data
8687 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8688 if (FAILED(rc)) return rc;
8689
8690 /*
8691 * NOTE: the assignment below must be the last thing to do,
8692 * otherwise it will be not possible to change the settings
8693 * somewhere in the code above because all setters will be
8694 * blocked by i_checkStateDependency(MutableStateDep).
8695 */
8696
8697 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8698 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8699 {
8700 /* no need to use i_setMachineState() during init() */
8701 mData->mMachineState = MachineState_AbortedSaved;
8702 }
8703 else if (config.fAborted)
8704 {
8705 mSSData->strStateFilePath.setNull();
8706
8707 /* no need to use i_setMachineState() during init() */
8708 mData->mMachineState = MachineState_Aborted;
8709 }
8710 else if (!mSSData->strStateFilePath.isEmpty())
8711 {
8712 /* no need to use i_setMachineState() during init() */
8713 mData->mMachineState = MachineState_Saved;
8714 }
8715
8716 // after loading settings, we are no longer different from the XML on disk
8717 mData->flModifications = 0;
8718
8719 return S_OK;
8720}
8721
8722/**
8723 * Recursively loads all snapshots starting from the given.
8724 *
8725 * @param data snapshot settings.
8726 * @param aCurSnapshotId Current snapshot ID from the settings file.
8727 * @param aParentSnapshot Parent snapshot.
8728 */
8729HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8730 const Guid &aCurSnapshotId,
8731 Snapshot *aParentSnapshot)
8732{
8733 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8734 AssertReturn(!i_isSessionMachine(), E_FAIL);
8735
8736 HRESULT rc = S_OK;
8737
8738 Utf8Str strStateFile;
8739 if (!data.strStateFile.isEmpty())
8740 {
8741 /* optional */
8742 strStateFile = data.strStateFile;
8743 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8744 if (RT_FAILURE(vrc))
8745 return setErrorBoth(E_FAIL, vrc,
8746 tr("Invalid saved state file path '%s' (%Rrc)"),
8747 strStateFile.c_str(),
8748 vrc);
8749 }
8750
8751 /* create a snapshot machine object */
8752 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8753 pSnapshotMachine.createObject();
8754 rc = pSnapshotMachine->initFromSettings(this,
8755 data.hardware,
8756 &data.debugging,
8757 &data.autostart,
8758 data.uuid.ref(),
8759 strStateFile);
8760 if (FAILED(rc)) return rc;
8761
8762 /* create a snapshot object */
8763 ComObjPtr<Snapshot> pSnapshot;
8764 pSnapshot.createObject();
8765 /* initialize the snapshot */
8766 rc = pSnapshot->init(mParent, // VirtualBox object
8767 data.uuid,
8768 data.strName,
8769 data.strDescription,
8770 data.timestamp,
8771 pSnapshotMachine,
8772 aParentSnapshot);
8773 if (FAILED(rc)) return rc;
8774
8775 /* memorize the first snapshot if necessary */
8776 if (!mData->mFirstSnapshot)
8777 mData->mFirstSnapshot = pSnapshot;
8778
8779 /* memorize the current snapshot when appropriate */
8780 if ( !mData->mCurrentSnapshot
8781 && pSnapshot->i_getId() == aCurSnapshotId
8782 )
8783 mData->mCurrentSnapshot = pSnapshot;
8784
8785 // now create the children
8786 for (settings::SnapshotsList::const_iterator
8787 it = data.llChildSnapshots.begin();
8788 it != data.llChildSnapshots.end();
8789 ++it)
8790 {
8791 const settings::Snapshot &childData = *it;
8792 // recurse
8793 rc = i_loadSnapshot(childData,
8794 aCurSnapshotId,
8795 pSnapshot); // parent = the one we created above
8796 if (FAILED(rc)) return rc;
8797 }
8798
8799 return rc;
8800}
8801
8802/**
8803 * Loads settings into mHWData.
8804 *
8805 * @param puuidRegistry Registry ID.
8806 * @param puuidSnapshot Snapshot ID
8807 * @param data Reference to the hardware settings.
8808 * @param pDbg Pointer to the debugging settings.
8809 * @param pAutostart Pointer to the autostart settings.
8810 */
8811HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8812 const Guid *puuidSnapshot,
8813 const settings::Hardware &data,
8814 const settings::Debugging *pDbg,
8815 const settings::Autostart *pAutostart)
8816{
8817 AssertReturn(!i_isSessionMachine(), E_FAIL);
8818
8819 HRESULT rc = S_OK;
8820
8821 try
8822 {
8823 ComObjPtr<GuestOSType> pGuestOSType;
8824 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8825
8826 /* The hardware version attribute (optional). */
8827 mHWData->mHWVersion = data.strVersion;
8828 mHWData->mHardwareUUID = data.uuid;
8829
8830 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8831 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8832 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8833 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8834 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8835 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8836 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8837 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8838 mHWData->mPAEEnabled = data.fPAE;
8839 mHWData->mLongMode = data.enmLongMode;
8840 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8841 mHWData->mAPIC = data.fAPIC;
8842 mHWData->mX2APIC = data.fX2APIC;
8843 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8844 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8845 mHWData->mSpecCtrl = data.fSpecCtrl;
8846 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8847 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8848 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8849 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8850 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8851 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8852 mHWData->mCPUCount = data.cCPUs;
8853 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8854 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8855 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8856 mHWData->mCpuProfile = data.strCpuProfile;
8857
8858 // cpu
8859 if (mHWData->mCPUHotPlugEnabled)
8860 {
8861 for (settings::CpuList::const_iterator
8862 it = data.llCpus.begin();
8863 it != data.llCpus.end();
8864 ++it)
8865 {
8866 const settings::Cpu &cpu = *it;
8867
8868 mHWData->mCPUAttached[cpu.ulId] = true;
8869 }
8870 }
8871
8872 // cpuid leafs
8873 for (settings::CpuIdLeafsList::const_iterator
8874 it = data.llCpuIdLeafs.begin();
8875 it != data.llCpuIdLeafs.end();
8876 ++it)
8877 {
8878 const settings::CpuIdLeaf &rLeaf= *it;
8879 if ( rLeaf.idx < UINT32_C(0x20)
8880 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8881 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8882 mHWData->mCpuIdLeafList.push_back(rLeaf);
8883 /* else: just ignore */
8884 }
8885
8886 mHWData->mMemorySize = data.ulMemorySizeMB;
8887 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8888
8889 // boot order
8890 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8891 {
8892 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8893 if (it == data.mapBootOrder.end())
8894 mHWData->mBootOrder[i] = DeviceType_Null;
8895 else
8896 mHWData->mBootOrder[i] = it->second;
8897 }
8898
8899 mHWData->mFirmwareType = data.firmwareType;
8900 mHWData->mPointingHIDType = data.pointingHIDType;
8901 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8902 mHWData->mChipsetType = data.chipsetType;
8903 mHWData->mIommuType = data.iommuType;
8904 mHWData->mParavirtProvider = data.paravirtProvider;
8905 mHWData->mParavirtDebug = data.strParavirtDebug;
8906 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8907 mHWData->mHPETEnabled = data.fHPETEnabled;
8908
8909 /* GraphicsAdapter */
8910 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8911 if (FAILED(rc)) return rc;
8912
8913 /* VRDEServer */
8914 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8915 if (FAILED(rc)) return rc;
8916
8917 /* BIOS */
8918 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8919 if (FAILED(rc)) return rc;
8920
8921 /* Trusted Platform Module */
8922 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8923 if (FAILED(rc)) return rc;
8924
8925 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8926 if (FAILED(rc)) return rc;
8927
8928 /* Recording settings */
8929 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8930 if (FAILED(rc)) return rc;
8931
8932 // Bandwidth control (must come before network adapters)
8933 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8934 if (FAILED(rc)) return rc;
8935
8936 /* USB controllers */
8937 for (settings::USBControllerList::const_iterator
8938 it = data.usbSettings.llUSBControllers.begin();
8939 it != data.usbSettings.llUSBControllers.end();
8940 ++it)
8941 {
8942 const settings::USBController &settingsCtrl = *it;
8943 ComObjPtr<USBController> newCtrl;
8944
8945 newCtrl.createObject();
8946 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8947 mUSBControllers->push_back(newCtrl);
8948 }
8949
8950 /* USB device filters */
8951 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8952 if (FAILED(rc)) return rc;
8953
8954 // network adapters (establish array size first and apply defaults, to
8955 // ensure reading the same settings as we saved, since the list skips
8956 // adapters having defaults)
8957 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8958 size_t oldCount = mNetworkAdapters.size();
8959 if (newCount > oldCount)
8960 {
8961 mNetworkAdapters.resize(newCount);
8962 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8963 {
8964 unconst(mNetworkAdapters[slot]).createObject();
8965 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8966 }
8967 }
8968 else if (newCount < oldCount)
8969 mNetworkAdapters.resize(newCount);
8970 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8971 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8972 for (settings::NetworkAdaptersList::const_iterator
8973 it = data.llNetworkAdapters.begin();
8974 it != data.llNetworkAdapters.end();
8975 ++it)
8976 {
8977 const settings::NetworkAdapter &nic = *it;
8978
8979 /* slot uniqueness is guaranteed by XML Schema */
8980 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8981 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8982 if (FAILED(rc)) return rc;
8983 }
8984
8985 // serial ports (establish defaults first, to ensure reading the same
8986 // settings as we saved, since the list skips ports having defaults)
8987 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8988 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8989 for (settings::SerialPortsList::const_iterator
8990 it = data.llSerialPorts.begin();
8991 it != data.llSerialPorts.end();
8992 ++it)
8993 {
8994 const settings::SerialPort &s = *it;
8995
8996 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8997 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8998 if (FAILED(rc)) return rc;
8999 }
9000
9001 // parallel ports (establish defaults first, to ensure reading the same
9002 // settings as we saved, since the list skips ports having defaults)
9003 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9004 mParallelPorts[i]->i_applyDefaults();
9005 for (settings::ParallelPortsList::const_iterator
9006 it = data.llParallelPorts.begin();
9007 it != data.llParallelPorts.end();
9008 ++it)
9009 {
9010 const settings::ParallelPort &p = *it;
9011
9012 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9013 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9014 if (FAILED(rc)) return rc;
9015 }
9016
9017 /* AudioAdapter */
9018 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9019 if (FAILED(rc)) return rc;
9020
9021 /* storage controllers */
9022 rc = i_loadStorageControllers(data.storage,
9023 puuidRegistry,
9024 puuidSnapshot);
9025 if (FAILED(rc)) return rc;
9026
9027 /* Shared folders */
9028 for (settings::SharedFoldersList::const_iterator
9029 it = data.llSharedFolders.begin();
9030 it != data.llSharedFolders.end();
9031 ++it)
9032 {
9033 const settings::SharedFolder &sf = *it;
9034
9035 ComObjPtr<SharedFolder> sharedFolder;
9036 /* Check for double entries. Not allowed! */
9037 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9038 if (SUCCEEDED(rc))
9039 return setError(VBOX_E_OBJECT_IN_USE,
9040 tr("Shared folder named '%s' already exists"),
9041 sf.strName.c_str());
9042
9043 /* Create the new shared folder. Don't break on error. This will be
9044 * reported when the machine starts. */
9045 sharedFolder.createObject();
9046 rc = sharedFolder->init(i_getMachine(),
9047 sf.strName,
9048 sf.strHostPath,
9049 RT_BOOL(sf.fWritable),
9050 RT_BOOL(sf.fAutoMount),
9051 sf.strAutoMountPoint,
9052 false /* fFailOnError */);
9053 if (FAILED(rc)) return rc;
9054 mHWData->mSharedFolders.push_back(sharedFolder);
9055 }
9056
9057 // Clipboard
9058 mHWData->mClipboardMode = data.clipboardMode;
9059 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9060
9061 // drag'n'drop
9062 mHWData->mDnDMode = data.dndMode;
9063
9064 // guest settings
9065 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9066
9067 // IO settings
9068 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9069 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9070
9071 // Host PCI devices
9072 for (settings::HostPCIDeviceAttachmentList::const_iterator
9073 it = data.pciAttachments.begin();
9074 it != data.pciAttachments.end();
9075 ++it)
9076 {
9077 const settings::HostPCIDeviceAttachment &hpda = *it;
9078 ComObjPtr<PCIDeviceAttachment> pda;
9079
9080 pda.createObject();
9081 pda->i_loadSettings(this, hpda);
9082 mHWData->mPCIDeviceAssignments.push_back(pda);
9083 }
9084
9085 /*
9086 * (The following isn't really real hardware, but it lives in HWData
9087 * for reasons of convenience.)
9088 */
9089
9090#ifdef VBOX_WITH_GUEST_PROPS
9091 /* Guest properties (optional) */
9092
9093 /* Only load transient guest properties for configs which have saved
9094 * state, because there shouldn't be any for powered off VMs. The same
9095 * logic applies for snapshots, as offline snapshots shouldn't have
9096 * any such properties. They confuse the code in various places.
9097 * Note: can't rely on the machine state, as it isn't set yet. */
9098 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9099 /* apologies for the hacky unconst() usage, but this needs hacking
9100 * actually inconsistent settings into consistency, otherwise there
9101 * will be some corner cases where the inconsistency survives
9102 * surprisingly long without getting fixed, especially for snapshots
9103 * as there are no config changes. */
9104 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9105 for (settings::GuestPropertiesList::iterator
9106 it = llGuestProperties.begin();
9107 it != llGuestProperties.end();
9108 /*nothing*/)
9109 {
9110 const settings::GuestProperty &prop = *it;
9111 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9112 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9113 if ( fSkipTransientGuestProperties
9114 && ( fFlags & GUEST_PROP_F_TRANSIENT
9115 || fFlags & GUEST_PROP_F_TRANSRESET))
9116 {
9117 it = llGuestProperties.erase(it);
9118 continue;
9119 }
9120 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9121 mHWData->mGuestProperties[prop.strName] = property;
9122 ++it;
9123 }
9124#endif /* VBOX_WITH_GUEST_PROPS defined */
9125
9126 rc = i_loadDebugging(pDbg);
9127 if (FAILED(rc))
9128 return rc;
9129
9130 mHWData->mAutostart = *pAutostart;
9131
9132 /* default frontend */
9133 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9134 }
9135 catch (std::bad_alloc &)
9136 {
9137 return E_OUTOFMEMORY;
9138 }
9139
9140 AssertComRC(rc);
9141 return rc;
9142}
9143
9144/**
9145 * Called from i_loadHardware() to load the debugging settings of the
9146 * machine.
9147 *
9148 * @param pDbg Pointer to the settings.
9149 */
9150HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9151{
9152 mHWData->mDebugging = *pDbg;
9153 /* no more processing currently required, this will probably change. */
9154 return S_OK;
9155}
9156
9157/**
9158 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9159 *
9160 * @param data storage settings.
9161 * @param puuidRegistry media registry ID to set media to or NULL;
9162 * see Machine::i_loadMachineDataFromSettings()
9163 * @param puuidSnapshot snapshot ID
9164 * @return
9165 */
9166HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9167 const Guid *puuidRegistry,
9168 const Guid *puuidSnapshot)
9169{
9170 AssertReturn(!i_isSessionMachine(), E_FAIL);
9171
9172 HRESULT rc = S_OK;
9173
9174 for (settings::StorageControllersList::const_iterator
9175 it = data.llStorageControllers.begin();
9176 it != data.llStorageControllers.end();
9177 ++it)
9178 {
9179 const settings::StorageController &ctlData = *it;
9180
9181 ComObjPtr<StorageController> pCtl;
9182 /* Try to find one with the name first. */
9183 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9184 if (SUCCEEDED(rc))
9185 return setError(VBOX_E_OBJECT_IN_USE,
9186 tr("Storage controller named '%s' already exists"),
9187 ctlData.strName.c_str());
9188
9189 pCtl.createObject();
9190 rc = pCtl->init(this,
9191 ctlData.strName,
9192 ctlData.storageBus,
9193 ctlData.ulInstance,
9194 ctlData.fBootable);
9195 if (FAILED(rc)) return rc;
9196
9197 mStorageControllers->push_back(pCtl);
9198
9199 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9200 if (FAILED(rc)) return rc;
9201
9202 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9203 if (FAILED(rc)) return rc;
9204
9205 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9206 if (FAILED(rc)) return rc;
9207
9208 /* Load the attached devices now. */
9209 rc = i_loadStorageDevices(pCtl,
9210 ctlData,
9211 puuidRegistry,
9212 puuidSnapshot);
9213 if (FAILED(rc)) return rc;
9214 }
9215
9216 return S_OK;
9217}
9218
9219/**
9220 * Called from i_loadStorageControllers for a controller's devices.
9221 *
9222 * @param aStorageController
9223 * @param data
9224 * @param puuidRegistry media registry ID to set media to or NULL; see
9225 * Machine::i_loadMachineDataFromSettings()
9226 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9227 * @return
9228 */
9229HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9230 const settings::StorageController &data,
9231 const Guid *puuidRegistry,
9232 const Guid *puuidSnapshot)
9233{
9234 HRESULT rc = S_OK;
9235
9236 /* paranoia: detect duplicate attachments */
9237 for (settings::AttachedDevicesList::const_iterator
9238 it = data.llAttachedDevices.begin();
9239 it != data.llAttachedDevices.end();
9240 ++it)
9241 {
9242 const settings::AttachedDevice &ad = *it;
9243
9244 for (settings::AttachedDevicesList::const_iterator it2 = it;
9245 it2 != data.llAttachedDevices.end();
9246 ++it2)
9247 {
9248 if (it == it2)
9249 continue;
9250
9251 const settings::AttachedDevice &ad2 = *it2;
9252
9253 if ( ad.lPort == ad2.lPort
9254 && ad.lDevice == ad2.lDevice)
9255 {
9256 return setError(E_FAIL,
9257 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9258 aStorageController->i_getName().c_str(),
9259 ad.lPort,
9260 ad.lDevice,
9261 mUserData->s.strName.c_str());
9262 }
9263 }
9264 }
9265
9266 for (settings::AttachedDevicesList::const_iterator
9267 it = data.llAttachedDevices.begin();
9268 it != data.llAttachedDevices.end();
9269 ++it)
9270 {
9271 const settings::AttachedDevice &dev = *it;
9272 ComObjPtr<Medium> medium;
9273
9274 switch (dev.deviceType)
9275 {
9276 case DeviceType_Floppy:
9277 case DeviceType_DVD:
9278 if (dev.strHostDriveSrc.isNotEmpty())
9279 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9280 false /* fRefresh */, medium);
9281 else
9282 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9283 dev.uuid,
9284 false /* fRefresh */,
9285 false /* aSetError */,
9286 medium);
9287 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9288 // This is not an error. The host drive or UUID might have vanished, so just go
9289 // ahead without this removeable medium attachment
9290 rc = S_OK;
9291 break;
9292
9293 case DeviceType_HardDisk:
9294 {
9295 /* find a hard disk by UUID */
9296 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9297 if (FAILED(rc))
9298 {
9299 if (i_isSnapshotMachine())
9300 {
9301 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9302 // so the user knows that the bad disk is in a snapshot somewhere
9303 com::ErrorInfo info;
9304 return setError(E_FAIL,
9305 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9306 puuidSnapshot->raw(),
9307 info.getText().raw());
9308 }
9309 else
9310 return rc;
9311 }
9312
9313 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9314
9315 if (medium->i_getType() == MediumType_Immutable)
9316 {
9317 if (i_isSnapshotMachine())
9318 return setError(E_FAIL,
9319 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9320 "of the virtual machine '%s' ('%s')"),
9321 medium->i_getLocationFull().c_str(),
9322 dev.uuid.raw(),
9323 puuidSnapshot->raw(),
9324 mUserData->s.strName.c_str(),
9325 mData->m_strConfigFileFull.c_str());
9326
9327 return setError(E_FAIL,
9328 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9329 medium->i_getLocationFull().c_str(),
9330 dev.uuid.raw(),
9331 mUserData->s.strName.c_str(),
9332 mData->m_strConfigFileFull.c_str());
9333 }
9334
9335 if (medium->i_getType() == MediumType_MultiAttach)
9336 {
9337 if (i_isSnapshotMachine())
9338 return setError(E_FAIL,
9339 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9340 "of the virtual machine '%s' ('%s')"),
9341 medium->i_getLocationFull().c_str(),
9342 dev.uuid.raw(),
9343 puuidSnapshot->raw(),
9344 mUserData->s.strName.c_str(),
9345 mData->m_strConfigFileFull.c_str());
9346
9347 return setError(E_FAIL,
9348 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9349 medium->i_getLocationFull().c_str(),
9350 dev.uuid.raw(),
9351 mUserData->s.strName.c_str(),
9352 mData->m_strConfigFileFull.c_str());
9353 }
9354
9355 if ( !i_isSnapshotMachine()
9356 && medium->i_getChildren().size() != 0
9357 )
9358 return setError(E_FAIL,
9359 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9360 "because it has %d differencing child hard disks"),
9361 medium->i_getLocationFull().c_str(),
9362 dev.uuid.raw(),
9363 mUserData->s.strName.c_str(),
9364 mData->m_strConfigFileFull.c_str(),
9365 medium->i_getChildren().size());
9366
9367 if (i_findAttachment(*mMediumAttachments.data(),
9368 medium))
9369 return setError(E_FAIL,
9370 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9371 medium->i_getLocationFull().c_str(),
9372 dev.uuid.raw(),
9373 mUserData->s.strName.c_str(),
9374 mData->m_strConfigFileFull.c_str());
9375
9376 break;
9377 }
9378
9379 default:
9380 return setError(E_FAIL,
9381 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9382 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9383 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9384 }
9385
9386 if (FAILED(rc))
9387 break;
9388
9389 /* Bandwidth groups are loaded at this point. */
9390 ComObjPtr<BandwidthGroup> pBwGroup;
9391
9392 if (!dev.strBwGroup.isEmpty())
9393 {
9394 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9395 if (FAILED(rc))
9396 return setError(E_FAIL,
9397 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9398 medium->i_getLocationFull().c_str(),
9399 dev.strBwGroup.c_str(),
9400 mUserData->s.strName.c_str(),
9401 mData->m_strConfigFileFull.c_str());
9402 pBwGroup->i_reference();
9403 }
9404
9405 const Utf8Str controllerName = aStorageController->i_getName();
9406 ComObjPtr<MediumAttachment> pAttachment;
9407 pAttachment.createObject();
9408 rc = pAttachment->init(this,
9409 medium,
9410 controllerName,
9411 dev.lPort,
9412 dev.lDevice,
9413 dev.deviceType,
9414 false,
9415 dev.fPassThrough,
9416 dev.fTempEject,
9417 dev.fNonRotational,
9418 dev.fDiscard,
9419 dev.fHotPluggable,
9420 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9421 if (FAILED(rc)) break;
9422
9423 /* associate the medium with this machine and snapshot */
9424 if (!medium.isNull())
9425 {
9426 AutoCaller medCaller(medium);
9427 if (FAILED(medCaller.rc())) return medCaller.rc();
9428 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9429
9430 if (i_isSnapshotMachine())
9431 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9432 else
9433 rc = medium->i_addBackReference(mData->mUuid);
9434 /* If the medium->addBackReference fails it sets an appropriate
9435 * error message, so no need to do any guesswork here. */
9436
9437 if (puuidRegistry)
9438 // caller wants registry ID to be set on all attached media (OVF import case)
9439 medium->i_addRegistry(*puuidRegistry);
9440 }
9441
9442 if (FAILED(rc))
9443 break;
9444
9445 /* back up mMediumAttachments to let registeredInit() properly rollback
9446 * on failure (= limited accessibility) */
9447 i_setModified(IsModified_Storage);
9448 mMediumAttachments.backup();
9449 mMediumAttachments->push_back(pAttachment);
9450 }
9451
9452 return rc;
9453}
9454
9455/**
9456 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9457 *
9458 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9459 * @param aSnapshot where to return the found snapshot
9460 * @param aSetError true to set extended error info on failure
9461 */
9462HRESULT Machine::i_findSnapshotById(const Guid &aId,
9463 ComObjPtr<Snapshot> &aSnapshot,
9464 bool aSetError /* = false */)
9465{
9466 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9467
9468 if (!mData->mFirstSnapshot)
9469 {
9470 if (aSetError)
9471 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9472 return E_FAIL;
9473 }
9474
9475 if (aId.isZero())
9476 aSnapshot = mData->mFirstSnapshot;
9477 else
9478 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9479
9480 if (!aSnapshot)
9481 {
9482 if (aSetError)
9483 return setError(E_FAIL,
9484 tr("Could not find a snapshot with UUID {%s}"),
9485 aId.toString().c_str());
9486 return E_FAIL;
9487 }
9488
9489 return S_OK;
9490}
9491
9492/**
9493 * Returns the snapshot with the given name or fails of no such snapshot.
9494 *
9495 * @param strName snapshot name to find
9496 * @param aSnapshot where to return the found snapshot
9497 * @param aSetError true to set extended error info on failure
9498 */
9499HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9500 ComObjPtr<Snapshot> &aSnapshot,
9501 bool aSetError /* = false */)
9502{
9503 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9504
9505 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9506
9507 if (!mData->mFirstSnapshot)
9508 {
9509 if (aSetError)
9510 return setError(VBOX_E_OBJECT_NOT_FOUND,
9511 tr("This machine does not have any snapshots"));
9512 return VBOX_E_OBJECT_NOT_FOUND;
9513 }
9514
9515 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9516
9517 if (!aSnapshot)
9518 {
9519 if (aSetError)
9520 return setError(VBOX_E_OBJECT_NOT_FOUND,
9521 tr("Could not find a snapshot named '%s'"), strName.c_str());
9522 return VBOX_E_OBJECT_NOT_FOUND;
9523 }
9524
9525 return S_OK;
9526}
9527
9528/**
9529 * Returns a storage controller object with the given name.
9530 *
9531 * @param aName storage controller name to find
9532 * @param aStorageController where to return the found storage controller
9533 * @param aSetError true to set extended error info on failure
9534 */
9535HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9536 ComObjPtr<StorageController> &aStorageController,
9537 bool aSetError /* = false */)
9538{
9539 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9540
9541 for (StorageControllerList::const_iterator
9542 it = mStorageControllers->begin();
9543 it != mStorageControllers->end();
9544 ++it)
9545 {
9546 if ((*it)->i_getName() == aName)
9547 {
9548 aStorageController = (*it);
9549 return S_OK;
9550 }
9551 }
9552
9553 if (aSetError)
9554 return setError(VBOX_E_OBJECT_NOT_FOUND,
9555 tr("Could not find a storage controller named '%s'"),
9556 aName.c_str());
9557 return VBOX_E_OBJECT_NOT_FOUND;
9558}
9559
9560/**
9561 * Returns a USB controller object with the given name.
9562 *
9563 * @param aName USB controller name to find
9564 * @param aUSBController where to return the found USB controller
9565 * @param aSetError true to set extended error info on failure
9566 */
9567HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9568 ComObjPtr<USBController> &aUSBController,
9569 bool aSetError /* = false */)
9570{
9571 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9572
9573 for (USBControllerList::const_iterator
9574 it = mUSBControllers->begin();
9575 it != mUSBControllers->end();
9576 ++it)
9577 {
9578 if ((*it)->i_getName() == aName)
9579 {
9580 aUSBController = (*it);
9581 return S_OK;
9582 }
9583 }
9584
9585 if (aSetError)
9586 return setError(VBOX_E_OBJECT_NOT_FOUND,
9587 tr("Could not find a storage controller named '%s'"),
9588 aName.c_str());
9589 return VBOX_E_OBJECT_NOT_FOUND;
9590}
9591
9592/**
9593 * Returns the number of USB controller instance of the given type.
9594 *
9595 * @param enmType USB controller type.
9596 */
9597ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9598{
9599 ULONG cCtrls = 0;
9600
9601 for (USBControllerList::const_iterator
9602 it = mUSBControllers->begin();
9603 it != mUSBControllers->end();
9604 ++it)
9605 {
9606 if ((*it)->i_getControllerType() == enmType)
9607 cCtrls++;
9608 }
9609
9610 return cCtrls;
9611}
9612
9613HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9614 MediumAttachmentList &atts)
9615{
9616 AutoCaller autoCaller(this);
9617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9618
9619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9620
9621 for (MediumAttachmentList::const_iterator
9622 it = mMediumAttachments->begin();
9623 it != mMediumAttachments->end();
9624 ++it)
9625 {
9626 const ComObjPtr<MediumAttachment> &pAtt = *it;
9627 // should never happen, but deal with NULL pointers in the list.
9628 AssertContinue(!pAtt.isNull());
9629
9630 // getControllerName() needs caller+read lock
9631 AutoCaller autoAttCaller(pAtt);
9632 if (FAILED(autoAttCaller.rc()))
9633 {
9634 atts.clear();
9635 return autoAttCaller.rc();
9636 }
9637 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9638
9639 if (pAtt->i_getControllerName() == aName)
9640 atts.push_back(pAtt);
9641 }
9642
9643 return S_OK;
9644}
9645
9646
9647/**
9648 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9649 * file if the machine name was changed and about creating a new settings file
9650 * if this is a new machine.
9651 *
9652 * @note Must be never called directly but only from #saveSettings().
9653 */
9654HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9655 bool *pfSettingsFileIsNew)
9656{
9657 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9658
9659 HRESULT rc = S_OK;
9660
9661 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9662 /// @todo need to handle primary group change, too
9663
9664 /* attempt to rename the settings file if machine name is changed */
9665 if ( mUserData->s.fNameSync
9666 && mUserData.isBackedUp()
9667 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9668 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9669 )
9670 {
9671 bool dirRenamed = false;
9672 bool fileRenamed = false;
9673
9674 Utf8Str configFile, newConfigFile;
9675 Utf8Str configFilePrev, newConfigFilePrev;
9676 Utf8Str NVRAMFile, newNVRAMFile;
9677 Utf8Str configDir, newConfigDir;
9678
9679 do
9680 {
9681 int vrc = VINF_SUCCESS;
9682
9683 Utf8Str name = mUserData.backedUpData()->s.strName;
9684 Utf8Str newName = mUserData->s.strName;
9685 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9686 if (group == "/")
9687 group.setNull();
9688 Utf8Str newGroup = mUserData->s.llGroups.front();
9689 if (newGroup == "/")
9690 newGroup.setNull();
9691
9692 configFile = mData->m_strConfigFileFull;
9693
9694 /* first, rename the directory if it matches the group and machine name */
9695 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9696 /** @todo hack, make somehow use of ComposeMachineFilename */
9697 if (mUserData->s.fDirectoryIncludesUUID)
9698 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9699 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9700 /** @todo hack, make somehow use of ComposeMachineFilename */
9701 if (mUserData->s.fDirectoryIncludesUUID)
9702 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9703 configDir = configFile;
9704 configDir.stripFilename();
9705 newConfigDir = configDir;
9706 if ( configDir.length() >= groupPlusName.length()
9707 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9708 groupPlusName.c_str()))
9709 {
9710 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9711 Utf8Str newConfigBaseDir(newConfigDir);
9712 newConfigDir.append(newGroupPlusName);
9713 /* consistency: use \ if appropriate on the platform */
9714 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9715 /* new dir and old dir cannot be equal here because of 'if'
9716 * above and because name != newName */
9717 Assert(configDir != newConfigDir);
9718 if (!fSettingsFileIsNew)
9719 {
9720 /* perform real rename only if the machine is not new */
9721 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9722 if ( vrc == VERR_FILE_NOT_FOUND
9723 || vrc == VERR_PATH_NOT_FOUND)
9724 {
9725 /* create the parent directory, then retry renaming */
9726 Utf8Str parent(newConfigDir);
9727 parent.stripFilename();
9728 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9729 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9730 }
9731 if (RT_FAILURE(vrc))
9732 {
9733 rc = setErrorBoth(E_FAIL, vrc,
9734 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9735 configDir.c_str(),
9736 newConfigDir.c_str(),
9737 vrc);
9738 break;
9739 }
9740 /* delete subdirectories which are no longer needed */
9741 Utf8Str dir(configDir);
9742 dir.stripFilename();
9743 while (dir != newConfigBaseDir && dir != ".")
9744 {
9745 vrc = RTDirRemove(dir.c_str());
9746 if (RT_FAILURE(vrc))
9747 break;
9748 dir.stripFilename();
9749 }
9750 dirRenamed = true;
9751 }
9752 }
9753
9754 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9755
9756 /* then try to rename the settings file itself */
9757 if (newConfigFile != configFile)
9758 {
9759 /* get the path to old settings file in renamed directory */
9760 Assert(mData->m_strConfigFileFull == configFile);
9761 configFile.printf("%s%c%s",
9762 newConfigDir.c_str(),
9763 RTPATH_DELIMITER,
9764 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9765 if (!fSettingsFileIsNew)
9766 {
9767 /* perform real rename only if the machine is not new */
9768 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9769 if (RT_FAILURE(vrc))
9770 {
9771 rc = setErrorBoth(E_FAIL, vrc,
9772 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9773 configFile.c_str(),
9774 newConfigFile.c_str(),
9775 vrc);
9776 break;
9777 }
9778 fileRenamed = true;
9779 configFilePrev = configFile;
9780 configFilePrev += "-prev";
9781 newConfigFilePrev = newConfigFile;
9782 newConfigFilePrev += "-prev";
9783 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9784 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9785 if (NVRAMFile.isNotEmpty())
9786 {
9787 // in the NVRAM file path, replace the old directory with the new directory
9788 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9789 {
9790 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9791 NVRAMFile = newConfigDir + strNVRAMFile;
9792 }
9793 newNVRAMFile = newConfigFile;
9794 newNVRAMFile.stripSuffix();
9795 newNVRAMFile += ".nvram";
9796 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9797 }
9798 }
9799 }
9800
9801 // update m_strConfigFileFull amd mConfigFile
9802 mData->m_strConfigFileFull = newConfigFile;
9803 // compute the relative path too
9804 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9805
9806 // store the old and new so that VirtualBox::i_saveSettings() can update
9807 // the media registry
9808 if ( mData->mRegistered
9809 && (configDir != newConfigDir || configFile != newConfigFile))
9810 {
9811 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9812
9813 if (pfNeedsGlobalSaveSettings)
9814 *pfNeedsGlobalSaveSettings = true;
9815 }
9816
9817 // in the saved state file path, replace the old directory with the new directory
9818 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9819 {
9820 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9821 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9822 }
9823 if (newNVRAMFile.isNotEmpty())
9824 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9825
9826 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9827 if (mData->mFirstSnapshot)
9828 {
9829 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9830 newConfigDir.c_str());
9831 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9832 newConfigDir.c_str());
9833 }
9834 }
9835 while (0);
9836
9837 if (FAILED(rc))
9838 {
9839 /* silently try to rename everything back */
9840 if (fileRenamed)
9841 {
9842 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9843 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9844 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9845 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9846 }
9847 if (dirRenamed)
9848 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9849 }
9850
9851 if (FAILED(rc)) return rc;
9852 }
9853
9854 if (fSettingsFileIsNew)
9855 {
9856 /* create a virgin config file */
9857 int vrc = VINF_SUCCESS;
9858
9859 /* ensure the settings directory exists */
9860 Utf8Str path(mData->m_strConfigFileFull);
9861 path.stripFilename();
9862 if (!RTDirExists(path.c_str()))
9863 {
9864 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9865 if (RT_FAILURE(vrc))
9866 {
9867 return setErrorBoth(E_FAIL, vrc,
9868 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9869 path.c_str(),
9870 vrc);
9871 }
9872 }
9873
9874 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9875 path = mData->m_strConfigFileFull;
9876 RTFILE f = NIL_RTFILE;
9877 vrc = RTFileOpen(&f, path.c_str(),
9878 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9879 if (RT_FAILURE(vrc))
9880 return setErrorBoth(E_FAIL, vrc,
9881 tr("Could not create the settings file '%s' (%Rrc)"),
9882 path.c_str(),
9883 vrc);
9884 RTFileClose(f);
9885 }
9886 if (pfSettingsFileIsNew)
9887 *pfSettingsFileIsNew = fSettingsFileIsNew;
9888
9889 return rc;
9890}
9891
9892/**
9893 * Saves and commits machine data, user data and hardware data.
9894 *
9895 * Note that on failure, the data remains uncommitted.
9896 *
9897 * @a aFlags may combine the following flags:
9898 *
9899 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9900 * Used when saving settings after an operation that makes them 100%
9901 * correspond to the settings from the current snapshot.
9902 * - SaveS_Force: settings will be saved without doing a deep compare of the
9903 * settings structures. This is used when this is called because snapshots
9904 * have changed to avoid the overhead of the deep compare.
9905 *
9906 * @note Must be called from under this object's write lock. Locks children for
9907 * writing.
9908 *
9909 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9910 * initialized to false and that will be set to true by this function if
9911 * the caller must invoke VirtualBox::i_saveSettings() because the global
9912 * settings have changed. This will happen if a machine rename has been
9913 * saved and the global machine and media registries will therefore need
9914 * updating.
9915 * @param alock Reference to the lock for this machine object.
9916 * @param aFlags Flags.
9917 */
9918HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9919 AutoWriteLock &alock,
9920 int aFlags /*= 0*/)
9921{
9922 LogFlowThisFuncEnter();
9923
9924 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9925
9926 /* make sure child objects are unable to modify the settings while we are
9927 * saving them */
9928 i_ensureNoStateDependencies(alock);
9929
9930 AssertReturn(!i_isSnapshotMachine(),
9931 E_FAIL);
9932
9933 if (!mData->mAccessible)
9934 return setError(VBOX_E_INVALID_VM_STATE,
9935 tr("The machine is not accessible, so cannot save settings"));
9936
9937 HRESULT rc = S_OK;
9938 bool fNeedsWrite = false;
9939 bool fSettingsFileIsNew = false;
9940
9941 /* First, prepare to save settings. It will care about renaming the
9942 * settings directory and file if the machine name was changed and about
9943 * creating a new settings file if this is a new machine. */
9944 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
9945 &fSettingsFileIsNew);
9946 if (FAILED(rc)) return rc;
9947
9948 // keep a pointer to the current settings structures
9949 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9950 settings::MachineConfigFile *pNewConfig = NULL;
9951
9952 try
9953 {
9954 // make a fresh one to have everyone write stuff into
9955 pNewConfig = new settings::MachineConfigFile(NULL);
9956 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9957
9958 // now go and copy all the settings data from COM to the settings structures
9959 // (this calls i_saveSettings() on all the COM objects in the machine)
9960 i_copyMachineDataToSettings(*pNewConfig);
9961
9962 if (aFlags & SaveS_ResetCurStateModified)
9963 {
9964 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9965 mData->mCurrentStateModified = FALSE;
9966 fNeedsWrite = true; // always, no need to compare
9967 }
9968 else if (aFlags & SaveS_Force)
9969 {
9970 fNeedsWrite = true; // always, no need to compare
9971 }
9972 else
9973 {
9974 if (!mData->mCurrentStateModified)
9975 {
9976 // do a deep compare of the settings that we just saved with the settings
9977 // previously stored in the config file; this invokes MachineConfigFile::operator==
9978 // which does a deep compare of all the settings, which is expensive but less expensive
9979 // than writing out XML in vain
9980 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9981
9982 // could still be modified if any settings changed
9983 mData->mCurrentStateModified = fAnySettingsChanged;
9984
9985 fNeedsWrite = fAnySettingsChanged;
9986 }
9987 else
9988 fNeedsWrite = true;
9989 }
9990
9991 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9992
9993 if (fNeedsWrite)
9994 // now spit it all out!
9995 pNewConfig->write(mData->m_strConfigFileFull);
9996
9997 mData->pMachineConfigFile = pNewConfig;
9998 delete pOldConfig;
9999 i_commit();
10000
10001 // after saving settings, we are no longer different from the XML on disk
10002 mData->flModifications = 0;
10003 }
10004 catch (HRESULT err)
10005 {
10006 // we assume that error info is set by the thrower
10007 rc = err;
10008
10009 // delete any newly created settings file
10010 if (fSettingsFileIsNew)
10011 RTFileDelete(mData->m_strConfigFileFull.c_str());
10012
10013 // restore old config
10014 delete pNewConfig;
10015 mData->pMachineConfigFile = pOldConfig;
10016 }
10017 catch (...)
10018 {
10019 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10020 }
10021
10022 if (fNeedsWrite)
10023 {
10024 /* Fire the data change event, even on failure (since we've already
10025 * committed all data). This is done only for SessionMachines because
10026 * mutable Machine instances are always not registered (i.e. private
10027 * to the client process that creates them) and thus don't need to
10028 * inform callbacks. */
10029 if (i_isSessionMachine())
10030 mParent->i_onMachineDataChanged(mData->mUuid);
10031 }
10032
10033 LogFlowThisFunc(("rc=%08X\n", rc));
10034 LogFlowThisFuncLeave();
10035 return rc;
10036}
10037
10038/**
10039 * Implementation for saving the machine settings into the given
10040 * settings::MachineConfigFile instance. This copies machine extradata
10041 * from the previous machine config file in the instance data, if any.
10042 *
10043 * This gets called from two locations:
10044 *
10045 * -- Machine::i_saveSettings(), during the regular XML writing;
10046 *
10047 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10048 * exported to OVF and we write the VirtualBox proprietary XML
10049 * into a <vbox:Machine> tag.
10050 *
10051 * This routine fills all the fields in there, including snapshots, *except*
10052 * for the following:
10053 *
10054 * -- fCurrentStateModified. There is some special logic associated with that.
10055 *
10056 * The caller can then call MachineConfigFile::write() or do something else
10057 * with it.
10058 *
10059 * Caller must hold the machine lock!
10060 *
10061 * This throws XML errors and HRESULT, so the caller must have a catch block!
10062 */
10063void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10064{
10065 // deep copy extradata, being extra careful with self assignment (the STL
10066 // map assignment on Mac OS X clang based Xcode isn't checking)
10067 if (&config != mData->pMachineConfigFile)
10068 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10069
10070 config.uuid = mData->mUuid;
10071
10072 // copy name, description, OS type, teleport, UTC etc.
10073 config.machineUserData = mUserData->s;
10074
10075 if ( mData->mMachineState == MachineState_Saved
10076 || mData->mMachineState == MachineState_AbortedSaved
10077 || mData->mMachineState == MachineState_Restoring
10078 // when doing certain snapshot operations we may or may not have
10079 // a saved state in the current state, so keep everything as is
10080 || ( ( mData->mMachineState == MachineState_Snapshotting
10081 || mData->mMachineState == MachineState_DeletingSnapshot
10082 || mData->mMachineState == MachineState_RestoringSnapshot)
10083 && (!mSSData->strStateFilePath.isEmpty())
10084 )
10085 )
10086 {
10087 Assert(!mSSData->strStateFilePath.isEmpty());
10088 /* try to make the file name relative to the settings file dir */
10089 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10090 }
10091 else
10092 {
10093 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10094 config.strStateFile.setNull();
10095 }
10096
10097 if (mData->mCurrentSnapshot)
10098 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10099 else
10100 config.uuidCurrentSnapshot.clear();
10101
10102 config.timeLastStateChange = mData->mLastStateChange;
10103 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10104 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10105
10106 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10107 if (FAILED(rc)) throw rc;
10108
10109 // save machine's media registry if this is VirtualBox 4.0 or later
10110 if (config.canHaveOwnMediaRegistry())
10111 {
10112 // determine machine folder
10113 Utf8Str strMachineFolder = i_getSettingsFileFull();
10114 strMachineFolder.stripFilename();
10115 mParent->i_saveMediaRegistry(config.mediaRegistry,
10116 i_getId(), // only media with registry ID == machine UUID
10117 strMachineFolder);
10118 // this throws HRESULT
10119 }
10120
10121 // save snapshots
10122 rc = i_saveAllSnapshots(config);
10123 if (FAILED(rc)) throw rc;
10124}
10125
10126/**
10127 * Saves all snapshots of the machine into the given machine config file. Called
10128 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10129 * @param config
10130 * @return
10131 */
10132HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10133{
10134 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10135
10136 HRESULT rc = S_OK;
10137
10138 try
10139 {
10140 config.llFirstSnapshot.clear();
10141
10142 if (mData->mFirstSnapshot)
10143 {
10144 // the settings use a list for "the first snapshot"
10145 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10146
10147 // get reference to the snapshot on the list and work on that
10148 // element straight in the list to avoid excessive copying later
10149 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10150 if (FAILED(rc)) throw rc;
10151 }
10152
10153// if (mType == IsSessionMachine)
10154// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10155
10156 }
10157 catch (HRESULT err)
10158 {
10159 /* we assume that error info is set by the thrower */
10160 rc = err;
10161 }
10162 catch (...)
10163 {
10164 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10165 }
10166
10167 return rc;
10168}
10169
10170/**
10171 * Saves the VM hardware configuration. It is assumed that the
10172 * given node is empty.
10173 *
10174 * @param data Reference to the settings object for the hardware config.
10175 * @param pDbg Pointer to the settings object for the debugging config
10176 * which happens to live in mHWData.
10177 * @param pAutostart Pointer to the settings object for the autostart config
10178 * which happens to live in mHWData.
10179 */
10180HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10181 settings::Autostart *pAutostart)
10182{
10183 HRESULT rc = S_OK;
10184
10185 try
10186 {
10187 /* The hardware version attribute (optional).
10188 Automatically upgrade from 1 to current default hardware version
10189 when there is no saved state. (ugly!) */
10190 if ( mHWData->mHWVersion == "1"
10191 && mSSData->strStateFilePath.isEmpty()
10192 )
10193 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10194
10195 data.strVersion = mHWData->mHWVersion;
10196 data.uuid = mHWData->mHardwareUUID;
10197
10198 // CPU
10199 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10200 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10201 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10202 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10203 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10204 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10205 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10206 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10207 data.fPAE = !!mHWData->mPAEEnabled;
10208 data.enmLongMode = mHWData->mLongMode;
10209 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10210 data.fAPIC = !!mHWData->mAPIC;
10211 data.fX2APIC = !!mHWData->mX2APIC;
10212 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10213 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10214 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10215 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10216 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10217 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10218 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10219 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10220 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10221 data.cCPUs = mHWData->mCPUCount;
10222 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10223 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10224 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10225 data.strCpuProfile = mHWData->mCpuProfile;
10226
10227 data.llCpus.clear();
10228 if (data.fCpuHotPlug)
10229 {
10230 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10231 {
10232 if (mHWData->mCPUAttached[idx])
10233 {
10234 settings::Cpu cpu;
10235 cpu.ulId = idx;
10236 data.llCpus.push_back(cpu);
10237 }
10238 }
10239 }
10240
10241 /* Standard and Extended CPUID leafs. */
10242 data.llCpuIdLeafs.clear();
10243 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10244
10245 // memory
10246 data.ulMemorySizeMB = mHWData->mMemorySize;
10247 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10248
10249 // firmware
10250 data.firmwareType = mHWData->mFirmwareType;
10251
10252 // HID
10253 data.pointingHIDType = mHWData->mPointingHIDType;
10254 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10255
10256 // chipset
10257 data.chipsetType = mHWData->mChipsetType;
10258
10259 // iommu
10260 data.iommuType = mHWData->mIommuType;
10261
10262 // paravirt
10263 data.paravirtProvider = mHWData->mParavirtProvider;
10264 data.strParavirtDebug = mHWData->mParavirtDebug;
10265
10266 // emulated USB card reader
10267 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10268
10269 // HPET
10270 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10271
10272 // boot order
10273 data.mapBootOrder.clear();
10274 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10275 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10276
10277 /* VRDEServer settings (optional) */
10278 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10279 if (FAILED(rc)) throw rc;
10280
10281 /* BIOS settings (required) */
10282 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10283 if (FAILED(rc)) throw rc;
10284
10285 /* Trusted Platform Module settings (required) */
10286 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10287 if (FAILED(rc)) throw rc;
10288
10289 /* NVRAM settings (required) */
10290 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10291 if (FAILED(rc)) throw rc;
10292
10293 /* Recording settings (required) */
10294 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10295 if (FAILED(rc)) throw rc;
10296
10297 /* GraphicsAdapter settings (required) */
10298 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10299 if (FAILED(rc)) throw rc;
10300
10301 /* USB Controller (required) */
10302 data.usbSettings.llUSBControllers.clear();
10303 for (USBControllerList::const_iterator
10304 it = mUSBControllers->begin();
10305 it != mUSBControllers->end();
10306 ++it)
10307 {
10308 ComObjPtr<USBController> ctrl = *it;
10309 settings::USBController settingsCtrl;
10310
10311 settingsCtrl.strName = ctrl->i_getName();
10312 settingsCtrl.enmType = ctrl->i_getControllerType();
10313
10314 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10315 }
10316
10317 /* USB device filters (required) */
10318 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10319 if (FAILED(rc)) throw rc;
10320
10321 /* Network adapters (required) */
10322 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10323 data.llNetworkAdapters.clear();
10324 /* Write out only the nominal number of network adapters for this
10325 * chipset type. Since Machine::commit() hasn't been called there
10326 * may be extra NIC settings in the vector. */
10327 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10328 {
10329 settings::NetworkAdapter nic;
10330 nic.ulSlot = (uint32_t)slot;
10331 /* paranoia check... must not be NULL, but must not crash either. */
10332 if (mNetworkAdapters[slot])
10333 {
10334 if (mNetworkAdapters[slot]->i_hasDefaults())
10335 continue;
10336
10337 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10338 if (FAILED(rc)) throw rc;
10339
10340 data.llNetworkAdapters.push_back(nic);
10341 }
10342 }
10343
10344 /* Serial ports */
10345 data.llSerialPorts.clear();
10346 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10347 {
10348 if (mSerialPorts[slot]->i_hasDefaults())
10349 continue;
10350
10351 settings::SerialPort s;
10352 s.ulSlot = slot;
10353 rc = mSerialPorts[slot]->i_saveSettings(s);
10354 if (FAILED(rc)) return rc;
10355
10356 data.llSerialPorts.push_back(s);
10357 }
10358
10359 /* Parallel ports */
10360 data.llParallelPorts.clear();
10361 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10362 {
10363 if (mParallelPorts[slot]->i_hasDefaults())
10364 continue;
10365
10366 settings::ParallelPort p;
10367 p.ulSlot = slot;
10368 rc = mParallelPorts[slot]->i_saveSettings(p);
10369 if (FAILED(rc)) return rc;
10370
10371 data.llParallelPorts.push_back(p);
10372 }
10373
10374 /* Audio adapter */
10375 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10376 if (FAILED(rc)) return rc;
10377
10378 rc = i_saveStorageControllers(data.storage);
10379 if (FAILED(rc)) return rc;
10380
10381 /* Shared folders */
10382 data.llSharedFolders.clear();
10383 for (HWData::SharedFolderList::const_iterator
10384 it = mHWData->mSharedFolders.begin();
10385 it != mHWData->mSharedFolders.end();
10386 ++it)
10387 {
10388 SharedFolder *pSF = *it;
10389 AutoCaller sfCaller(pSF);
10390 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10391 settings::SharedFolder sf;
10392 sf.strName = pSF->i_getName();
10393 sf.strHostPath = pSF->i_getHostPath();
10394 sf.fWritable = !!pSF->i_isWritable();
10395 sf.fAutoMount = !!pSF->i_isAutoMounted();
10396 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10397
10398 data.llSharedFolders.push_back(sf);
10399 }
10400
10401 // clipboard
10402 data.clipboardMode = mHWData->mClipboardMode;
10403 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10404
10405 // drag'n'drop
10406 data.dndMode = mHWData->mDnDMode;
10407
10408 /* Guest */
10409 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10410
10411 // IO settings
10412 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10413 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10414
10415 /* BandwidthControl (required) */
10416 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10417 if (FAILED(rc)) throw rc;
10418
10419 /* Host PCI devices */
10420 data.pciAttachments.clear();
10421 for (HWData::PCIDeviceAssignmentList::const_iterator
10422 it = mHWData->mPCIDeviceAssignments.begin();
10423 it != mHWData->mPCIDeviceAssignments.end();
10424 ++it)
10425 {
10426 ComObjPtr<PCIDeviceAttachment> pda = *it;
10427 settings::HostPCIDeviceAttachment hpda;
10428
10429 rc = pda->i_saveSettings(hpda);
10430 if (FAILED(rc)) throw rc;
10431
10432 data.pciAttachments.push_back(hpda);
10433 }
10434
10435 // guest properties
10436 data.llGuestProperties.clear();
10437#ifdef VBOX_WITH_GUEST_PROPS
10438 for (HWData::GuestPropertyMap::const_iterator
10439 it = mHWData->mGuestProperties.begin();
10440 it != mHWData->mGuestProperties.end();
10441 ++it)
10442 {
10443 HWData::GuestProperty property = it->second;
10444
10445 /* Remove transient guest properties at shutdown unless we
10446 * are saving state. Note that restoring snapshot intentionally
10447 * keeps them, they will be removed if appropriate once the final
10448 * machine state is set (as crashes etc. need to work). */
10449 if ( ( mData->mMachineState == MachineState_PoweredOff
10450 || mData->mMachineState == MachineState_Aborted
10451 || mData->mMachineState == MachineState_Teleported)
10452 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10453 continue;
10454 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10455 prop.strName = it->first;
10456 prop.strValue = property.strValue;
10457 prop.timestamp = (uint64_t)property.mTimestamp;
10458 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10459 GuestPropWriteFlags(property.mFlags, szFlags);
10460 prop.strFlags = szFlags;
10461
10462 data.llGuestProperties.push_back(prop);
10463 }
10464
10465 /* I presume this doesn't require a backup(). */
10466 mData->mGuestPropertiesModified = FALSE;
10467#endif /* VBOX_WITH_GUEST_PROPS defined */
10468
10469 *pDbg = mHWData->mDebugging;
10470 *pAutostart = mHWData->mAutostart;
10471
10472 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10473 }
10474 catch (std::bad_alloc &)
10475 {
10476 return E_OUTOFMEMORY;
10477 }
10478
10479 AssertComRC(rc);
10480 return rc;
10481}
10482
10483/**
10484 * Saves the storage controller configuration.
10485 *
10486 * @param data storage settings.
10487 */
10488HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10489{
10490 data.llStorageControllers.clear();
10491
10492 for (StorageControllerList::const_iterator
10493 it = mStorageControllers->begin();
10494 it != mStorageControllers->end();
10495 ++it)
10496 {
10497 HRESULT rc;
10498 ComObjPtr<StorageController> pCtl = *it;
10499
10500 settings::StorageController ctl;
10501 ctl.strName = pCtl->i_getName();
10502 ctl.controllerType = pCtl->i_getControllerType();
10503 ctl.storageBus = pCtl->i_getStorageBus();
10504 ctl.ulInstance = pCtl->i_getInstance();
10505 ctl.fBootable = pCtl->i_getBootable();
10506
10507 /* Save the port count. */
10508 ULONG portCount;
10509 rc = pCtl->COMGETTER(PortCount)(&portCount);
10510 ComAssertComRCRet(rc, rc);
10511 ctl.ulPortCount = portCount;
10512
10513 /* Save fUseHostIOCache */
10514 BOOL fUseHostIOCache;
10515 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10516 ComAssertComRCRet(rc, rc);
10517 ctl.fUseHostIOCache = !!fUseHostIOCache;
10518
10519 /* save the devices now. */
10520 rc = i_saveStorageDevices(pCtl, ctl);
10521 ComAssertComRCRet(rc, rc);
10522
10523 data.llStorageControllers.push_back(ctl);
10524 }
10525
10526 return S_OK;
10527}
10528
10529/**
10530 * Saves the hard disk configuration.
10531 */
10532HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10533 settings::StorageController &data)
10534{
10535 MediumAttachmentList atts;
10536
10537 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10538 if (FAILED(rc)) return rc;
10539
10540 data.llAttachedDevices.clear();
10541 for (MediumAttachmentList::const_iterator
10542 it = atts.begin();
10543 it != atts.end();
10544 ++it)
10545 {
10546 settings::AttachedDevice dev;
10547 IMediumAttachment *iA = *it;
10548 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10549 Medium *pMedium = pAttach->i_getMedium();
10550
10551 dev.deviceType = pAttach->i_getType();
10552 dev.lPort = pAttach->i_getPort();
10553 dev.lDevice = pAttach->i_getDevice();
10554 dev.fPassThrough = pAttach->i_getPassthrough();
10555 dev.fHotPluggable = pAttach->i_getHotPluggable();
10556 if (pMedium)
10557 {
10558 if (pMedium->i_isHostDrive())
10559 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10560 else
10561 dev.uuid = pMedium->i_getId();
10562 dev.fTempEject = pAttach->i_getTempEject();
10563 dev.fNonRotational = pAttach->i_getNonRotational();
10564 dev.fDiscard = pAttach->i_getDiscard();
10565 }
10566
10567 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10568
10569 data.llAttachedDevices.push_back(dev);
10570 }
10571
10572 return S_OK;
10573}
10574
10575/**
10576 * Saves machine state settings as defined by aFlags
10577 * (SaveSTS_* values).
10578 *
10579 * @param aFlags Combination of SaveSTS_* flags.
10580 *
10581 * @note Locks objects for writing.
10582 */
10583HRESULT Machine::i_saveStateSettings(int aFlags)
10584{
10585 if (aFlags == 0)
10586 return S_OK;
10587
10588 AutoCaller autoCaller(this);
10589 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10590
10591 /* This object's write lock is also necessary to serialize file access
10592 * (prevent concurrent reads and writes) */
10593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10594
10595 HRESULT rc = S_OK;
10596
10597 Assert(mData->pMachineConfigFile);
10598
10599 try
10600 {
10601 if (aFlags & SaveSTS_CurStateModified)
10602 mData->pMachineConfigFile->fCurrentStateModified = true;
10603
10604 if (aFlags & SaveSTS_StateFilePath)
10605 {
10606 if (!mSSData->strStateFilePath.isEmpty())
10607 /* try to make the file name relative to the settings file dir */
10608 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10609 else
10610 mData->pMachineConfigFile->strStateFile.setNull();
10611 }
10612
10613 if (aFlags & SaveSTS_StateTimeStamp)
10614 {
10615 Assert( mData->mMachineState != MachineState_Aborted
10616 || mSSData->strStateFilePath.isEmpty());
10617
10618 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10619
10620 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10621 || mData->mMachineState == MachineState_AbortedSaved);
10622/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10623 }
10624
10625 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10626 }
10627 catch (...)
10628 {
10629 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10630 }
10631
10632 return rc;
10633}
10634
10635/**
10636 * Ensures that the given medium is added to a media registry. If this machine
10637 * was created with 4.0 or later, then the machine registry is used. Otherwise
10638 * the global VirtualBox media registry is used.
10639 *
10640 * Caller must NOT hold machine lock, media tree or any medium locks!
10641 *
10642 * @param pMedium
10643 */
10644void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10645{
10646 /* Paranoia checks: do not hold machine or media tree locks. */
10647 AssertReturnVoid(!isWriteLockOnCurrentThread());
10648 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10649
10650 ComObjPtr<Medium> pBase;
10651 {
10652 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10653 pBase = pMedium->i_getBase();
10654 }
10655
10656 /* Paranoia checks: do not hold medium locks. */
10657 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10658 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10659
10660 // decide which medium registry to use now that the medium is attached:
10661 Guid uuid;
10662 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10663 if (fCanHaveOwnMediaRegistry)
10664 // machine XML is VirtualBox 4.0 or higher:
10665 uuid = i_getId(); // machine UUID
10666 else
10667 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10668
10669 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10670 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10671 if (pMedium->i_addRegistry(uuid))
10672 mParent->i_markRegistryModified(uuid);
10673
10674 /* For more complex hard disk structures it can happen that the base
10675 * medium isn't yet associated with any medium registry. Do that now. */
10676 if (pMedium != pBase)
10677 {
10678 /* Tree lock needed by Medium::addRegistry when recursing. */
10679 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10680 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10681 {
10682 treeLock.release();
10683 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10684 treeLock.acquire();
10685 }
10686 if (pBase->i_addRegistryRecursive(uuid))
10687 {
10688 treeLock.release();
10689 mParent->i_markRegistryModified(uuid);
10690 }
10691 }
10692}
10693
10694/**
10695 * Creates differencing hard disks for all normal hard disks attached to this
10696 * machine and a new set of attachments to refer to created disks.
10697 *
10698 * Used when taking a snapshot or when deleting the current state. Gets called
10699 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10700 *
10701 * This method assumes that mMediumAttachments contains the original hard disk
10702 * attachments it needs to create diffs for. On success, these attachments will
10703 * be replaced with the created diffs.
10704 *
10705 * Attachments with non-normal hard disks are left as is.
10706 *
10707 * If @a aOnline is @c false then the original hard disks that require implicit
10708 * diffs will be locked for reading. Otherwise it is assumed that they are
10709 * already locked for writing (when the VM was started). Note that in the latter
10710 * case it is responsibility of the caller to lock the newly created diffs for
10711 * writing if this method succeeds.
10712 *
10713 * @param aProgress Progress object to run (must contain at least as
10714 * many operations left as the number of hard disks
10715 * attached).
10716 * @param aWeight Weight of this operation.
10717 * @param aOnline Whether the VM was online prior to this operation.
10718 *
10719 * @note The progress object is not marked as completed, neither on success nor
10720 * on failure. This is a responsibility of the caller.
10721 *
10722 * @note Locks this object and the media tree for writing.
10723 */
10724HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10725 ULONG aWeight,
10726 bool aOnline)
10727{
10728 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10729
10730 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10731 AssertReturn(!!pProgressControl, E_INVALIDARG);
10732
10733 AutoCaller autoCaller(this);
10734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10735
10736 AutoMultiWriteLock2 alock(this->lockHandle(),
10737 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10738
10739 /* must be in a protective state because we release the lock below */
10740 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10741 || mData->mMachineState == MachineState_OnlineSnapshotting
10742 || mData->mMachineState == MachineState_LiveSnapshotting
10743 || mData->mMachineState == MachineState_RestoringSnapshot
10744 || mData->mMachineState == MachineState_DeletingSnapshot
10745 , E_FAIL);
10746
10747 HRESULT rc = S_OK;
10748
10749 // use appropriate locked media map (online or offline)
10750 MediumLockListMap lockedMediaOffline;
10751 MediumLockListMap *lockedMediaMap;
10752 if (aOnline)
10753 lockedMediaMap = &mData->mSession.mLockedMedia;
10754 else
10755 lockedMediaMap = &lockedMediaOffline;
10756
10757 try
10758 {
10759 if (!aOnline)
10760 {
10761 /* lock all attached hard disks early to detect "in use"
10762 * situations before creating actual diffs */
10763 for (MediumAttachmentList::const_iterator
10764 it = mMediumAttachments->begin();
10765 it != mMediumAttachments->end();
10766 ++it)
10767 {
10768 MediumAttachment *pAtt = *it;
10769 if (pAtt->i_getType() == DeviceType_HardDisk)
10770 {
10771 Medium *pMedium = pAtt->i_getMedium();
10772 Assert(pMedium);
10773
10774 MediumLockList *pMediumLockList(new MediumLockList());
10775 alock.release();
10776 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10777 NULL /* pToLockWrite */,
10778 false /* fMediumLockWriteAll */,
10779 NULL,
10780 *pMediumLockList);
10781 alock.acquire();
10782 if (FAILED(rc))
10783 {
10784 delete pMediumLockList;
10785 throw rc;
10786 }
10787 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10788 if (FAILED(rc))
10789 {
10790 throw setError(rc,
10791 tr("Collecting locking information for all attached media failed"));
10792 }
10793 }
10794 }
10795
10796 /* Now lock all media. If this fails, nothing is locked. */
10797 alock.release();
10798 rc = lockedMediaMap->Lock();
10799 alock.acquire();
10800 if (FAILED(rc))
10801 {
10802 throw setError(rc,
10803 tr("Locking of attached media failed"));
10804 }
10805 }
10806
10807 /* remember the current list (note that we don't use backup() since
10808 * mMediumAttachments may be already backed up) */
10809 MediumAttachmentList atts = *mMediumAttachments.data();
10810
10811 /* start from scratch */
10812 mMediumAttachments->clear();
10813
10814 /* go through remembered attachments and create diffs for normal hard
10815 * disks and attach them */
10816 for (MediumAttachmentList::const_iterator
10817 it = atts.begin();
10818 it != atts.end();
10819 ++it)
10820 {
10821 MediumAttachment *pAtt = *it;
10822
10823 DeviceType_T devType = pAtt->i_getType();
10824 Medium *pMedium = pAtt->i_getMedium();
10825
10826 if ( devType != DeviceType_HardDisk
10827 || pMedium == NULL
10828 || pMedium->i_getType() != MediumType_Normal)
10829 {
10830 /* copy the attachment as is */
10831
10832 /** @todo the progress object created in SessionMachine::TakeSnaphot
10833 * only expects operations for hard disks. Later other
10834 * device types need to show up in the progress as well. */
10835 if (devType == DeviceType_HardDisk)
10836 {
10837 if (pMedium == NULL)
10838 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10839 aWeight); // weight
10840 else
10841 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10842 pMedium->i_getBase()->i_getName().c_str()).raw(),
10843 aWeight); // weight
10844 }
10845
10846 mMediumAttachments->push_back(pAtt);
10847 continue;
10848 }
10849
10850 /* need a diff */
10851 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10852 pMedium->i_getBase()->i_getName().c_str()).raw(),
10853 aWeight); // weight
10854
10855 Utf8Str strFullSnapshotFolder;
10856 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10857
10858 ComObjPtr<Medium> diff;
10859 diff.createObject();
10860 // store the diff in the same registry as the parent
10861 // (this cannot fail here because we can't create implicit diffs for
10862 // unregistered images)
10863 Guid uuidRegistryParent;
10864 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10865 Assert(fInRegistry); NOREF(fInRegistry);
10866 rc = diff->init(mParent,
10867 pMedium->i_getPreferredDiffFormat(),
10868 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10869 uuidRegistryParent,
10870 DeviceType_HardDisk);
10871 if (FAILED(rc)) throw rc;
10872
10873 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10874 * the push_back? Looks like we're going to release medium with the
10875 * wrong kind of lock (general issue with if we fail anywhere at all)
10876 * and an orphaned VDI in the snapshots folder. */
10877
10878 /* update the appropriate lock list */
10879 MediumLockList *pMediumLockList;
10880 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10881 AssertComRCThrowRC(rc);
10882 if (aOnline)
10883 {
10884 alock.release();
10885 /* The currently attached medium will be read-only, change
10886 * the lock type to read. */
10887 rc = pMediumLockList->Update(pMedium, false);
10888 alock.acquire();
10889 AssertComRCThrowRC(rc);
10890 }
10891
10892 /* release the locks before the potentially lengthy operation */
10893 alock.release();
10894 rc = pMedium->i_createDiffStorage(diff,
10895 pMedium->i_getPreferredDiffVariant(),
10896 pMediumLockList,
10897 NULL /* aProgress */,
10898 true /* aWait */,
10899 false /* aNotify */);
10900 alock.acquire();
10901 if (FAILED(rc)) throw rc;
10902
10903 /* actual lock list update is done in Machine::i_commitMedia */
10904
10905 rc = diff->i_addBackReference(mData->mUuid);
10906 AssertComRCThrowRC(rc);
10907
10908 /* add a new attachment */
10909 ComObjPtr<MediumAttachment> attachment;
10910 attachment.createObject();
10911 rc = attachment->init(this,
10912 diff,
10913 pAtt->i_getControllerName(),
10914 pAtt->i_getPort(),
10915 pAtt->i_getDevice(),
10916 DeviceType_HardDisk,
10917 true /* aImplicit */,
10918 false /* aPassthrough */,
10919 false /* aTempEject */,
10920 pAtt->i_getNonRotational(),
10921 pAtt->i_getDiscard(),
10922 pAtt->i_getHotPluggable(),
10923 pAtt->i_getBandwidthGroup());
10924 if (FAILED(rc)) throw rc;
10925
10926 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10927 AssertComRCThrowRC(rc);
10928 mMediumAttachments->push_back(attachment);
10929 }
10930 }
10931 catch (HRESULT aRC) { rc = aRC; }
10932
10933 /* unlock all hard disks we locked when there is no VM */
10934 if (!aOnline)
10935 {
10936 ErrorInfoKeeper eik;
10937
10938 HRESULT rc1 = lockedMediaMap->Clear();
10939 AssertComRC(rc1);
10940 }
10941
10942 return rc;
10943}
10944
10945/**
10946 * Deletes implicit differencing hard disks created either by
10947 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10948 * mMediumAttachments.
10949 *
10950 * Note that to delete hard disks created by #attachDevice() this method is
10951 * called from #i_rollbackMedia() when the changes are rolled back.
10952 *
10953 * @note Locks this object and the media tree for writing.
10954 */
10955HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10956{
10957 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10958
10959 AutoCaller autoCaller(this);
10960 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10961
10962 AutoMultiWriteLock2 alock(this->lockHandle(),
10963 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10964
10965 /* We absolutely must have backed up state. */
10966 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10967
10968 /* Check if there are any implicitly created diff images. */
10969 bool fImplicitDiffs = false;
10970 for (MediumAttachmentList::const_iterator
10971 it = mMediumAttachments->begin();
10972 it != mMediumAttachments->end();
10973 ++it)
10974 {
10975 const ComObjPtr<MediumAttachment> &pAtt = *it;
10976 if (pAtt->i_isImplicit())
10977 {
10978 fImplicitDiffs = true;
10979 break;
10980 }
10981 }
10982 /* If there is nothing to do, leave early. This saves lots of image locking
10983 * effort. It also avoids a MachineStateChanged event without real reason.
10984 * This is important e.g. when loading a VM config, because there should be
10985 * no events. Otherwise API clients can become thoroughly confused for
10986 * inaccessible VMs (the code for loading VM configs uses this method for
10987 * cleanup if the config makes no sense), as they take such events as an
10988 * indication that the VM is alive, and they would force the VM config to
10989 * be reread, leading to an endless loop. */
10990 if (!fImplicitDiffs)
10991 return S_OK;
10992
10993 HRESULT rc = S_OK;
10994 MachineState_T oldState = mData->mMachineState;
10995
10996 /* will release the lock before the potentially lengthy operation,
10997 * so protect with the special state (unless already protected) */
10998 if ( oldState != MachineState_Snapshotting
10999 && oldState != MachineState_OnlineSnapshotting
11000 && oldState != MachineState_LiveSnapshotting
11001 && oldState != MachineState_RestoringSnapshot
11002 && oldState != MachineState_DeletingSnapshot
11003 && oldState != MachineState_DeletingSnapshotOnline
11004 && oldState != MachineState_DeletingSnapshotPaused
11005 )
11006 i_setMachineState(MachineState_SettingUp);
11007
11008 // use appropriate locked media map (online or offline)
11009 MediumLockListMap lockedMediaOffline;
11010 MediumLockListMap *lockedMediaMap;
11011 if (aOnline)
11012 lockedMediaMap = &mData->mSession.mLockedMedia;
11013 else
11014 lockedMediaMap = &lockedMediaOffline;
11015
11016 try
11017 {
11018 if (!aOnline)
11019 {
11020 /* lock all attached hard disks early to detect "in use"
11021 * situations before deleting actual diffs */
11022 for (MediumAttachmentList::const_iterator
11023 it = mMediumAttachments->begin();
11024 it != mMediumAttachments->end();
11025 ++it)
11026 {
11027 MediumAttachment *pAtt = *it;
11028 if (pAtt->i_getType() == DeviceType_HardDisk)
11029 {
11030 Medium *pMedium = pAtt->i_getMedium();
11031 Assert(pMedium);
11032
11033 MediumLockList *pMediumLockList(new MediumLockList());
11034 alock.release();
11035 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11036 NULL /* pToLockWrite */,
11037 false /* fMediumLockWriteAll */,
11038 NULL,
11039 *pMediumLockList);
11040 alock.acquire();
11041
11042 if (FAILED(rc))
11043 {
11044 delete pMediumLockList;
11045 throw rc;
11046 }
11047
11048 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11049 if (FAILED(rc))
11050 throw rc;
11051 }
11052 }
11053
11054 if (FAILED(rc))
11055 throw rc;
11056 } // end of offline
11057
11058 /* Lock lists are now up to date and include implicitly created media */
11059
11060 /* Go through remembered attachments and delete all implicitly created
11061 * diffs and fix up the attachment information */
11062 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11063 MediumAttachmentList implicitAtts;
11064 for (MediumAttachmentList::const_iterator
11065 it = mMediumAttachments->begin();
11066 it != mMediumAttachments->end();
11067 ++it)
11068 {
11069 ComObjPtr<MediumAttachment> pAtt = *it;
11070 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11071 if (pMedium.isNull())
11072 continue;
11073
11074 // Implicit attachments go on the list for deletion and back references are removed.
11075 if (pAtt->i_isImplicit())
11076 {
11077 /* Deassociate and mark for deletion */
11078 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11079 rc = pMedium->i_removeBackReference(mData->mUuid);
11080 if (FAILED(rc))
11081 throw rc;
11082 implicitAtts.push_back(pAtt);
11083 continue;
11084 }
11085
11086 /* Was this medium attached before? */
11087 if (!i_findAttachment(oldAtts, pMedium))
11088 {
11089 /* no: de-associate */
11090 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11091 rc = pMedium->i_removeBackReference(mData->mUuid);
11092 if (FAILED(rc))
11093 throw rc;
11094 continue;
11095 }
11096 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11097 }
11098
11099 /* If there are implicit attachments to delete, throw away the lock
11100 * map contents (which will unlock all media) since the medium
11101 * attachments will be rolled back. Below we need to completely
11102 * recreate the lock map anyway since it is infinitely complex to
11103 * do this incrementally (would need reconstructing each attachment
11104 * change, which would be extremely hairy). */
11105 if (implicitAtts.size() != 0)
11106 {
11107 ErrorInfoKeeper eik;
11108
11109 HRESULT rc1 = lockedMediaMap->Clear();
11110 AssertComRC(rc1);
11111 }
11112
11113 /* rollback hard disk changes */
11114 mMediumAttachments.rollback();
11115
11116 MultiResult mrc(S_OK);
11117
11118 // Delete unused implicit diffs.
11119 if (implicitAtts.size() != 0)
11120 {
11121 alock.release();
11122
11123 for (MediumAttachmentList::const_iterator
11124 it = implicitAtts.begin();
11125 it != implicitAtts.end();
11126 ++it)
11127 {
11128 // Remove medium associated with this attachment.
11129 ComObjPtr<MediumAttachment> pAtt = *it;
11130 Assert(pAtt);
11131 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11132 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11133 Assert(pMedium);
11134
11135 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11136 // continue on delete failure, just collect error messages
11137 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11138 pMedium->i_getLocationFull().c_str() ));
11139 mrc = rc;
11140 }
11141 // Clear the list of deleted implicit attachments now, while not
11142 // holding the lock, as it will ultimately trigger Medium::uninit()
11143 // calls which assume that the media tree lock isn't held.
11144 implicitAtts.clear();
11145
11146 alock.acquire();
11147
11148 /* if there is a VM recreate media lock map as mentioned above,
11149 * otherwise it is a waste of time and we leave things unlocked */
11150 if (aOnline)
11151 {
11152 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11153 /* must never be NULL, but better safe than sorry */
11154 if (!pMachine.isNull())
11155 {
11156 alock.release();
11157 rc = mData->mSession.mMachine->i_lockMedia();
11158 alock.acquire();
11159 if (FAILED(rc))
11160 throw rc;
11161 }
11162 }
11163 }
11164 }
11165 catch (HRESULT aRC) {rc = aRC;}
11166
11167 if (mData->mMachineState == MachineState_SettingUp)
11168 i_setMachineState(oldState);
11169
11170 /* unlock all hard disks we locked when there is no VM */
11171 if (!aOnline)
11172 {
11173 ErrorInfoKeeper eik;
11174
11175 HRESULT rc1 = lockedMediaMap->Clear();
11176 AssertComRC(rc1);
11177 }
11178
11179 return rc;
11180}
11181
11182
11183/**
11184 * Looks through the given list of media attachments for one with the given parameters
11185 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11186 * can be searched as well if needed.
11187 *
11188 * @param ll
11189 * @param aControllerName
11190 * @param aControllerPort
11191 * @param aDevice
11192 * @return
11193 */
11194MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11195 const Utf8Str &aControllerName,
11196 LONG aControllerPort,
11197 LONG aDevice)
11198{
11199 for (MediumAttachmentList::const_iterator
11200 it = ll.begin();
11201 it != ll.end();
11202 ++it)
11203 {
11204 MediumAttachment *pAttach = *it;
11205 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11206 return pAttach;
11207 }
11208
11209 return NULL;
11210}
11211
11212/**
11213 * Looks through the given list of media attachments for one with the given parameters
11214 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11215 * can be searched as well if needed.
11216 *
11217 * @param ll
11218 * @param pMedium
11219 * @return
11220 */
11221MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11222 ComObjPtr<Medium> pMedium)
11223{
11224 for (MediumAttachmentList::const_iterator
11225 it = ll.begin();
11226 it != ll.end();
11227 ++it)
11228 {
11229 MediumAttachment *pAttach = *it;
11230 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11231 if (pMediumThis == pMedium)
11232 return pAttach;
11233 }
11234
11235 return NULL;
11236}
11237
11238/**
11239 * Looks through the given list of media attachments for one with the given parameters
11240 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11241 * can be searched as well if needed.
11242 *
11243 * @param ll
11244 * @param id
11245 * @return
11246 */
11247MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11248 Guid &id)
11249{
11250 for (MediumAttachmentList::const_iterator
11251 it = ll.begin();
11252 it != ll.end();
11253 ++it)
11254 {
11255 MediumAttachment *pAttach = *it;
11256 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11257 if (pMediumThis->i_getId() == id)
11258 return pAttach;
11259 }
11260
11261 return NULL;
11262}
11263
11264/**
11265 * Main implementation for Machine::DetachDevice. This also gets called
11266 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11267 *
11268 * @param pAttach Medium attachment to detach.
11269 * @param writeLock Machine write lock which the caller must have locked once.
11270 * This may be released temporarily in here.
11271 * @param pSnapshot If NULL, then the detachment is for the current machine.
11272 * Otherwise this is for a SnapshotMachine, and this must be
11273 * its snapshot.
11274 * @return
11275 */
11276HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11277 AutoWriteLock &writeLock,
11278 Snapshot *pSnapshot)
11279{
11280 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11281 DeviceType_T mediumType = pAttach->i_getType();
11282
11283 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11284
11285 if (pAttach->i_isImplicit())
11286 {
11287 /* attempt to implicitly delete the implicitly created diff */
11288
11289 /// @todo move the implicit flag from MediumAttachment to Medium
11290 /// and forbid any hard disk operation when it is implicit. Or maybe
11291 /// a special media state for it to make it even more simple.
11292
11293 Assert(mMediumAttachments.isBackedUp());
11294
11295 /* will release the lock before the potentially lengthy operation, so
11296 * protect with the special state */
11297 MachineState_T oldState = mData->mMachineState;
11298 i_setMachineState(MachineState_SettingUp);
11299
11300 writeLock.release();
11301
11302 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11303 true /*aWait*/,
11304 false /*aNotify*/);
11305
11306 writeLock.acquire();
11307
11308 i_setMachineState(oldState);
11309
11310 if (FAILED(rc)) return rc;
11311 }
11312
11313 i_setModified(IsModified_Storage);
11314 mMediumAttachments.backup();
11315 mMediumAttachments->remove(pAttach);
11316
11317 if (!oldmedium.isNull())
11318 {
11319 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11320 if (pSnapshot)
11321 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11322 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11323 else if (mediumType != DeviceType_HardDisk)
11324 oldmedium->i_removeBackReference(mData->mUuid);
11325 }
11326
11327 return S_OK;
11328}
11329
11330/**
11331 * Goes thru all media of the given list and
11332 *
11333 * 1) calls i_detachDevice() on each of them for this machine and
11334 * 2) adds all Medium objects found in the process to the given list,
11335 * depending on cleanupMode.
11336 *
11337 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11338 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11339 * media to the list.
11340 *
11341 * This gets called from Machine::Unregister, both for the actual Machine and
11342 * the SnapshotMachine objects that might be found in the snapshots.
11343 *
11344 * Requires caller and locking. The machine lock must be passed in because it
11345 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11346 *
11347 * @param writeLock Machine lock from top-level caller; this gets passed to
11348 * i_detachDevice.
11349 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11350 * object if called for a SnapshotMachine.
11351 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11352 * added to llMedia; if Full, then all media get added;
11353 * otherwise no media get added.
11354 * @param llMedia Caller's list to receive Medium objects which got detached so
11355 * caller can close() them, depending on cleanupMode.
11356 * @return
11357 */
11358HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11359 Snapshot *pSnapshot,
11360 CleanupMode_T cleanupMode,
11361 MediaList &llMedia)
11362{
11363 Assert(isWriteLockOnCurrentThread());
11364
11365 HRESULT rc;
11366
11367 // make a temporary list because i_detachDevice invalidates iterators into
11368 // mMediumAttachments
11369 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11370
11371 for (MediumAttachmentList::iterator
11372 it = llAttachments2.begin();
11373 it != llAttachments2.end();
11374 ++it)
11375 {
11376 ComObjPtr<MediumAttachment> &pAttach = *it;
11377 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11378
11379 if (!pMedium.isNull())
11380 {
11381 AutoCaller mac(pMedium);
11382 if (FAILED(mac.rc())) return mac.rc();
11383 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11384 DeviceType_T devType = pMedium->i_getDeviceType();
11385 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11386 && devType == DeviceType_HardDisk)
11387 || (cleanupMode == CleanupMode_Full)
11388 )
11389 {
11390 llMedia.push_back(pMedium);
11391 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11392 /* Not allowed to keep this lock as below we need the parent
11393 * medium lock, and the lock order is parent to child. */
11394 lock.release();
11395 /*
11396 * Search for medias which are not attached to any machine, but
11397 * in the chain to an attached disk. Mediums are only consided
11398 * if they are:
11399 * - have only one child
11400 * - no references to any machines
11401 * - are of normal medium type
11402 */
11403 while (!pParent.isNull())
11404 {
11405 AutoCaller mac1(pParent);
11406 if (FAILED(mac1.rc())) return mac1.rc();
11407 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11408 if (pParent->i_getChildren().size() == 1)
11409 {
11410 if ( pParent->i_getMachineBackRefCount() == 0
11411 && pParent->i_getType() == MediumType_Normal
11412 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11413 llMedia.push_back(pParent);
11414 }
11415 else
11416 break;
11417 pParent = pParent->i_getParent();
11418 }
11419 }
11420 }
11421
11422 // real machine: then we need to use the proper method
11423 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11424
11425 if (FAILED(rc))
11426 return rc;
11427 }
11428
11429 return S_OK;
11430}
11431
11432/**
11433 * Perform deferred hard disk detachments.
11434 *
11435 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11436 * changed (not backed up).
11437 *
11438 * If @a aOnline is @c true then this method will also unlock the old hard
11439 * disks for which the new implicit diffs were created and will lock these new
11440 * diffs for writing.
11441 *
11442 * @param aOnline Whether the VM was online prior to this operation.
11443 *
11444 * @note Locks this object for writing!
11445 */
11446void Machine::i_commitMedia(bool aOnline /*= false*/)
11447{
11448 AutoCaller autoCaller(this);
11449 AssertComRCReturnVoid(autoCaller.rc());
11450
11451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11452
11453 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11454
11455 HRESULT rc = S_OK;
11456
11457 /* no attach/detach operations -- nothing to do */
11458 if (!mMediumAttachments.isBackedUp())
11459 return;
11460
11461 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11462 bool fMediaNeedsLocking = false;
11463
11464 /* enumerate new attachments */
11465 for (MediumAttachmentList::const_iterator
11466 it = mMediumAttachments->begin();
11467 it != mMediumAttachments->end();
11468 ++it)
11469 {
11470 MediumAttachment *pAttach = *it;
11471
11472 pAttach->i_commit();
11473
11474 Medium *pMedium = pAttach->i_getMedium();
11475 bool fImplicit = pAttach->i_isImplicit();
11476
11477 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11478 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11479 fImplicit));
11480
11481 /** @todo convert all this Machine-based voodoo to MediumAttachment
11482 * based commit logic. */
11483 if (fImplicit)
11484 {
11485 /* convert implicit attachment to normal */
11486 pAttach->i_setImplicit(false);
11487
11488 if ( aOnline
11489 && pMedium
11490 && pAttach->i_getType() == DeviceType_HardDisk
11491 )
11492 {
11493 /* update the appropriate lock list */
11494 MediumLockList *pMediumLockList;
11495 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11496 AssertComRC(rc);
11497 if (pMediumLockList)
11498 {
11499 /* unlock if there's a need to change the locking */
11500 if (!fMediaNeedsLocking)
11501 {
11502 rc = mData->mSession.mLockedMedia.Unlock();
11503 AssertComRC(rc);
11504 fMediaNeedsLocking = true;
11505 }
11506 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11507 AssertComRC(rc);
11508 rc = pMediumLockList->Append(pMedium, true);
11509 AssertComRC(rc);
11510 }
11511 }
11512
11513 continue;
11514 }
11515
11516 if (pMedium)
11517 {
11518 /* was this medium attached before? */
11519 for (MediumAttachmentList::iterator
11520 oldIt = oldAtts.begin();
11521 oldIt != oldAtts.end();
11522 ++oldIt)
11523 {
11524 MediumAttachment *pOldAttach = *oldIt;
11525 if (pOldAttach->i_getMedium() == pMedium)
11526 {
11527 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11528
11529 /* yes: remove from old to avoid de-association */
11530 oldAtts.erase(oldIt);
11531 break;
11532 }
11533 }
11534 }
11535 }
11536
11537 /* enumerate remaining old attachments and de-associate from the
11538 * current machine state */
11539 for (MediumAttachmentList::const_iterator
11540 it = oldAtts.begin();
11541 it != oldAtts.end();
11542 ++it)
11543 {
11544 MediumAttachment *pAttach = *it;
11545 Medium *pMedium = pAttach->i_getMedium();
11546
11547 /* Detach only hard disks, since DVD/floppy media is detached
11548 * instantly in MountMedium. */
11549 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11550 {
11551 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11552
11553 /* now de-associate from the current machine state */
11554 rc = pMedium->i_removeBackReference(mData->mUuid);
11555 AssertComRC(rc);
11556
11557 if (aOnline)
11558 {
11559 /* unlock since medium is not used anymore */
11560 MediumLockList *pMediumLockList;
11561 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11562 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11563 {
11564 /* this happens for online snapshots, there the attachment
11565 * is changing, but only to a diff image created under
11566 * the old one, so there is no separate lock list */
11567 Assert(!pMediumLockList);
11568 }
11569 else
11570 {
11571 AssertComRC(rc);
11572 if (pMediumLockList)
11573 {
11574 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11575 AssertComRC(rc);
11576 }
11577 }
11578 }
11579 }
11580 }
11581
11582 /* take media locks again so that the locking state is consistent */
11583 if (fMediaNeedsLocking)
11584 {
11585 Assert(aOnline);
11586 rc = mData->mSession.mLockedMedia.Lock();
11587 AssertComRC(rc);
11588 }
11589
11590 /* commit the hard disk changes */
11591 mMediumAttachments.commit();
11592
11593 if (i_isSessionMachine())
11594 {
11595 /*
11596 * Update the parent machine to point to the new owner.
11597 * This is necessary because the stored parent will point to the
11598 * session machine otherwise and cause crashes or errors later
11599 * when the session machine gets invalid.
11600 */
11601 /** @todo Change the MediumAttachment class to behave like any other
11602 * class in this regard by creating peer MediumAttachment
11603 * objects for session machines and share the data with the peer
11604 * machine.
11605 */
11606 for (MediumAttachmentList::const_iterator
11607 it = mMediumAttachments->begin();
11608 it != mMediumAttachments->end();
11609 ++it)
11610 (*it)->i_updateParentMachine(mPeer);
11611
11612 /* attach new data to the primary machine and reshare it */
11613 mPeer->mMediumAttachments.attach(mMediumAttachments);
11614 }
11615
11616 return;
11617}
11618
11619/**
11620 * Perform deferred deletion of implicitly created diffs.
11621 *
11622 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11623 * changed (not backed up).
11624 *
11625 * @note Locks this object for writing!
11626 */
11627void Machine::i_rollbackMedia()
11628{
11629 AutoCaller autoCaller(this);
11630 AssertComRCReturnVoid(autoCaller.rc());
11631
11632 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11633 LogFlowThisFunc(("Entering rollbackMedia\n"));
11634
11635 HRESULT rc = S_OK;
11636
11637 /* no attach/detach operations -- nothing to do */
11638 if (!mMediumAttachments.isBackedUp())
11639 return;
11640
11641 /* enumerate new attachments */
11642 for (MediumAttachmentList::const_iterator
11643 it = mMediumAttachments->begin();
11644 it != mMediumAttachments->end();
11645 ++it)
11646 {
11647 MediumAttachment *pAttach = *it;
11648 /* Fix up the backrefs for DVD/floppy media. */
11649 if (pAttach->i_getType() != DeviceType_HardDisk)
11650 {
11651 Medium *pMedium = pAttach->i_getMedium();
11652 if (pMedium)
11653 {
11654 rc = pMedium->i_removeBackReference(mData->mUuid);
11655 AssertComRC(rc);
11656 }
11657 }
11658
11659 (*it)->i_rollback();
11660
11661 pAttach = *it;
11662 /* Fix up the backrefs for DVD/floppy media. */
11663 if (pAttach->i_getType() != DeviceType_HardDisk)
11664 {
11665 Medium *pMedium = pAttach->i_getMedium();
11666 if (pMedium)
11667 {
11668 rc = pMedium->i_addBackReference(mData->mUuid);
11669 AssertComRC(rc);
11670 }
11671 }
11672 }
11673
11674 /** @todo convert all this Machine-based voodoo to MediumAttachment
11675 * based rollback logic. */
11676 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11677
11678 return;
11679}
11680
11681/**
11682 * Returns true if the settings file is located in the directory named exactly
11683 * as the machine; this means, among other things, that the machine directory
11684 * should be auto-renamed.
11685 *
11686 * @param aSettingsDir if not NULL, the full machine settings file directory
11687 * name will be assigned there.
11688 *
11689 * @note Doesn't lock anything.
11690 * @note Not thread safe (must be called from this object's lock).
11691 */
11692bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11693{
11694 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11695 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11696 if (aSettingsDir)
11697 *aSettingsDir = strMachineDirName;
11698 strMachineDirName.stripPath(); // vmname
11699 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11700 strConfigFileOnly.stripPath() // vmname.vbox
11701 .stripSuffix(); // vmname
11702 /** @todo hack, make somehow use of ComposeMachineFilename */
11703 if (mUserData->s.fDirectoryIncludesUUID)
11704 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11705
11706 AssertReturn(!strMachineDirName.isEmpty(), false);
11707 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11708
11709 return strMachineDirName == strConfigFileOnly;
11710}
11711
11712/**
11713 * Discards all changes to machine settings.
11714 *
11715 * @param aNotify Whether to notify the direct session about changes or not.
11716 *
11717 * @note Locks objects for writing!
11718 */
11719void Machine::i_rollback(bool aNotify)
11720{
11721 AutoCaller autoCaller(this);
11722 AssertComRCReturn(autoCaller.rc(), (void)0);
11723
11724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11725
11726 if (!mStorageControllers.isNull())
11727 {
11728 if (mStorageControllers.isBackedUp())
11729 {
11730 /* unitialize all new devices (absent in the backed up list). */
11731 StorageControllerList *backedList = mStorageControllers.backedUpData();
11732 for (StorageControllerList::const_iterator
11733 it = mStorageControllers->begin();
11734 it != mStorageControllers->end();
11735 ++it)
11736 {
11737 if ( std::find(backedList->begin(), backedList->end(), *it)
11738 == backedList->end()
11739 )
11740 {
11741 (*it)->uninit();
11742 }
11743 }
11744
11745 /* restore the list */
11746 mStorageControllers.rollback();
11747 }
11748
11749 /* rollback any changes to devices after restoring the list */
11750 if (mData->flModifications & IsModified_Storage)
11751 {
11752 for (StorageControllerList::const_iterator
11753 it = mStorageControllers->begin();
11754 it != mStorageControllers->end();
11755 ++it)
11756 {
11757 (*it)->i_rollback();
11758 }
11759 }
11760 }
11761
11762 if (!mUSBControllers.isNull())
11763 {
11764 if (mUSBControllers.isBackedUp())
11765 {
11766 /* unitialize all new devices (absent in the backed up list). */
11767 USBControllerList *backedList = mUSBControllers.backedUpData();
11768 for (USBControllerList::const_iterator
11769 it = mUSBControllers->begin();
11770 it != mUSBControllers->end();
11771 ++it)
11772 {
11773 if ( std::find(backedList->begin(), backedList->end(), *it)
11774 == backedList->end()
11775 )
11776 {
11777 (*it)->uninit();
11778 }
11779 }
11780
11781 /* restore the list */
11782 mUSBControllers.rollback();
11783 }
11784
11785 /* rollback any changes to devices after restoring the list */
11786 if (mData->flModifications & IsModified_USB)
11787 {
11788 for (USBControllerList::const_iterator
11789 it = mUSBControllers->begin();
11790 it != mUSBControllers->end();
11791 ++it)
11792 {
11793 (*it)->i_rollback();
11794 }
11795 }
11796 }
11797
11798 mUserData.rollback();
11799
11800 mHWData.rollback();
11801
11802 if (mData->flModifications & IsModified_Storage)
11803 i_rollbackMedia();
11804
11805 if (mBIOSSettings)
11806 mBIOSSettings->i_rollback();
11807
11808 if (mTrustedPlatformModule)
11809 mTrustedPlatformModule->i_rollback();
11810
11811 if (mNvramStore)
11812 mNvramStore->i_rollback();
11813
11814 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11815 mRecordingSettings->i_rollback();
11816
11817 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11818 mGraphicsAdapter->i_rollback();
11819
11820 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11821 mVRDEServer->i_rollback();
11822
11823 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11824 mAudioAdapter->i_rollback();
11825
11826 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11827 mUSBDeviceFilters->i_rollback();
11828
11829 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11830 mBandwidthControl->i_rollback();
11831
11832 if (!mHWData.isNull())
11833 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11834 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11835 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11836 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11837
11838 if (mData->flModifications & IsModified_NetworkAdapters)
11839 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11840 if ( mNetworkAdapters[slot]
11841 && mNetworkAdapters[slot]->i_isModified())
11842 {
11843 mNetworkAdapters[slot]->i_rollback();
11844 networkAdapters[slot] = mNetworkAdapters[slot];
11845 }
11846
11847 if (mData->flModifications & IsModified_SerialPorts)
11848 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11849 if ( mSerialPorts[slot]
11850 && mSerialPorts[slot]->i_isModified())
11851 {
11852 mSerialPorts[slot]->i_rollback();
11853 serialPorts[slot] = mSerialPorts[slot];
11854 }
11855
11856 if (mData->flModifications & IsModified_ParallelPorts)
11857 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11858 if ( mParallelPorts[slot]
11859 && mParallelPorts[slot]->i_isModified())
11860 {
11861 mParallelPorts[slot]->i_rollback();
11862 parallelPorts[slot] = mParallelPorts[slot];
11863 }
11864
11865 if (aNotify)
11866 {
11867 /* inform the direct session about changes */
11868
11869 ComObjPtr<Machine> that = this;
11870 uint32_t flModifications = mData->flModifications;
11871 alock.release();
11872
11873 if (flModifications & IsModified_SharedFolders)
11874 that->i_onSharedFolderChange();
11875
11876 if (flModifications & IsModified_VRDEServer)
11877 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11878 if (flModifications & IsModified_USB)
11879 that->i_onUSBControllerChange();
11880
11881 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11882 if (networkAdapters[slot])
11883 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11884 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11885 if (serialPorts[slot])
11886 that->i_onSerialPortChange(serialPorts[slot]);
11887 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11888 if (parallelPorts[slot])
11889 that->i_onParallelPortChange(parallelPorts[slot]);
11890
11891 if (flModifications & IsModified_Storage)
11892 {
11893 for (StorageControllerList::const_iterator
11894 it = mStorageControllers->begin();
11895 it != mStorageControllers->end();
11896 ++it)
11897 {
11898 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11899 }
11900 }
11901
11902
11903#if 0
11904 if (flModifications & IsModified_BandwidthControl)
11905 that->onBandwidthControlChange();
11906#endif
11907 }
11908}
11909
11910/**
11911 * Commits all the changes to machine settings.
11912 *
11913 * Note that this operation is supposed to never fail.
11914 *
11915 * @note Locks this object and children for writing.
11916 */
11917void Machine::i_commit()
11918{
11919 AutoCaller autoCaller(this);
11920 AssertComRCReturnVoid(autoCaller.rc());
11921
11922 AutoCaller peerCaller(mPeer);
11923 AssertComRCReturnVoid(peerCaller.rc());
11924
11925 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11926
11927 /*
11928 * use safe commit to ensure Snapshot machines (that share mUserData)
11929 * will still refer to a valid memory location
11930 */
11931 mUserData.commitCopy();
11932
11933 mHWData.commit();
11934
11935 if (mMediumAttachments.isBackedUp())
11936 i_commitMedia(Global::IsOnline(mData->mMachineState));
11937
11938 mBIOSSettings->i_commit();
11939 mTrustedPlatformModule->i_commit();
11940 mNvramStore->i_commit();
11941 mRecordingSettings->i_commit();
11942 mGraphicsAdapter->i_commit();
11943 mVRDEServer->i_commit();
11944 mAudioAdapter->i_commit();
11945 mUSBDeviceFilters->i_commit();
11946 mBandwidthControl->i_commit();
11947
11948 /* Since mNetworkAdapters is a list which might have been changed (resized)
11949 * without using the Backupable<> template we need to handle the copying
11950 * of the list entries manually, including the creation of peers for the
11951 * new objects. */
11952 bool commitNetworkAdapters = false;
11953 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11954 if (mPeer)
11955 {
11956 /* commit everything, even the ones which will go away */
11957 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11958 mNetworkAdapters[slot]->i_commit();
11959 /* copy over the new entries, creating a peer and uninit the original */
11960 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11961 for (size_t slot = 0; slot < newSize; slot++)
11962 {
11963 /* look if this adapter has a peer device */
11964 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11965 if (!peer)
11966 {
11967 /* no peer means the adapter is a newly created one;
11968 * create a peer owning data this data share it with */
11969 peer.createObject();
11970 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11971 }
11972 mPeer->mNetworkAdapters[slot] = peer;
11973 }
11974 /* uninit any no longer needed network adapters */
11975 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11976 mNetworkAdapters[slot]->uninit();
11977 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11978 {
11979 if (mPeer->mNetworkAdapters[slot])
11980 mPeer->mNetworkAdapters[slot]->uninit();
11981 }
11982 /* Keep the original network adapter count until this point, so that
11983 * discarding a chipset type change will not lose settings. */
11984 mNetworkAdapters.resize(newSize);
11985 mPeer->mNetworkAdapters.resize(newSize);
11986 }
11987 else
11988 {
11989 /* we have no peer (our parent is the newly created machine);
11990 * just commit changes to the network adapters */
11991 commitNetworkAdapters = true;
11992 }
11993 if (commitNetworkAdapters)
11994 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11995 mNetworkAdapters[slot]->i_commit();
11996
11997 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11998 mSerialPorts[slot]->i_commit();
11999 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12000 mParallelPorts[slot]->i_commit();
12001
12002 bool commitStorageControllers = false;
12003
12004 if (mStorageControllers.isBackedUp())
12005 {
12006 mStorageControllers.commit();
12007
12008 if (mPeer)
12009 {
12010 /* Commit all changes to new controllers (this will reshare data with
12011 * peers for those who have peers) */
12012 StorageControllerList *newList = new StorageControllerList();
12013 for (StorageControllerList::const_iterator
12014 it = mStorageControllers->begin();
12015 it != mStorageControllers->end();
12016 ++it)
12017 {
12018 (*it)->i_commit();
12019
12020 /* look if this controller has a peer device */
12021 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12022 if (!peer)
12023 {
12024 /* no peer means the device is a newly created one;
12025 * create a peer owning data this device share it with */
12026 peer.createObject();
12027 peer->init(mPeer, *it, true /* aReshare */);
12028 }
12029 else
12030 {
12031 /* remove peer from the old list */
12032 mPeer->mStorageControllers->remove(peer);
12033 }
12034 /* and add it to the new list */
12035 newList->push_back(peer);
12036 }
12037
12038 /* uninit old peer's controllers that are left */
12039 for (StorageControllerList::const_iterator
12040 it = mPeer->mStorageControllers->begin();
12041 it != mPeer->mStorageControllers->end();
12042 ++it)
12043 {
12044 (*it)->uninit();
12045 }
12046
12047 /* attach new list of controllers to our peer */
12048 mPeer->mStorageControllers.attach(newList);
12049 }
12050 else
12051 {
12052 /* we have no peer (our parent is the newly created machine);
12053 * just commit changes to devices */
12054 commitStorageControllers = true;
12055 }
12056 }
12057 else
12058 {
12059 /* the list of controllers itself is not changed,
12060 * just commit changes to controllers themselves */
12061 commitStorageControllers = true;
12062 }
12063
12064 if (commitStorageControllers)
12065 {
12066 for (StorageControllerList::const_iterator
12067 it = mStorageControllers->begin();
12068 it != mStorageControllers->end();
12069 ++it)
12070 {
12071 (*it)->i_commit();
12072 }
12073 }
12074
12075 bool commitUSBControllers = false;
12076
12077 if (mUSBControllers.isBackedUp())
12078 {
12079 mUSBControllers.commit();
12080
12081 if (mPeer)
12082 {
12083 /* Commit all changes to new controllers (this will reshare data with
12084 * peers for those who have peers) */
12085 USBControllerList *newList = new USBControllerList();
12086 for (USBControllerList::const_iterator
12087 it = mUSBControllers->begin();
12088 it != mUSBControllers->end();
12089 ++it)
12090 {
12091 (*it)->i_commit();
12092
12093 /* look if this controller has a peer device */
12094 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12095 if (!peer)
12096 {
12097 /* no peer means the device is a newly created one;
12098 * create a peer owning data this device share it with */
12099 peer.createObject();
12100 peer->init(mPeer, *it, true /* aReshare */);
12101 }
12102 else
12103 {
12104 /* remove peer from the old list */
12105 mPeer->mUSBControllers->remove(peer);
12106 }
12107 /* and add it to the new list */
12108 newList->push_back(peer);
12109 }
12110
12111 /* uninit old peer's controllers that are left */
12112 for (USBControllerList::const_iterator
12113 it = mPeer->mUSBControllers->begin();
12114 it != mPeer->mUSBControllers->end();
12115 ++it)
12116 {
12117 (*it)->uninit();
12118 }
12119
12120 /* attach new list of controllers to our peer */
12121 mPeer->mUSBControllers.attach(newList);
12122 }
12123 else
12124 {
12125 /* we have no peer (our parent is the newly created machine);
12126 * just commit changes to devices */
12127 commitUSBControllers = true;
12128 }
12129 }
12130 else
12131 {
12132 /* the list of controllers itself is not changed,
12133 * just commit changes to controllers themselves */
12134 commitUSBControllers = true;
12135 }
12136
12137 if (commitUSBControllers)
12138 {
12139 for (USBControllerList::const_iterator
12140 it = mUSBControllers->begin();
12141 it != mUSBControllers->end();
12142 ++it)
12143 {
12144 (*it)->i_commit();
12145 }
12146 }
12147
12148 if (i_isSessionMachine())
12149 {
12150 /* attach new data to the primary machine and reshare it */
12151 mPeer->mUserData.attach(mUserData);
12152 mPeer->mHWData.attach(mHWData);
12153 /* mmMediumAttachments is reshared by fixupMedia */
12154 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12155 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12156 }
12157}
12158
12159/**
12160 * Copies all the hardware data from the given machine.
12161 *
12162 * Currently, only called when the VM is being restored from a snapshot. In
12163 * particular, this implies that the VM is not running during this method's
12164 * call.
12165 *
12166 * @note This method must be called from under this object's lock.
12167 *
12168 * @note This method doesn't call #i_commit(), so all data remains backed up and
12169 * unsaved.
12170 */
12171void Machine::i_copyFrom(Machine *aThat)
12172{
12173 AssertReturnVoid(!i_isSnapshotMachine());
12174 AssertReturnVoid(aThat->i_isSnapshotMachine());
12175
12176 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12177
12178 mHWData.assignCopy(aThat->mHWData);
12179
12180 // create copies of all shared folders (mHWData after attaching a copy
12181 // contains just references to original objects)
12182 for (HWData::SharedFolderList::iterator
12183 it = mHWData->mSharedFolders.begin();
12184 it != mHWData->mSharedFolders.end();
12185 ++it)
12186 {
12187 ComObjPtr<SharedFolder> folder;
12188 folder.createObject();
12189 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12190 AssertComRC(rc);
12191 *it = folder;
12192 }
12193
12194 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12195 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12196 mNvramStore->i_copyFrom(aThat->mNvramStore);
12197 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12198 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12199 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12200 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12201 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12202 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12203
12204 /* create private copies of all controllers */
12205 mStorageControllers.backup();
12206 mStorageControllers->clear();
12207 for (StorageControllerList::const_iterator
12208 it = aThat->mStorageControllers->begin();
12209 it != aThat->mStorageControllers->end();
12210 ++it)
12211 {
12212 ComObjPtr<StorageController> ctrl;
12213 ctrl.createObject();
12214 ctrl->initCopy(this, *it);
12215 mStorageControllers->push_back(ctrl);
12216 }
12217
12218 /* create private copies of all USB controllers */
12219 mUSBControllers.backup();
12220 mUSBControllers->clear();
12221 for (USBControllerList::const_iterator
12222 it = aThat->mUSBControllers->begin();
12223 it != aThat->mUSBControllers->end();
12224 ++it)
12225 {
12226 ComObjPtr<USBController> ctrl;
12227 ctrl.createObject();
12228 ctrl->initCopy(this, *it);
12229 mUSBControllers->push_back(ctrl);
12230 }
12231
12232 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12233 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12234 {
12235 if (mNetworkAdapters[slot].isNotNull())
12236 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12237 else
12238 {
12239 unconst(mNetworkAdapters[slot]).createObject();
12240 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12241 }
12242 }
12243 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12244 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12245 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12246 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12247}
12248
12249/**
12250 * Returns whether the given storage controller is hotplug capable.
12251 *
12252 * @returns true if the controller supports hotplugging
12253 * false otherwise.
12254 * @param enmCtrlType The controller type to check for.
12255 */
12256bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12257{
12258 ComPtr<ISystemProperties> systemProperties;
12259 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12260 if (FAILED(rc))
12261 return false;
12262
12263 BOOL aHotplugCapable = FALSE;
12264 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12265
12266 return RT_BOOL(aHotplugCapable);
12267}
12268
12269#ifdef VBOX_WITH_RESOURCE_USAGE_API
12270
12271void Machine::i_getDiskList(MediaList &list)
12272{
12273 for (MediumAttachmentList::const_iterator
12274 it = mMediumAttachments->begin();
12275 it != mMediumAttachments->end();
12276 ++it)
12277 {
12278 MediumAttachment *pAttach = *it;
12279 /* just in case */
12280 AssertContinue(pAttach);
12281
12282 AutoCaller localAutoCallerA(pAttach);
12283 if (FAILED(localAutoCallerA.rc())) continue;
12284
12285 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12286
12287 if (pAttach->i_getType() == DeviceType_HardDisk)
12288 list.push_back(pAttach->i_getMedium());
12289 }
12290}
12291
12292void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12293{
12294 AssertReturnVoid(isWriteLockOnCurrentThread());
12295 AssertPtrReturnVoid(aCollector);
12296
12297 pm::CollectorHAL *hal = aCollector->getHAL();
12298 /* Create sub metrics */
12299 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12300 "Percentage of processor time spent in user mode by the VM process.");
12301 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12302 "Percentage of processor time spent in kernel mode by the VM process.");
12303 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12304 "Size of resident portion of VM process in memory.");
12305 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12306 "Actual size of all VM disks combined.");
12307 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12308 "Network receive rate.");
12309 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12310 "Network transmit rate.");
12311 /* Create and register base metrics */
12312 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12313 cpuLoadUser, cpuLoadKernel);
12314 aCollector->registerBaseMetric(cpuLoad);
12315 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12316 ramUsageUsed);
12317 aCollector->registerBaseMetric(ramUsage);
12318 MediaList disks;
12319 i_getDiskList(disks);
12320 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12321 diskUsageUsed);
12322 aCollector->registerBaseMetric(diskUsage);
12323
12324 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12325 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12326 new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12328 new pm::AggregateMin()));
12329 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12330 new pm::AggregateMax()));
12331 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12332 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12333 new pm::AggregateAvg()));
12334 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12335 new pm::AggregateMin()));
12336 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12337 new pm::AggregateMax()));
12338
12339 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12340 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12341 new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12343 new pm::AggregateMin()));
12344 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12345 new pm::AggregateMax()));
12346
12347 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12348 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12349 new pm::AggregateAvg()));
12350 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12351 new pm::AggregateMin()));
12352 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12353 new pm::AggregateMax()));
12354
12355
12356 /* Guest metrics collector */
12357 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12358 aCollector->registerGuest(mCollectorGuest);
12359 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12360
12361 /* Create sub metrics */
12362 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12363 "Percentage of processor time spent in user mode as seen by the guest.");
12364 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12365 "Percentage of processor time spent in kernel mode as seen by the guest.");
12366 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12367 "Percentage of processor time spent idling as seen by the guest.");
12368
12369 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12370 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12371 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12372 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12373 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12374 pm::SubMetric *guestMemCache = new pm::SubMetric(
12375 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12376
12377 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12378 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12379
12380 /* Create and register base metrics */
12381 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12382 machineNetRx, machineNetTx);
12383 aCollector->registerBaseMetric(machineNetRate);
12384
12385 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12386 guestLoadUser, guestLoadKernel, guestLoadIdle);
12387 aCollector->registerBaseMetric(guestCpuLoad);
12388
12389 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12390 guestMemTotal, guestMemFree,
12391 guestMemBalloon, guestMemShared,
12392 guestMemCache, guestPagedTotal);
12393 aCollector->registerBaseMetric(guestCpuMem);
12394
12395 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12396 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12397 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12398 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12399
12400 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12401 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12402 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12403 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12404
12405 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12406 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12407 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12408 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12409
12410 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12411 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12412 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12413 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12414
12415 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12416 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12417 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12418 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12419
12420 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12421 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12422 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12423 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12424
12425 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12426 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12427 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12428 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12429
12430 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12431 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12432 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12433 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12434
12435 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12436 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12437 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12438 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12439
12440 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12441 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12442 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12443 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12444
12445 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12446 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12447 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12448 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12449}
12450
12451void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12452{
12453 AssertReturnVoid(isWriteLockOnCurrentThread());
12454
12455 if (aCollector)
12456 {
12457 aCollector->unregisterMetricsFor(aMachine);
12458 aCollector->unregisterBaseMetricsFor(aMachine);
12459 }
12460}
12461
12462#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12463
12464
12465////////////////////////////////////////////////////////////////////////////////
12466
12467DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12468
12469HRESULT SessionMachine::FinalConstruct()
12470{
12471 LogFlowThisFunc(("\n"));
12472
12473 mClientToken = NULL;
12474
12475 return BaseFinalConstruct();
12476}
12477
12478void SessionMachine::FinalRelease()
12479{
12480 LogFlowThisFunc(("\n"));
12481
12482 Assert(!mClientToken);
12483 /* paranoia, should not hang around any more */
12484 if (mClientToken)
12485 {
12486 delete mClientToken;
12487 mClientToken = NULL;
12488 }
12489
12490 uninit(Uninit::Unexpected);
12491
12492 BaseFinalRelease();
12493}
12494
12495/**
12496 * @note Must be called only by Machine::LockMachine() from its own write lock.
12497 */
12498HRESULT SessionMachine::init(Machine *aMachine)
12499{
12500 LogFlowThisFuncEnter();
12501 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12502
12503 AssertReturn(aMachine, E_INVALIDARG);
12504
12505 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12506
12507 /* Enclose the state transition NotReady->InInit->Ready */
12508 AutoInitSpan autoInitSpan(this);
12509 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12510
12511 HRESULT rc = S_OK;
12512
12513 RT_ZERO(mAuthLibCtx);
12514
12515 /* create the machine client token */
12516 try
12517 {
12518 mClientToken = new ClientToken(aMachine, this);
12519 if (!mClientToken->isReady())
12520 {
12521 delete mClientToken;
12522 mClientToken = NULL;
12523 rc = E_FAIL;
12524 }
12525 }
12526 catch (std::bad_alloc &)
12527 {
12528 rc = E_OUTOFMEMORY;
12529 }
12530 if (FAILED(rc))
12531 return rc;
12532
12533 /* memorize the peer Machine */
12534 unconst(mPeer) = aMachine;
12535 /* share the parent pointer */
12536 unconst(mParent) = aMachine->mParent;
12537
12538 /* take the pointers to data to share */
12539 mData.share(aMachine->mData);
12540 mSSData.share(aMachine->mSSData);
12541
12542 mUserData.share(aMachine->mUserData);
12543 mHWData.share(aMachine->mHWData);
12544 mMediumAttachments.share(aMachine->mMediumAttachments);
12545
12546 mStorageControllers.allocate();
12547 for (StorageControllerList::const_iterator
12548 it = aMachine->mStorageControllers->begin();
12549 it != aMachine->mStorageControllers->end();
12550 ++it)
12551 {
12552 ComObjPtr<StorageController> ctl;
12553 ctl.createObject();
12554 ctl->init(this, *it);
12555 mStorageControllers->push_back(ctl);
12556 }
12557
12558 mUSBControllers.allocate();
12559 for (USBControllerList::const_iterator
12560 it = aMachine->mUSBControllers->begin();
12561 it != aMachine->mUSBControllers->end();
12562 ++it)
12563 {
12564 ComObjPtr<USBController> ctl;
12565 ctl.createObject();
12566 ctl->init(this, *it);
12567 mUSBControllers->push_back(ctl);
12568 }
12569
12570 unconst(mBIOSSettings).createObject();
12571 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12572
12573 unconst(mTrustedPlatformModule).createObject();
12574 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12575
12576 unconst(mNvramStore).createObject();
12577 mNvramStore->init(this, aMachine->mNvramStore);
12578
12579 unconst(mRecordingSettings).createObject();
12580 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12581 /* create another GraphicsAdapter object that will be mutable */
12582 unconst(mGraphicsAdapter).createObject();
12583 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12584 /* create another VRDEServer object that will be mutable */
12585 unconst(mVRDEServer).createObject();
12586 mVRDEServer->init(this, aMachine->mVRDEServer);
12587 /* create another audio adapter object that will be mutable */
12588 unconst(mAudioAdapter).createObject();
12589 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12590 /* create a list of serial ports that will be mutable */
12591 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12592 {
12593 unconst(mSerialPorts[slot]).createObject();
12594 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12595 }
12596 /* create a list of parallel ports that will be mutable */
12597 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12598 {
12599 unconst(mParallelPorts[slot]).createObject();
12600 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12601 }
12602
12603 /* create another USB device filters object that will be mutable */
12604 unconst(mUSBDeviceFilters).createObject();
12605 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12606
12607 /* create a list of network adapters that will be mutable */
12608 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12609 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12610 {
12611 unconst(mNetworkAdapters[slot]).createObject();
12612 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12613 }
12614
12615 /* create another bandwidth control object that will be mutable */
12616 unconst(mBandwidthControl).createObject();
12617 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12618
12619 /* default is to delete saved state on Saved -> PoweredOff transition */
12620 mRemoveSavedState = true;
12621
12622 /* Confirm a successful initialization when it's the case */
12623 autoInitSpan.setSucceeded();
12624
12625 miNATNetworksStarted = 0;
12626
12627 LogFlowThisFuncLeave();
12628 return rc;
12629}
12630
12631/**
12632 * Uninitializes this session object. If the reason is other than
12633 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12634 * or the client watcher code.
12635 *
12636 * @param aReason uninitialization reason
12637 *
12638 * @note Locks mParent + this object for writing.
12639 */
12640void SessionMachine::uninit(Uninit::Reason aReason)
12641{
12642 LogFlowThisFuncEnter();
12643 LogFlowThisFunc(("reason=%d\n", aReason));
12644
12645 /*
12646 * Strongly reference ourselves to prevent this object deletion after
12647 * mData->mSession.mMachine.setNull() below (which can release the last
12648 * reference and call the destructor). Important: this must be done before
12649 * accessing any members (and before AutoUninitSpan that does it as well).
12650 * This self reference will be released as the very last step on return.
12651 */
12652 ComObjPtr<SessionMachine> selfRef;
12653 if (aReason != Uninit::Unexpected)
12654 selfRef = this;
12655
12656 /* Enclose the state transition Ready->InUninit->NotReady */
12657 AutoUninitSpan autoUninitSpan(this);
12658 if (autoUninitSpan.uninitDone())
12659 {
12660 LogFlowThisFunc(("Already uninitialized\n"));
12661 LogFlowThisFuncLeave();
12662 return;
12663 }
12664
12665 if (autoUninitSpan.initFailed())
12666 {
12667 /* We've been called by init() because it's failed. It's not really
12668 * necessary (nor it's safe) to perform the regular uninit sequence
12669 * below, the following is enough.
12670 */
12671 LogFlowThisFunc(("Initialization failed.\n"));
12672 /* destroy the machine client token */
12673 if (mClientToken)
12674 {
12675 delete mClientToken;
12676 mClientToken = NULL;
12677 }
12678 uninitDataAndChildObjects();
12679 mData.free();
12680 unconst(mParent) = NULL;
12681 unconst(mPeer) = NULL;
12682 LogFlowThisFuncLeave();
12683 return;
12684 }
12685
12686 MachineState_T lastState;
12687 {
12688 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12689 lastState = mData->mMachineState;
12690 }
12691 NOREF(lastState);
12692
12693#ifdef VBOX_WITH_USB
12694 // release all captured USB devices, but do this before requesting the locks below
12695 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12696 {
12697 /* Console::captureUSBDevices() is called in the VM process only after
12698 * setting the machine state to Starting or Restoring.
12699 * Console::detachAllUSBDevices() will be called upon successful
12700 * termination. So, we need to release USB devices only if there was
12701 * an abnormal termination of a running VM.
12702 *
12703 * This is identical to SessionMachine::DetachAllUSBDevices except
12704 * for the aAbnormal argument. */
12705 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12706 AssertComRC(rc);
12707 NOREF(rc);
12708
12709 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12710 if (service)
12711 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12712 }
12713#endif /* VBOX_WITH_USB */
12714
12715 // we need to lock this object in uninit() because the lock is shared
12716 // with mPeer (as well as data we modify below). mParent lock is needed
12717 // by several calls to it.
12718 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12719
12720#ifdef VBOX_WITH_RESOURCE_USAGE_API
12721 /*
12722 * It is safe to call Machine::i_unregisterMetrics() here because
12723 * PerformanceCollector::samplerCallback no longer accesses guest methods
12724 * holding the lock.
12725 */
12726 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12727 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12728 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12729 if (mCollectorGuest)
12730 {
12731 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12732 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12733 mCollectorGuest = NULL;
12734 }
12735#endif
12736
12737 if (aReason == Uninit::Abnormal)
12738 {
12739 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12740
12741 /*
12742 * Move the VM to the 'Aborted' machine state unless we are restoring a
12743 * VM that was in the 'Saved' machine state. In that case, if the VM
12744 * fails before reaching either the 'Restoring' machine state or the
12745 * 'Running' machine state then we set the machine state to
12746 * 'AbortedSaved' in order to preserve the saved state file so that the
12747 * VM can be restored in the future.
12748 */
12749 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12750 i_setMachineState(MachineState_AbortedSaved);
12751 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12752 i_setMachineState(MachineState_Aborted);
12753 }
12754
12755 // any machine settings modified?
12756 if (mData->flModifications)
12757 {
12758 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12759 i_rollback(false /* aNotify */);
12760 }
12761
12762 mData->mSession.mPID = NIL_RTPROCESS;
12763
12764 if (aReason == Uninit::Unexpected)
12765 {
12766 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12767 * client watcher thread to update the set of machines that have open
12768 * sessions. */
12769 mParent->i_updateClientWatcher();
12770 }
12771
12772 /* uninitialize all remote controls */
12773 if (mData->mSession.mRemoteControls.size())
12774 {
12775 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12776 mData->mSession.mRemoteControls.size()));
12777
12778 /* Always restart a the beginning, since the iterator is invalidated
12779 * by using erase(). */
12780 for (Data::Session::RemoteControlList::iterator
12781 it = mData->mSession.mRemoteControls.begin();
12782 it != mData->mSession.mRemoteControls.end();
12783 it = mData->mSession.mRemoteControls.begin())
12784 {
12785 ComPtr<IInternalSessionControl> pControl = *it;
12786 mData->mSession.mRemoteControls.erase(it);
12787 multilock.release();
12788 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12789 HRESULT rc = pControl->Uninitialize();
12790 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12791 if (FAILED(rc))
12792 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12793 multilock.acquire();
12794 }
12795 mData->mSession.mRemoteControls.clear();
12796 }
12797
12798 /* Remove all references to the NAT network service. The service will stop
12799 * if all references (also from other VMs) are removed. */
12800 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12801 {
12802 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12803 {
12804 BOOL enabled;
12805 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12806 if ( FAILED(hrc)
12807 || !enabled)
12808 continue;
12809
12810 NetworkAttachmentType_T type;
12811 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12812 if ( SUCCEEDED(hrc)
12813 && type == NetworkAttachmentType_NATNetwork)
12814 {
12815 Bstr name;
12816 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12817 if (SUCCEEDED(hrc))
12818 {
12819 multilock.release();
12820 Utf8Str strName(name);
12821 LogRel(("VM '%s' stops using NAT network '%s'\n",
12822 mUserData->s.strName.c_str(), strName.c_str()));
12823 mParent->i_natNetworkRefDec(strName);
12824 multilock.acquire();
12825 }
12826 }
12827 }
12828 }
12829
12830 /*
12831 * An expected uninitialization can come only from #i_checkForDeath().
12832 * Otherwise it means that something's gone really wrong (for example,
12833 * the Session implementation has released the VirtualBox reference
12834 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12835 * etc). However, it's also possible, that the client releases the IPC
12836 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12837 * but the VirtualBox release event comes first to the server process.
12838 * This case is practically possible, so we should not assert on an
12839 * unexpected uninit, just log a warning.
12840 */
12841
12842 if (aReason == Uninit::Unexpected)
12843 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12844
12845 if (aReason != Uninit::Normal)
12846 {
12847 mData->mSession.mDirectControl.setNull();
12848 }
12849 else
12850 {
12851 /* this must be null here (see #OnSessionEnd()) */
12852 Assert(mData->mSession.mDirectControl.isNull());
12853 Assert(mData->mSession.mState == SessionState_Unlocking);
12854 Assert(!mData->mSession.mProgress.isNull());
12855 }
12856 if (mData->mSession.mProgress)
12857 {
12858 if (aReason == Uninit::Normal)
12859 mData->mSession.mProgress->i_notifyComplete(S_OK);
12860 else
12861 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12862 COM_IIDOF(ISession),
12863 getComponentName(),
12864 tr("The VM session was aborted"));
12865 mData->mSession.mProgress.setNull();
12866 }
12867
12868 if (mConsoleTaskData.mProgress)
12869 {
12870 Assert(aReason == Uninit::Abnormal);
12871 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12872 COM_IIDOF(ISession),
12873 getComponentName(),
12874 tr("The VM session was aborted"));
12875 mConsoleTaskData.mProgress.setNull();
12876 }
12877
12878 /* remove the association between the peer machine and this session machine */
12879 Assert( (SessionMachine*)mData->mSession.mMachine == this
12880 || aReason == Uninit::Unexpected);
12881
12882 /* reset the rest of session data */
12883 mData->mSession.mLockType = LockType_Null;
12884 mData->mSession.mMachine.setNull();
12885 mData->mSession.mState = SessionState_Unlocked;
12886 mData->mSession.mName.setNull();
12887
12888 /* destroy the machine client token before leaving the exclusive lock */
12889 if (mClientToken)
12890 {
12891 delete mClientToken;
12892 mClientToken = NULL;
12893 }
12894
12895 /* fire an event */
12896 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12897
12898 uninitDataAndChildObjects();
12899
12900 /* free the essential data structure last */
12901 mData.free();
12902
12903 /* release the exclusive lock before setting the below two to NULL */
12904 multilock.release();
12905
12906 unconst(mParent) = NULL;
12907 unconst(mPeer) = NULL;
12908
12909 AuthLibUnload(&mAuthLibCtx);
12910
12911 LogFlowThisFuncLeave();
12912}
12913
12914// util::Lockable interface
12915////////////////////////////////////////////////////////////////////////////////
12916
12917/**
12918 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12919 * with the primary Machine instance (mPeer).
12920 */
12921RWLockHandle *SessionMachine::lockHandle() const
12922{
12923 AssertReturn(mPeer != NULL, NULL);
12924 return mPeer->lockHandle();
12925}
12926
12927// IInternalMachineControl methods
12928////////////////////////////////////////////////////////////////////////////////
12929
12930/**
12931 * Passes collected guest statistics to performance collector object
12932 */
12933HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12934 ULONG aCpuKernel, ULONG aCpuIdle,
12935 ULONG aMemTotal, ULONG aMemFree,
12936 ULONG aMemBalloon, ULONG aMemShared,
12937 ULONG aMemCache, ULONG aPageTotal,
12938 ULONG aAllocVMM, ULONG aFreeVMM,
12939 ULONG aBalloonedVMM, ULONG aSharedVMM,
12940 ULONG aVmNetRx, ULONG aVmNetTx)
12941{
12942#ifdef VBOX_WITH_RESOURCE_USAGE_API
12943 if (mCollectorGuest)
12944 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12945 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12946 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12947 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12948
12949 return S_OK;
12950#else
12951 NOREF(aValidStats);
12952 NOREF(aCpuUser);
12953 NOREF(aCpuKernel);
12954 NOREF(aCpuIdle);
12955 NOREF(aMemTotal);
12956 NOREF(aMemFree);
12957 NOREF(aMemBalloon);
12958 NOREF(aMemShared);
12959 NOREF(aMemCache);
12960 NOREF(aPageTotal);
12961 NOREF(aAllocVMM);
12962 NOREF(aFreeVMM);
12963 NOREF(aBalloonedVMM);
12964 NOREF(aSharedVMM);
12965 NOREF(aVmNetRx);
12966 NOREF(aVmNetTx);
12967 return E_NOTIMPL;
12968#endif
12969}
12970
12971////////////////////////////////////////////////////////////////////////////////
12972//
12973// SessionMachine task records
12974//
12975////////////////////////////////////////////////////////////////////////////////
12976
12977/**
12978 * Task record for saving the machine state.
12979 */
12980class SessionMachine::SaveStateTask
12981 : public Machine::Task
12982{
12983public:
12984 SaveStateTask(SessionMachine *m,
12985 Progress *p,
12986 const Utf8Str &t,
12987 Reason_T enmReason,
12988 const Utf8Str &strStateFilePath)
12989 : Task(m, p, t),
12990 m_enmReason(enmReason),
12991 m_strStateFilePath(strStateFilePath)
12992 {}
12993
12994private:
12995 void handler()
12996 {
12997 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12998 }
12999
13000 Reason_T m_enmReason;
13001 Utf8Str m_strStateFilePath;
13002
13003 friend class SessionMachine;
13004};
13005
13006/**
13007 * Task thread implementation for SessionMachine::SaveState(), called from
13008 * SessionMachine::taskHandler().
13009 *
13010 * @note Locks this object for writing.
13011 *
13012 * @param task
13013 * @return
13014 */
13015void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13016{
13017 LogFlowThisFuncEnter();
13018
13019 AutoCaller autoCaller(this);
13020 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13021 if (FAILED(autoCaller.rc()))
13022 {
13023 /* we might have been uninitialized because the session was accidentally
13024 * closed by the client, so don't assert */
13025 HRESULT rc = setError(E_FAIL,
13026 tr("The session has been accidentally closed"));
13027 task.m_pProgress->i_notifyComplete(rc);
13028 LogFlowThisFuncLeave();
13029 return;
13030 }
13031
13032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13033
13034 HRESULT rc = S_OK;
13035
13036 try
13037 {
13038 ComPtr<IInternalSessionControl> directControl;
13039 if (mData->mSession.mLockType == LockType_VM)
13040 directControl = mData->mSession.mDirectControl;
13041 if (directControl.isNull())
13042 throw setError(VBOX_E_INVALID_VM_STATE,
13043 tr("Trying to save state without a running VM"));
13044 alock.release();
13045 BOOL fSuspendedBySave;
13046 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13047 Assert(!fSuspendedBySave);
13048 alock.acquire();
13049
13050 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13051 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13052 throw E_FAIL);
13053
13054 if (SUCCEEDED(rc))
13055 {
13056 mSSData->strStateFilePath = task.m_strStateFilePath;
13057
13058 /* save all VM settings */
13059 rc = i_saveSettings(NULL, alock);
13060 // no need to check whether VirtualBox.xml needs saving also since
13061 // we can't have a name change pending at this point
13062 }
13063 else
13064 {
13065 // On failure, set the state to the state we had at the beginning.
13066 i_setMachineState(task.m_machineStateBackup);
13067 i_updateMachineStateOnClient();
13068
13069 // Delete the saved state file (might have been already created).
13070 // No need to check whether this is shared with a snapshot here
13071 // because we certainly created a fresh saved state file here.
13072 RTFileDelete(task.m_strStateFilePath.c_str());
13073 }
13074 }
13075 catch (HRESULT aRC) { rc = aRC; }
13076
13077 task.m_pProgress->i_notifyComplete(rc);
13078
13079 LogFlowThisFuncLeave();
13080}
13081
13082/**
13083 * @note Locks this object for writing.
13084 */
13085HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13086{
13087 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13088}
13089
13090HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13091{
13092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13093
13094 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13095 if (FAILED(rc)) return rc;
13096
13097 if ( mData->mMachineState != MachineState_Running
13098 && mData->mMachineState != MachineState_Paused
13099 )
13100 return setError(VBOX_E_INVALID_VM_STATE,
13101 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13102 Global::stringifyMachineState(mData->mMachineState));
13103
13104 ComObjPtr<Progress> pProgress;
13105 pProgress.createObject();
13106 rc = pProgress->init(i_getVirtualBox(),
13107 static_cast<IMachine *>(this) /* aInitiator */,
13108 tr("Saving the execution state of the virtual machine"),
13109 FALSE /* aCancelable */);
13110 if (FAILED(rc))
13111 return rc;
13112
13113 Utf8Str strStateFilePath;
13114 i_composeSavedStateFilename(strStateFilePath);
13115
13116 /* create and start the task on a separate thread (note that it will not
13117 * start working until we release alock) */
13118 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13119 rc = pTask->createThread();
13120 if (FAILED(rc))
13121 return rc;
13122
13123 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13124 i_setMachineState(MachineState_Saving);
13125 i_updateMachineStateOnClient();
13126
13127 pProgress.queryInterfaceTo(aProgress.asOutParam());
13128
13129 return S_OK;
13130}
13131
13132/**
13133 * @note Locks this object for writing.
13134 */
13135HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13136{
13137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13138
13139 HRESULT rc = i_checkStateDependency(MutableStateDep);
13140 if (FAILED(rc)) return rc;
13141
13142 if ( mData->mMachineState != MachineState_PoweredOff
13143 && mData->mMachineState != MachineState_Teleported
13144 && mData->mMachineState != MachineState_Aborted
13145 )
13146 return setError(VBOX_E_INVALID_VM_STATE,
13147 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13148 Global::stringifyMachineState(mData->mMachineState));
13149
13150 com::Utf8Str stateFilePathFull;
13151 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13152 if (RT_FAILURE(vrc))
13153 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13154 tr("Invalid saved state file path '%s' (%Rrc)"),
13155 aSavedStateFile.c_str(),
13156 vrc);
13157
13158 mSSData->strStateFilePath = stateFilePathFull;
13159
13160 /* The below i_setMachineState() will detect the state transition and will
13161 * update the settings file */
13162
13163 return i_setMachineState(MachineState_Saved);
13164}
13165
13166/**
13167 * @note Locks this object for writing.
13168 */
13169HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13170{
13171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13172
13173 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13174 if (FAILED(rc)) return rc;
13175
13176 if ( mData->mMachineState != MachineState_Saved
13177 && mData->mMachineState != MachineState_AbortedSaved)
13178 return setError(VBOX_E_INVALID_VM_STATE,
13179 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13180 Global::stringifyMachineState(mData->mMachineState));
13181
13182 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13183
13184 /*
13185 * Saved -> PoweredOff transition will be detected in the SessionMachine
13186 * and properly handled.
13187 */
13188 rc = i_setMachineState(MachineState_PoweredOff);
13189 return rc;
13190}
13191
13192
13193/**
13194 * @note Locks the same as #i_setMachineState() does.
13195 */
13196HRESULT SessionMachine::updateState(MachineState_T aState)
13197{
13198 return i_setMachineState(aState);
13199}
13200
13201/**
13202 * @note Locks this object for writing.
13203 */
13204HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13205{
13206 IProgress *pProgress(aProgress);
13207
13208 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13209
13210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13211
13212 if (mData->mSession.mState != SessionState_Locked)
13213 return VBOX_E_INVALID_OBJECT_STATE;
13214
13215 if (!mData->mSession.mProgress.isNull())
13216 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13217
13218 /* If we didn't reference the NAT network service yet, add a reference to
13219 * force a start */
13220 if (miNATNetworksStarted < 1)
13221 {
13222 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13223 {
13224 BOOL enabled;
13225 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13226 if ( FAILED(hrc)
13227 || !enabled)
13228 continue;
13229
13230 NetworkAttachmentType_T type;
13231 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13232 if ( SUCCEEDED(hrc)
13233 && type == NetworkAttachmentType_NATNetwork)
13234 {
13235 Bstr name;
13236 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13237 if (SUCCEEDED(hrc))
13238 {
13239 Utf8Str strName(name);
13240 LogRel(("VM '%s' starts using NAT network '%s'\n",
13241 mUserData->s.strName.c_str(), strName.c_str()));
13242 mPeer->lockHandle()->unlockWrite();
13243 mParent->i_natNetworkRefInc(strName);
13244#ifdef RT_LOCK_STRICT
13245 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13246#else
13247 mPeer->lockHandle()->lockWrite();
13248#endif
13249 }
13250 }
13251 }
13252 miNATNetworksStarted++;
13253 }
13254
13255 LogFlowThisFunc(("returns S_OK.\n"));
13256 return S_OK;
13257}
13258
13259/**
13260 * @note Locks this object for writing.
13261 */
13262HRESULT SessionMachine::endPowerUp(LONG aResult)
13263{
13264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13265
13266 if (mData->mSession.mState != SessionState_Locked)
13267 return VBOX_E_INVALID_OBJECT_STATE;
13268
13269 /* Finalize the LaunchVMProcess progress object. */
13270 if (mData->mSession.mProgress)
13271 {
13272 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13273 mData->mSession.mProgress.setNull();
13274 }
13275
13276 if (SUCCEEDED((HRESULT)aResult))
13277 {
13278#ifdef VBOX_WITH_RESOURCE_USAGE_API
13279 /* The VM has been powered up successfully, so it makes sense
13280 * now to offer the performance metrics for a running machine
13281 * object. Doing it earlier wouldn't be safe. */
13282 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13283 mData->mSession.mPID);
13284#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13285 }
13286
13287 return S_OK;
13288}
13289
13290/**
13291 * @note Locks this object for writing.
13292 */
13293HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13294{
13295 LogFlowThisFuncEnter();
13296
13297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13298
13299 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13300 E_FAIL);
13301
13302 /* create a progress object to track operation completion */
13303 ComObjPtr<Progress> pProgress;
13304 pProgress.createObject();
13305 pProgress->init(i_getVirtualBox(),
13306 static_cast<IMachine *>(this) /* aInitiator */,
13307 tr("Stopping the virtual machine"),
13308 FALSE /* aCancelable */);
13309
13310 /* fill in the console task data */
13311 mConsoleTaskData.mLastState = mData->mMachineState;
13312 mConsoleTaskData.mProgress = pProgress;
13313
13314 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13315 i_setMachineState(MachineState_Stopping);
13316
13317 pProgress.queryInterfaceTo(aProgress.asOutParam());
13318
13319 return S_OK;
13320}
13321
13322/**
13323 * @note Locks this object for writing.
13324 */
13325HRESULT SessionMachine::endPoweringDown(LONG aResult,
13326 const com::Utf8Str &aErrMsg)
13327{
13328 HRESULT const hrcResult = (HRESULT)aResult;
13329 LogFlowThisFuncEnter();
13330
13331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13332
13333 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13334 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13335 && mConsoleTaskData.mLastState != MachineState_Null,
13336 E_FAIL);
13337
13338 /*
13339 * On failure, set the state to the state we had when BeginPoweringDown()
13340 * was called (this is expected by Console::PowerDown() and the associated
13341 * task). On success the VM process already changed the state to
13342 * MachineState_PoweredOff, so no need to do anything.
13343 */
13344 if (FAILED(hrcResult))
13345 i_setMachineState(mConsoleTaskData.mLastState);
13346
13347 /* notify the progress object about operation completion */
13348 Assert(mConsoleTaskData.mProgress);
13349 if (SUCCEEDED(hrcResult))
13350 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13351 else
13352 {
13353 if (aErrMsg.length())
13354 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13355 COM_IIDOF(ISession),
13356 getComponentName(),
13357 aErrMsg.c_str());
13358 else
13359 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13360 }
13361
13362 /* clear out the temporary saved state data */
13363 mConsoleTaskData.mLastState = MachineState_Null;
13364 mConsoleTaskData.mProgress.setNull();
13365
13366 LogFlowThisFuncLeave();
13367 return S_OK;
13368}
13369
13370
13371/**
13372 * Goes through the USB filters of the given machine to see if the given
13373 * device matches any filter or not.
13374 *
13375 * @note Locks the same as USBController::hasMatchingFilter() does.
13376 */
13377HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13378 BOOL *aMatched,
13379 ULONG *aMaskedInterfaces)
13380{
13381 LogFlowThisFunc(("\n"));
13382
13383#ifdef VBOX_WITH_USB
13384 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13385#else
13386 NOREF(aDevice);
13387 NOREF(aMaskedInterfaces);
13388 *aMatched = FALSE;
13389#endif
13390
13391 return S_OK;
13392}
13393
13394/**
13395 * @note Locks the same as Host::captureUSBDevice() does.
13396 */
13397HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13398{
13399 LogFlowThisFunc(("\n"));
13400
13401#ifdef VBOX_WITH_USB
13402 /* if captureDeviceForVM() fails, it must have set extended error info */
13403 clearError();
13404 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13405 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13406 return rc;
13407
13408 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13409 AssertReturn(service, E_FAIL);
13410 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13411#else
13412 RT_NOREF(aId, aCaptureFilename);
13413 return E_NOTIMPL;
13414#endif
13415}
13416
13417/**
13418 * @note Locks the same as Host::detachUSBDevice() does.
13419 */
13420HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13421 BOOL aDone)
13422{
13423 LogFlowThisFunc(("\n"));
13424
13425#ifdef VBOX_WITH_USB
13426 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13427 AssertReturn(service, E_FAIL);
13428 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13429#else
13430 NOREF(aId);
13431 NOREF(aDone);
13432 return E_NOTIMPL;
13433#endif
13434}
13435
13436/**
13437 * Inserts all machine filters to the USB proxy service and then calls
13438 * Host::autoCaptureUSBDevices().
13439 *
13440 * Called by Console from the VM process upon VM startup.
13441 *
13442 * @note Locks what called methods lock.
13443 */
13444HRESULT SessionMachine::autoCaptureUSBDevices()
13445{
13446 LogFlowThisFunc(("\n"));
13447
13448#ifdef VBOX_WITH_USB
13449 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13450 AssertComRC(rc);
13451 NOREF(rc);
13452
13453 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13454 AssertReturn(service, E_FAIL);
13455 return service->autoCaptureDevicesForVM(this);
13456#else
13457 return S_OK;
13458#endif
13459}
13460
13461/**
13462 * Removes all machine filters from the USB proxy service and then calls
13463 * Host::detachAllUSBDevices().
13464 *
13465 * Called by Console from the VM process upon normal VM termination or by
13466 * SessionMachine::uninit() upon abnormal VM termination (from under the
13467 * Machine/SessionMachine lock).
13468 *
13469 * @note Locks what called methods lock.
13470 */
13471HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13472{
13473 LogFlowThisFunc(("\n"));
13474
13475#ifdef VBOX_WITH_USB
13476 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13477 AssertComRC(rc);
13478 NOREF(rc);
13479
13480 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13481 AssertReturn(service, E_FAIL);
13482 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13483#else
13484 NOREF(aDone);
13485 return S_OK;
13486#endif
13487}
13488
13489/**
13490 * @note Locks this object for writing.
13491 */
13492HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13493 ComPtr<IProgress> &aProgress)
13494{
13495 LogFlowThisFuncEnter();
13496
13497 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13498 /*
13499 * We don't assert below because it might happen that a non-direct session
13500 * informs us it is closed right after we've been uninitialized -- it's ok.
13501 */
13502
13503 /* get IInternalSessionControl interface */
13504 ComPtr<IInternalSessionControl> control(aSession);
13505
13506 ComAssertRet(!control.isNull(), E_INVALIDARG);
13507
13508 /* Creating a Progress object requires the VirtualBox lock, and
13509 * thus locking it here is required by the lock order rules. */
13510 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13511
13512 if (control == mData->mSession.mDirectControl)
13513 {
13514 /* The direct session is being normally closed by the client process
13515 * ----------------------------------------------------------------- */
13516
13517 /* go to the closing state (essential for all open*Session() calls and
13518 * for #i_checkForDeath()) */
13519 Assert(mData->mSession.mState == SessionState_Locked);
13520 mData->mSession.mState = SessionState_Unlocking;
13521
13522 /* set direct control to NULL to release the remote instance */
13523 mData->mSession.mDirectControl.setNull();
13524 LogFlowThisFunc(("Direct control is set to NULL\n"));
13525
13526 if (mData->mSession.mProgress)
13527 {
13528 /* finalize the progress, someone might wait if a frontend
13529 * closes the session before powering on the VM. */
13530 mData->mSession.mProgress->notifyComplete(E_FAIL,
13531 COM_IIDOF(ISession),
13532 getComponentName(),
13533 tr("The VM session was closed before any attempt to power it on"));
13534 mData->mSession.mProgress.setNull();
13535 }
13536
13537 /* Create the progress object the client will use to wait until
13538 * #i_checkForDeath() is called to uninitialize this session object after
13539 * it releases the IPC semaphore.
13540 * Note! Because we're "reusing" mProgress here, this must be a proxy
13541 * object just like for LaunchVMProcess. */
13542 Assert(mData->mSession.mProgress.isNull());
13543 ComObjPtr<ProgressProxy> progress;
13544 progress.createObject();
13545 ComPtr<IUnknown> pPeer(mPeer);
13546 progress->init(mParent, pPeer,
13547 Bstr(tr("Closing session")).raw(),
13548 FALSE /* aCancelable */);
13549 progress.queryInterfaceTo(aProgress.asOutParam());
13550 mData->mSession.mProgress = progress;
13551 }
13552 else
13553 {
13554 /* the remote session is being normally closed */
13555 bool found = false;
13556 for (Data::Session::RemoteControlList::iterator
13557 it = mData->mSession.mRemoteControls.begin();
13558 it != mData->mSession.mRemoteControls.end();
13559 ++it)
13560 {
13561 if (control == *it)
13562 {
13563 found = true;
13564 // This MUST be erase(it), not remove(*it) as the latter
13565 // triggers a very nasty use after free due to the place where
13566 // the value "lives".
13567 mData->mSession.mRemoteControls.erase(it);
13568 break;
13569 }
13570 }
13571 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13572 E_INVALIDARG);
13573 }
13574
13575 /* signal the client watcher thread, because the client is going away */
13576 mParent->i_updateClientWatcher();
13577
13578 LogFlowThisFuncLeave();
13579 return S_OK;
13580}
13581
13582HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13583 std::vector<com::Utf8Str> &aValues,
13584 std::vector<LONG64> &aTimestamps,
13585 std::vector<com::Utf8Str> &aFlags)
13586{
13587 LogFlowThisFunc(("\n"));
13588
13589#ifdef VBOX_WITH_GUEST_PROPS
13590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13591
13592 size_t cEntries = mHWData->mGuestProperties.size();
13593 aNames.resize(cEntries);
13594 aValues.resize(cEntries);
13595 aTimestamps.resize(cEntries);
13596 aFlags.resize(cEntries);
13597
13598 size_t i = 0;
13599 for (HWData::GuestPropertyMap::const_iterator
13600 it = mHWData->mGuestProperties.begin();
13601 it != mHWData->mGuestProperties.end();
13602 ++it, ++i)
13603 {
13604 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13605 aNames[i] = it->first;
13606 aValues[i] = it->second.strValue;
13607 aTimestamps[i] = it->second.mTimestamp;
13608
13609 /* If it is NULL, keep it NULL. */
13610 if (it->second.mFlags)
13611 {
13612 GuestPropWriteFlags(it->second.mFlags, szFlags);
13613 aFlags[i] = szFlags;
13614 }
13615 else
13616 aFlags[i] = "";
13617
13618 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13619 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13620 }
13621 return S_OK;
13622#else
13623 ReturnComNotImplemented();
13624#endif
13625}
13626
13627HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13628 const com::Utf8Str &aValue,
13629 LONG64 aTimestamp,
13630 const com::Utf8Str &aFlags,
13631 BOOL fWasDeleted)
13632{
13633 LogFlowThisFunc(("\n"));
13634
13635#ifdef VBOX_WITH_GUEST_PROPS
13636 try
13637 {
13638 /*
13639 * Convert input up front.
13640 */
13641 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13642 if (aFlags.length())
13643 {
13644 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13645 AssertRCReturn(vrc, E_INVALIDARG);
13646 }
13647
13648 /*
13649 * Now grab the object lock, validate the state and do the update.
13650 */
13651
13652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13653
13654 if (!Global::IsOnline(mData->mMachineState))
13655 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13656
13657 i_setModified(IsModified_MachineData);
13658 mHWData.backup();
13659
13660 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13661 if (it != mHWData->mGuestProperties.end())
13662 {
13663 if (!fWasDeleted)
13664 {
13665 it->second.strValue = aValue;
13666 it->second.mTimestamp = aTimestamp;
13667 it->second.mFlags = fFlags;
13668 }
13669 else
13670 mHWData->mGuestProperties.erase(it);
13671
13672 mData->mGuestPropertiesModified = TRUE;
13673 }
13674 else if (!fWasDeleted)
13675 {
13676 HWData::GuestProperty prop;
13677 prop.strValue = aValue;
13678 prop.mTimestamp = aTimestamp;
13679 prop.mFlags = fFlags;
13680
13681 mHWData->mGuestProperties[aName] = prop;
13682 mData->mGuestPropertiesModified = TRUE;
13683 }
13684
13685 alock.release();
13686
13687 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13688 }
13689 catch (...)
13690 {
13691 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13692 }
13693 return S_OK;
13694#else
13695 ReturnComNotImplemented();
13696#endif
13697}
13698
13699
13700HRESULT SessionMachine::lockMedia()
13701{
13702 AutoMultiWriteLock2 alock(this->lockHandle(),
13703 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13704
13705 AssertReturn( mData->mMachineState == MachineState_Starting
13706 || mData->mMachineState == MachineState_Restoring
13707 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13708
13709 clearError();
13710 alock.release();
13711 return i_lockMedia();
13712}
13713
13714HRESULT SessionMachine::unlockMedia()
13715{
13716 HRESULT hrc = i_unlockMedia();
13717 return hrc;
13718}
13719
13720HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13721 ComPtr<IMediumAttachment> &aNewAttachment)
13722{
13723 // request the host lock first, since might be calling Host methods for getting host drives;
13724 // next, protect the media tree all the while we're in here, as well as our member variables
13725 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13726 this->lockHandle(),
13727 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13728
13729 IMediumAttachment *iAttach = aAttachment;
13730 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13731
13732 Utf8Str ctrlName;
13733 LONG lPort;
13734 LONG lDevice;
13735 bool fTempEject;
13736 {
13737 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13738
13739 /* Need to query the details first, as the IMediumAttachment reference
13740 * might be to the original settings, which we are going to change. */
13741 ctrlName = pAttach->i_getControllerName();
13742 lPort = pAttach->i_getPort();
13743 lDevice = pAttach->i_getDevice();
13744 fTempEject = pAttach->i_getTempEject();
13745 }
13746
13747 if (!fTempEject)
13748 {
13749 /* Remember previously mounted medium. The medium before taking the
13750 * backup is not necessarily the same thing. */
13751 ComObjPtr<Medium> oldmedium;
13752 oldmedium = pAttach->i_getMedium();
13753
13754 i_setModified(IsModified_Storage);
13755 mMediumAttachments.backup();
13756
13757 // The backup operation makes the pAttach reference point to the
13758 // old settings. Re-get the correct reference.
13759 pAttach = i_findAttachment(*mMediumAttachments.data(),
13760 ctrlName,
13761 lPort,
13762 lDevice);
13763
13764 {
13765 AutoCaller autoAttachCaller(this);
13766 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13767
13768 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13769 if (!oldmedium.isNull())
13770 oldmedium->i_removeBackReference(mData->mUuid);
13771
13772 pAttach->i_updateMedium(NULL);
13773 pAttach->i_updateEjected();
13774 }
13775
13776 i_setModified(IsModified_Storage);
13777 }
13778 else
13779 {
13780 {
13781 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13782 pAttach->i_updateEjected();
13783 }
13784 }
13785
13786 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13787
13788 return S_OK;
13789}
13790
13791HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13792 com::Utf8Str &aResult)
13793{
13794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13795
13796 HRESULT hr = S_OK;
13797
13798 if (!mAuthLibCtx.hAuthLibrary)
13799 {
13800 /* Load the external authentication library. */
13801 Bstr authLibrary;
13802 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13803
13804 Utf8Str filename = authLibrary;
13805
13806 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13807 if (RT_FAILURE(vrc))
13808 hr = setErrorBoth(E_FAIL, vrc,
13809 tr("Could not load the external authentication library '%s' (%Rrc)"),
13810 filename.c_str(), vrc);
13811 }
13812
13813 /* The auth library might need the machine lock. */
13814 alock.release();
13815
13816 if (FAILED(hr))
13817 return hr;
13818
13819 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13820 {
13821 enum VRDEAuthParams
13822 {
13823 parmUuid = 1,
13824 parmGuestJudgement,
13825 parmUser,
13826 parmPassword,
13827 parmDomain,
13828 parmClientId
13829 };
13830
13831 AuthResult result = AuthResultAccessDenied;
13832
13833 Guid uuid(aAuthParams[parmUuid]);
13834 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13835 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13836
13837 result = AuthLibAuthenticate(&mAuthLibCtx,
13838 uuid.raw(), guestJudgement,
13839 aAuthParams[parmUser].c_str(),
13840 aAuthParams[parmPassword].c_str(),
13841 aAuthParams[parmDomain].c_str(),
13842 u32ClientId);
13843
13844 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13845 size_t cbPassword = aAuthParams[parmPassword].length();
13846 if (cbPassword)
13847 {
13848 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13849 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13850 }
13851
13852 if (result == AuthResultAccessGranted)
13853 aResult = "granted";
13854 else
13855 aResult = "denied";
13856
13857 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13858 aAuthParams[parmUser].c_str(), aResult.c_str()));
13859 }
13860 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13861 {
13862 enum VRDEAuthDisconnectParams
13863 {
13864 parmUuid = 1,
13865 parmClientId
13866 };
13867
13868 Guid uuid(aAuthParams[parmUuid]);
13869 uint32_t u32ClientId = 0;
13870 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13871 }
13872 else
13873 {
13874 hr = E_INVALIDARG;
13875 }
13876
13877 return hr;
13878}
13879
13880// public methods only for internal purposes
13881/////////////////////////////////////////////////////////////////////////////
13882
13883#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13884/**
13885 * Called from the client watcher thread to check for expected or unexpected
13886 * death of the client process that has a direct session to this machine.
13887 *
13888 * On Win32 and on OS/2, this method is called only when we've got the
13889 * mutex (i.e. the client has either died or terminated normally) so it always
13890 * returns @c true (the client is terminated, the session machine is
13891 * uninitialized).
13892 *
13893 * On other platforms, the method returns @c true if the client process has
13894 * terminated normally or abnormally and the session machine was uninitialized,
13895 * and @c false if the client process is still alive.
13896 *
13897 * @note Locks this object for writing.
13898 */
13899bool SessionMachine::i_checkForDeath()
13900{
13901 Uninit::Reason reason;
13902 bool terminated = false;
13903
13904 /* Enclose autoCaller with a block because calling uninit() from under it
13905 * will deadlock. */
13906 {
13907 AutoCaller autoCaller(this);
13908 if (!autoCaller.isOk())
13909 {
13910 /* return true if not ready, to cause the client watcher to exclude
13911 * the corresponding session from watching */
13912 LogFlowThisFunc(("Already uninitialized!\n"));
13913 return true;
13914 }
13915
13916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13917
13918 /* Determine the reason of death: if the session state is Closing here,
13919 * everything is fine. Otherwise it means that the client did not call
13920 * OnSessionEnd() before it released the IPC semaphore. This may happen
13921 * either because the client process has abnormally terminated, or
13922 * because it simply forgot to call ISession::Close() before exiting. We
13923 * threat the latter also as an abnormal termination (see
13924 * Session::uninit() for details). */
13925 reason = mData->mSession.mState == SessionState_Unlocking ?
13926 Uninit::Normal :
13927 Uninit::Abnormal;
13928
13929 if (mClientToken)
13930 terminated = mClientToken->release();
13931 } /* AutoCaller block */
13932
13933 if (terminated)
13934 uninit(reason);
13935
13936 return terminated;
13937}
13938
13939void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13940{
13941 LogFlowThisFunc(("\n"));
13942
13943 strTokenId.setNull();
13944
13945 AutoCaller autoCaller(this);
13946 AssertComRCReturnVoid(autoCaller.rc());
13947
13948 Assert(mClientToken);
13949 if (mClientToken)
13950 mClientToken->getId(strTokenId);
13951}
13952#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13953IToken *SessionMachine::i_getToken()
13954{
13955 LogFlowThisFunc(("\n"));
13956
13957 AutoCaller autoCaller(this);
13958 AssertComRCReturn(autoCaller.rc(), NULL);
13959
13960 Assert(mClientToken);
13961 if (mClientToken)
13962 return mClientToken->getToken();
13963 else
13964 return NULL;
13965}
13966#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13967
13968Machine::ClientToken *SessionMachine::i_getClientToken()
13969{
13970 LogFlowThisFunc(("\n"));
13971
13972 AutoCaller autoCaller(this);
13973 AssertComRCReturn(autoCaller.rc(), NULL);
13974
13975 return mClientToken;
13976}
13977
13978
13979/**
13980 * @note Locks this object for reading.
13981 */
13982HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13983{
13984 LogFlowThisFunc(("\n"));
13985
13986 AutoCaller autoCaller(this);
13987 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13988
13989 ComPtr<IInternalSessionControl> directControl;
13990 {
13991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13992 if (mData->mSession.mLockType == LockType_VM)
13993 directControl = mData->mSession.mDirectControl;
13994 }
13995
13996 /* ignore notifications sent after #OnSessionEnd() is called */
13997 if (!directControl)
13998 return S_OK;
13999
14000 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14001}
14002
14003/**
14004 * @note Locks this object for reading.
14005 */
14006HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14007 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14008 const Utf8Str &aGuestIp, LONG aGuestPort)
14009{
14010 LogFlowThisFunc(("\n"));
14011
14012 AutoCaller autoCaller(this);
14013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14014
14015 ComPtr<IInternalSessionControl> directControl;
14016 {
14017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14018 if (mData->mSession.mLockType == LockType_VM)
14019 directControl = mData->mSession.mDirectControl;
14020 }
14021
14022 /* ignore notifications sent after #OnSessionEnd() is called */
14023 if (!directControl)
14024 return S_OK;
14025 /*
14026 * instead acting like callback we ask IVirtualBox deliver corresponding event
14027 */
14028
14029 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14030 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14031 return S_OK;
14032}
14033
14034/**
14035 * @note Locks this object for reading.
14036 */
14037HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14043
14044 ComPtr<IInternalSessionControl> directControl;
14045 {
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047 if (mData->mSession.mLockType == LockType_VM)
14048 directControl = mData->mSession.mDirectControl;
14049 }
14050
14051 /* ignore notifications sent after #OnSessionEnd() is called */
14052 if (!directControl)
14053 return S_OK;
14054
14055 return directControl->OnAudioAdapterChange(audioAdapter);
14056}
14057
14058/**
14059 * @note Locks this object for reading.
14060 */
14061HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14062{
14063 LogFlowThisFunc(("\n"));
14064
14065 AutoCaller autoCaller(this);
14066 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14067
14068 ComPtr<IInternalSessionControl> directControl;
14069 {
14070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14071 if (mData->mSession.mLockType == LockType_VM)
14072 directControl = mData->mSession.mDirectControl;
14073 }
14074
14075 /* ignore notifications sent after #OnSessionEnd() is called */
14076 if (!directControl)
14077 return S_OK;
14078
14079 return directControl->OnSerialPortChange(serialPort);
14080}
14081
14082/**
14083 * @note Locks this object for reading.
14084 */
14085HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14086{
14087 LogFlowThisFunc(("\n"));
14088
14089 AutoCaller autoCaller(this);
14090 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14091
14092 ComPtr<IInternalSessionControl> directControl;
14093 {
14094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14095 if (mData->mSession.mLockType == LockType_VM)
14096 directControl = mData->mSession.mDirectControl;
14097 }
14098
14099 /* ignore notifications sent after #OnSessionEnd() is called */
14100 if (!directControl)
14101 return S_OK;
14102
14103 return directControl->OnParallelPortChange(parallelPort);
14104}
14105
14106/**
14107 * @note Locks this object for reading.
14108 */
14109HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14110{
14111 LogFlowThisFunc(("\n"));
14112
14113 AutoCaller autoCaller(this);
14114 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14115
14116 ComPtr<IInternalSessionControl> directControl;
14117 {
14118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14119 if (mData->mSession.mLockType == LockType_VM)
14120 directControl = mData->mSession.mDirectControl;
14121 }
14122
14123 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14124
14125 /* ignore notifications sent after #OnSessionEnd() is called */
14126 if (!directControl)
14127 return S_OK;
14128
14129 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14130}
14131
14132/**
14133 * @note Locks this object for reading.
14134 */
14135HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14136{
14137 LogFlowThisFunc(("\n"));
14138
14139 AutoCaller autoCaller(this);
14140 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14141
14142 ComPtr<IInternalSessionControl> directControl;
14143 {
14144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14145 if (mData->mSession.mLockType == LockType_VM)
14146 directControl = mData->mSession.mDirectControl;
14147 }
14148
14149 mParent->i_onMediumChanged(aAttachment);
14150
14151 /* ignore notifications sent after #OnSessionEnd() is called */
14152 if (!directControl)
14153 return S_OK;
14154
14155 return directControl->OnMediumChange(aAttachment, aForce);
14156}
14157
14158HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14159{
14160 LogFlowThisFunc(("\n"));
14161
14162 AutoCaller autoCaller(this);
14163 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14164
14165 ComPtr<IInternalSessionControl> directControl;
14166 {
14167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14168 if (mData->mSession.mLockType == LockType_VM)
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* ignore notifications sent after #OnSessionEnd() is called */
14173 if (!directControl)
14174 return S_OK;
14175
14176 return directControl->OnVMProcessPriorityChange(aPriority);
14177}
14178
14179/**
14180 * @note Locks this object for reading.
14181 */
14182HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14183{
14184 LogFlowThisFunc(("\n"));
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 if (mData->mSession.mLockType == LockType_VM)
14193 directControl = mData->mSession.mDirectControl;
14194 }
14195
14196 /* ignore notifications sent after #OnSessionEnd() is called */
14197 if (!directControl)
14198 return S_OK;
14199
14200 return directControl->OnCPUChange(aCPU, aRemove);
14201}
14202
14203HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14204{
14205 LogFlowThisFunc(("\n"));
14206
14207 AutoCaller autoCaller(this);
14208 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14209
14210 ComPtr<IInternalSessionControl> directControl;
14211 {
14212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14213 if (mData->mSession.mLockType == LockType_VM)
14214 directControl = mData->mSession.mDirectControl;
14215 }
14216
14217 /* ignore notifications sent after #OnSessionEnd() is called */
14218 if (!directControl)
14219 return S_OK;
14220
14221 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14222}
14223
14224/**
14225 * @note Locks this object for reading.
14226 */
14227HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14228{
14229 LogFlowThisFunc(("\n"));
14230
14231 AutoCaller autoCaller(this);
14232 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14233
14234 ComPtr<IInternalSessionControl> directControl;
14235 {
14236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14237 if (mData->mSession.mLockType == LockType_VM)
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 /* ignore notifications sent after #OnSessionEnd() is called */
14242 if (!directControl)
14243 return S_OK;
14244
14245 return directControl->OnVRDEServerChange(aRestart);
14246}
14247
14248/**
14249 * @note Locks this object for reading.
14250 */
14251HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14252{
14253 LogFlowThisFunc(("\n"));
14254
14255 AutoCaller autoCaller(this);
14256 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14257
14258 ComPtr<IInternalSessionControl> directControl;
14259 {
14260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14261 if (mData->mSession.mLockType == LockType_VM)
14262 directControl = mData->mSession.mDirectControl;
14263 }
14264
14265 /* ignore notifications sent after #OnSessionEnd() is called */
14266 if (!directControl)
14267 return S_OK;
14268
14269 return directControl->OnRecordingChange(aEnable);
14270}
14271
14272/**
14273 * @note Locks this object for reading.
14274 */
14275HRESULT SessionMachine::i_onUSBControllerChange()
14276{
14277 LogFlowThisFunc(("\n"));
14278
14279 AutoCaller autoCaller(this);
14280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14281
14282 ComPtr<IInternalSessionControl> directControl;
14283 {
14284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14285 if (mData->mSession.mLockType == LockType_VM)
14286 directControl = mData->mSession.mDirectControl;
14287 }
14288
14289 /* ignore notifications sent after #OnSessionEnd() is called */
14290 if (!directControl)
14291 return S_OK;
14292
14293 return directControl->OnUSBControllerChange();
14294}
14295
14296/**
14297 * @note Locks this object for reading.
14298 */
14299HRESULT SessionMachine::i_onSharedFolderChange()
14300{
14301 LogFlowThisFunc(("\n"));
14302
14303 AutoCaller autoCaller(this);
14304 AssertComRCReturnRC(autoCaller.rc());
14305
14306 ComPtr<IInternalSessionControl> directControl;
14307 {
14308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14309 if (mData->mSession.mLockType == LockType_VM)
14310 directControl = mData->mSession.mDirectControl;
14311 }
14312
14313 /* ignore notifications sent after #OnSessionEnd() is called */
14314 if (!directControl)
14315 return S_OK;
14316
14317 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14318}
14319
14320/**
14321 * @note Locks this object for reading.
14322 */
14323HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14324{
14325 LogFlowThisFunc(("\n"));
14326
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturnRC(autoCaller.rc());
14329
14330 ComPtr<IInternalSessionControl> directControl;
14331 {
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333 if (mData->mSession.mLockType == LockType_VM)
14334 directControl = mData->mSession.mDirectControl;
14335 }
14336
14337 /* ignore notifications sent after #OnSessionEnd() is called */
14338 if (!directControl)
14339 return S_OK;
14340
14341 return directControl->OnClipboardModeChange(aClipboardMode);
14342}
14343
14344/**
14345 * @note Locks this object for reading.
14346 */
14347HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14348{
14349 LogFlowThisFunc(("\n"));
14350
14351 AutoCaller autoCaller(this);
14352 AssertComRCReturnRC(autoCaller.rc());
14353
14354 ComPtr<IInternalSessionControl> directControl;
14355 {
14356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14357 if (mData->mSession.mLockType == LockType_VM)
14358 directControl = mData->mSession.mDirectControl;
14359 }
14360
14361 /* ignore notifications sent after #OnSessionEnd() is called */
14362 if (!directControl)
14363 return S_OK;
14364
14365 return directControl->OnClipboardFileTransferModeChange(aEnable);
14366}
14367
14368/**
14369 * @note Locks this object for reading.
14370 */
14371HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14372{
14373 LogFlowThisFunc(("\n"));
14374
14375 AutoCaller autoCaller(this);
14376 AssertComRCReturnRC(autoCaller.rc());
14377
14378 ComPtr<IInternalSessionControl> directControl;
14379 {
14380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14381 if (mData->mSession.mLockType == LockType_VM)
14382 directControl = mData->mSession.mDirectControl;
14383 }
14384
14385 /* ignore notifications sent after #OnSessionEnd() is called */
14386 if (!directControl)
14387 return S_OK;
14388
14389 return directControl->OnDnDModeChange(aDnDMode);
14390}
14391
14392/**
14393 * @note Locks this object for reading.
14394 */
14395HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14396{
14397 LogFlowThisFunc(("\n"));
14398
14399 AutoCaller autoCaller(this);
14400 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14401
14402 ComPtr<IInternalSessionControl> directControl;
14403 {
14404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14405 if (mData->mSession.mLockType == LockType_VM)
14406 directControl = mData->mSession.mDirectControl;
14407 }
14408
14409 /* ignore notifications sent after #OnSessionEnd() is called */
14410 if (!directControl)
14411 return S_OK;
14412
14413 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14414}
14415
14416/**
14417 * @note Locks this object for reading.
14418 */
14419HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14420{
14421 LogFlowThisFunc(("\n"));
14422
14423 AutoCaller autoCaller(this);
14424 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14425
14426 ComPtr<IInternalSessionControl> directControl;
14427 {
14428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14429 if (mData->mSession.mLockType == LockType_VM)
14430 directControl = mData->mSession.mDirectControl;
14431 }
14432
14433 /* ignore notifications sent after #OnSessionEnd() is called */
14434 if (!directControl)
14435 return S_OK;
14436
14437 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14438}
14439
14440/**
14441 * Returns @c true if this machine's USB controller reports it has a matching
14442 * filter for the given USB device and @c false otherwise.
14443 *
14444 * @note locks this object for reading.
14445 */
14446bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14447{
14448 AutoCaller autoCaller(this);
14449 /* silently return if not ready -- this method may be called after the
14450 * direct machine session has been called */
14451 if (!autoCaller.isOk())
14452 return false;
14453
14454#ifdef VBOX_WITH_USB
14455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14456
14457 switch (mData->mMachineState)
14458 {
14459 case MachineState_Starting:
14460 case MachineState_Restoring:
14461 case MachineState_TeleportingIn:
14462 case MachineState_Paused:
14463 case MachineState_Running:
14464 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14465 * elsewhere... */
14466 alock.release();
14467 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14468 default: break;
14469 }
14470#else
14471 NOREF(aDevice);
14472 NOREF(aMaskedIfs);
14473#endif
14474 return false;
14475}
14476
14477/**
14478 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14479 */
14480HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14481 IVirtualBoxErrorInfo *aError,
14482 ULONG aMaskedIfs,
14483 const com::Utf8Str &aCaptureFilename)
14484{
14485 LogFlowThisFunc(("\n"));
14486
14487 AutoCaller autoCaller(this);
14488
14489 /* This notification may happen after the machine object has been
14490 * uninitialized (the session was closed), so don't assert. */
14491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14492
14493 ComPtr<IInternalSessionControl> directControl;
14494 {
14495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14496 if (mData->mSession.mLockType == LockType_VM)
14497 directControl = mData->mSession.mDirectControl;
14498 }
14499
14500 /* fail on notifications sent after #OnSessionEnd() is called, it is
14501 * expected by the caller */
14502 if (!directControl)
14503 return E_FAIL;
14504
14505 /* No locks should be held at this point. */
14506 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14507 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14508
14509 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14510}
14511
14512/**
14513 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14514 */
14515HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14516 IVirtualBoxErrorInfo *aError)
14517{
14518 LogFlowThisFunc(("\n"));
14519
14520 AutoCaller autoCaller(this);
14521
14522 /* This notification may happen after the machine object has been
14523 * uninitialized (the session was closed), so don't assert. */
14524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14525
14526 ComPtr<IInternalSessionControl> directControl;
14527 {
14528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14529 if (mData->mSession.mLockType == LockType_VM)
14530 directControl = mData->mSession.mDirectControl;
14531 }
14532
14533 /* fail on notifications sent after #OnSessionEnd() is called, it is
14534 * expected by the caller */
14535 if (!directControl)
14536 return E_FAIL;
14537
14538 /* No locks should be held at this point. */
14539 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14540 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14541
14542 return directControl->OnUSBDeviceDetach(aId, aError);
14543}
14544
14545// protected methods
14546/////////////////////////////////////////////////////////////////////////////
14547
14548/**
14549 * Deletes the given file if it is no longer in use by either the current machine state
14550 * (if the machine is "saved") or any of the machine's snapshots.
14551 *
14552 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14553 * but is different for each SnapshotMachine. When calling this, the order of calling this
14554 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14555 * is therefore critical. I know, it's all rather messy.
14556 *
14557 * @param strStateFile
14558 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14559 * the test for whether the saved state file is in use.
14560 */
14561void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14562 Snapshot *pSnapshotToIgnore)
14563{
14564 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14565 if ( (strStateFile.isNotEmpty())
14566 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14567 )
14568 // ... and it must also not be shared with other snapshots
14569 if ( !mData->mFirstSnapshot
14570 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14571 // this checks the SnapshotMachine's state file paths
14572 )
14573 RTFileDelete(strStateFile.c_str());
14574}
14575
14576/**
14577 * Locks the attached media.
14578 *
14579 * All attached hard disks are locked for writing and DVD/floppy are locked for
14580 * reading. Parents of attached hard disks (if any) are locked for reading.
14581 *
14582 * This method also performs accessibility check of all media it locks: if some
14583 * media is inaccessible, the method will return a failure and a bunch of
14584 * extended error info objects per each inaccessible medium.
14585 *
14586 * Note that this method is atomic: if it returns a success, all media are
14587 * locked as described above; on failure no media is locked at all (all
14588 * succeeded individual locks will be undone).
14589 *
14590 * The caller is responsible for doing the necessary state sanity checks.
14591 *
14592 * The locks made by this method must be undone by calling #unlockMedia() when
14593 * no more needed.
14594 */
14595HRESULT SessionMachine::i_lockMedia()
14596{
14597 AutoCaller autoCaller(this);
14598 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14599
14600 AutoMultiWriteLock2 alock(this->lockHandle(),
14601 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14602
14603 /* bail out if trying to lock things with already set up locking */
14604 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14605
14606 MultiResult mrc(S_OK);
14607
14608 /* Collect locking information for all medium objects attached to the VM. */
14609 for (MediumAttachmentList::const_iterator
14610 it = mMediumAttachments->begin();
14611 it != mMediumAttachments->end();
14612 ++it)
14613 {
14614 MediumAttachment *pAtt = *it;
14615 DeviceType_T devType = pAtt->i_getType();
14616 Medium *pMedium = pAtt->i_getMedium();
14617
14618 MediumLockList *pMediumLockList(new MediumLockList());
14619 // There can be attachments without a medium (floppy/dvd), and thus
14620 // it's impossible to create a medium lock list. It still makes sense
14621 // to have the empty medium lock list in the map in case a medium is
14622 // attached later.
14623 if (pMedium != NULL)
14624 {
14625 MediumType_T mediumType = pMedium->i_getType();
14626 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14627 || mediumType == MediumType_Shareable;
14628 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14629
14630 alock.release();
14631 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14632 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14633 false /* fMediumLockWriteAll */,
14634 NULL,
14635 *pMediumLockList);
14636 alock.acquire();
14637 if (FAILED(mrc))
14638 {
14639 delete pMediumLockList;
14640 mData->mSession.mLockedMedia.Clear();
14641 break;
14642 }
14643 }
14644
14645 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14646 if (FAILED(rc))
14647 {
14648 mData->mSession.mLockedMedia.Clear();
14649 mrc = setError(rc,
14650 tr("Collecting locking information for all attached media failed"));
14651 break;
14652 }
14653 }
14654
14655 if (SUCCEEDED(mrc))
14656 {
14657 /* Now lock all media. If this fails, nothing is locked. */
14658 alock.release();
14659 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14660 alock.acquire();
14661 if (FAILED(rc))
14662 {
14663 mrc = setError(rc,
14664 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14665 }
14666 }
14667
14668 return mrc;
14669}
14670
14671/**
14672 * Undoes the locks made by by #lockMedia().
14673 */
14674HRESULT SessionMachine::i_unlockMedia()
14675{
14676 AutoCaller autoCaller(this);
14677 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14678
14679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14680
14681 /* we may be holding important error info on the current thread;
14682 * preserve it */
14683 ErrorInfoKeeper eik;
14684
14685 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14686 AssertComRC(rc);
14687 return rc;
14688}
14689
14690/**
14691 * Helper to change the machine state (reimplementation).
14692 *
14693 * @note Locks this object for writing.
14694 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14695 * it can cause crashes in random places due to unexpectedly committing
14696 * the current settings. The caller is responsible for that. The call
14697 * to saveStateSettings is fine, because this method does not commit.
14698 */
14699HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14700{
14701 LogFlowThisFuncEnter();
14702
14703 AutoCaller autoCaller(this);
14704 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14705
14706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14707
14708 MachineState_T oldMachineState = mData->mMachineState;
14709
14710 AssertMsgReturn(oldMachineState != aMachineState,
14711 ("oldMachineState=%s, aMachineState=%s\n",
14712 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14713 E_FAIL);
14714
14715 HRESULT rc = S_OK;
14716
14717 int stsFlags = 0;
14718 bool deleteSavedState = false;
14719
14720 /* detect some state transitions */
14721
14722 if ( ( ( oldMachineState == MachineState_Saved
14723 || oldMachineState == MachineState_AbortedSaved
14724 )
14725 && aMachineState == MachineState_Restoring
14726 )
14727 || ( ( oldMachineState == MachineState_PoweredOff
14728 || oldMachineState == MachineState_Teleported
14729 || oldMachineState == MachineState_Aborted
14730 )
14731 && ( aMachineState == MachineState_TeleportingIn
14732 || aMachineState == MachineState_Starting
14733 )
14734 )
14735 )
14736 {
14737 /* The EMT thread is about to start */
14738
14739 /* Nothing to do here for now... */
14740
14741 /// @todo NEWMEDIA don't let mDVDDrive and other children
14742 /// change anything when in the Starting/Restoring state
14743 }
14744 else if ( ( oldMachineState == MachineState_Running
14745 || oldMachineState == MachineState_Paused
14746 || oldMachineState == MachineState_Teleporting
14747 || oldMachineState == MachineState_OnlineSnapshotting
14748 || oldMachineState == MachineState_LiveSnapshotting
14749 || oldMachineState == MachineState_Stuck
14750 || oldMachineState == MachineState_Starting
14751 || oldMachineState == MachineState_Stopping
14752 || oldMachineState == MachineState_Saving
14753 || oldMachineState == MachineState_Restoring
14754 || oldMachineState == MachineState_TeleportingPausedVM
14755 || oldMachineState == MachineState_TeleportingIn
14756 )
14757 && ( aMachineState == MachineState_PoweredOff
14758 || aMachineState == MachineState_Saved
14759 || aMachineState == MachineState_Teleported
14760 || aMachineState == MachineState_Aborted
14761 || aMachineState == MachineState_AbortedSaved
14762 )
14763 )
14764 {
14765 /* The EMT thread has just stopped, unlock attached media. Note that as
14766 * opposed to locking that is done from Console, we do unlocking here
14767 * because the VM process may have aborted before having a chance to
14768 * properly unlock all media it locked. */
14769
14770 unlockMedia();
14771 }
14772
14773 if (oldMachineState == MachineState_Restoring)
14774 {
14775 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14776 {
14777 /*
14778 * delete the saved state file once the machine has finished
14779 * restoring from it (note that Console sets the state from
14780 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14781 * to give the user an ability to fix an error and retry --
14782 * we keep the saved state file in this case)
14783 */
14784 deleteSavedState = true;
14785 }
14786 }
14787 else if ( oldMachineState == MachineState_Saved
14788 && ( aMachineState == MachineState_PoweredOff
14789 || aMachineState == MachineState_Teleported
14790 )
14791 )
14792 {
14793 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14794 deleteSavedState = true;
14795 mData->mCurrentStateModified = TRUE;
14796 stsFlags |= SaveSTS_CurStateModified;
14797 }
14798 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14799 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14800
14801 if ( aMachineState == MachineState_Starting
14802 || aMachineState == MachineState_Restoring
14803 || aMachineState == MachineState_TeleportingIn
14804 )
14805 {
14806 /* set the current state modified flag to indicate that the current
14807 * state is no more identical to the state in the
14808 * current snapshot */
14809 if (!mData->mCurrentSnapshot.isNull())
14810 {
14811 mData->mCurrentStateModified = TRUE;
14812 stsFlags |= SaveSTS_CurStateModified;
14813 }
14814 }
14815
14816 if (deleteSavedState)
14817 {
14818 if (mRemoveSavedState)
14819 {
14820 Assert(!mSSData->strStateFilePath.isEmpty());
14821
14822 // it is safe to delete the saved state file if ...
14823 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14824 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14825 // ... none of the snapshots share the saved state file
14826 )
14827 RTFileDelete(mSSData->strStateFilePath.c_str());
14828 }
14829
14830 mSSData->strStateFilePath.setNull();
14831 stsFlags |= SaveSTS_StateFilePath;
14832 }
14833
14834 /* redirect to the underlying peer machine */
14835 mPeer->i_setMachineState(aMachineState);
14836
14837 if ( oldMachineState != MachineState_RestoringSnapshot
14838 && ( aMachineState == MachineState_PoweredOff
14839 || aMachineState == MachineState_Teleported
14840 || aMachineState == MachineState_Aborted
14841 || aMachineState == MachineState_AbortedSaved
14842 || aMachineState == MachineState_Saved))
14843 {
14844 /* the machine has stopped execution
14845 * (or the saved state file was adopted) */
14846 stsFlags |= SaveSTS_StateTimeStamp;
14847 }
14848
14849 if ( ( oldMachineState == MachineState_PoweredOff
14850 || oldMachineState == MachineState_Aborted
14851 || oldMachineState == MachineState_Teleported
14852 )
14853 && aMachineState == MachineState_Saved)
14854 {
14855 /* the saved state file was adopted */
14856 Assert(!mSSData->strStateFilePath.isEmpty());
14857 stsFlags |= SaveSTS_StateFilePath;
14858 }
14859
14860#ifdef VBOX_WITH_GUEST_PROPS
14861 if ( aMachineState == MachineState_PoweredOff
14862 || aMachineState == MachineState_Aborted
14863 || aMachineState == MachineState_Teleported)
14864 {
14865 /* Make sure any transient guest properties get removed from the
14866 * property store on shutdown. */
14867 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14868
14869 /* remove it from the settings representation */
14870 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14871 for (settings::GuestPropertiesList::iterator
14872 it = llGuestProperties.begin();
14873 it != llGuestProperties.end();
14874 /*nothing*/)
14875 {
14876 const settings::GuestProperty &prop = *it;
14877 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14878 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14879 {
14880 it = llGuestProperties.erase(it);
14881 fNeedsSaving = true;
14882 }
14883 else
14884 {
14885 ++it;
14886 }
14887 }
14888
14889 /* Additionally remove it from the HWData representation. Required to
14890 * keep everything in sync, as this is what the API keeps using. */
14891 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14892 for (HWData::GuestPropertyMap::iterator
14893 it = llHWGuestProperties.begin();
14894 it != llHWGuestProperties.end();
14895 /*nothing*/)
14896 {
14897 uint32_t fFlags = it->second.mFlags;
14898 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14899 {
14900 /* iterator where we need to continue after the erase call
14901 * (C++03 is a fact still, and it doesn't return the iterator
14902 * which would allow continuing) */
14903 HWData::GuestPropertyMap::iterator it2 = it;
14904 ++it2;
14905 llHWGuestProperties.erase(it);
14906 it = it2;
14907 fNeedsSaving = true;
14908 }
14909 else
14910 {
14911 ++it;
14912 }
14913 }
14914
14915 if (fNeedsSaving)
14916 {
14917 mData->mCurrentStateModified = TRUE;
14918 stsFlags |= SaveSTS_CurStateModified;
14919 }
14920 }
14921#endif /* VBOX_WITH_GUEST_PROPS */
14922
14923 rc = i_saveStateSettings(stsFlags);
14924
14925 if ( ( oldMachineState != MachineState_PoweredOff
14926 && oldMachineState != MachineState_Aborted
14927 && oldMachineState != MachineState_Teleported
14928 )
14929 && ( aMachineState == MachineState_PoweredOff
14930 || aMachineState == MachineState_Aborted
14931 || aMachineState == MachineState_Teleported
14932 )
14933 )
14934 {
14935 /* we've been shut down for any reason */
14936 /* no special action so far */
14937 }
14938
14939 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
14940 LogFlowThisFuncLeave();
14941 return rc;
14942}
14943
14944/**
14945 * Sends the current machine state value to the VM process.
14946 *
14947 * @note Locks this object for reading, then calls a client process.
14948 */
14949HRESULT SessionMachine::i_updateMachineStateOnClient()
14950{
14951 AutoCaller autoCaller(this);
14952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14953
14954 ComPtr<IInternalSessionControl> directControl;
14955 {
14956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14957 AssertReturn(!!mData, E_FAIL);
14958 if (mData->mSession.mLockType == LockType_VM)
14959 directControl = mData->mSession.mDirectControl;
14960
14961 /* directControl may be already set to NULL here in #OnSessionEnd()
14962 * called too early by the direct session process while there is still
14963 * some operation (like deleting the snapshot) in progress. The client
14964 * process in this case is waiting inside Session::close() for the
14965 * "end session" process object to complete, while #uninit() called by
14966 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14967 * operation to complete. For now, we accept this inconsistent behavior
14968 * and simply do nothing here. */
14969
14970 if (mData->mSession.mState == SessionState_Unlocking)
14971 return S_OK;
14972 }
14973
14974 /* ignore notifications sent after #OnSessionEnd() is called */
14975 if (!directControl)
14976 return S_OK;
14977
14978 return directControl->UpdateMachineState(mData->mMachineState);
14979}
14980
14981
14982/*static*/
14983HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14984{
14985 va_list args;
14986 va_start(args, pcszMsg);
14987 HRESULT rc = setErrorInternalV(aResultCode,
14988 getStaticClassIID(),
14989 getStaticComponentName(),
14990 pcszMsg, args,
14991 false /* aWarning */,
14992 true /* aLogIt */);
14993 va_end(args);
14994 return rc;
14995}
14996
14997
14998HRESULT Machine::updateState(MachineState_T aState)
14999{
15000 NOREF(aState);
15001 ReturnComNotImplemented();
15002}
15003
15004HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15005{
15006 NOREF(aProgress);
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::endPowerUp(LONG aResult)
15011{
15012 NOREF(aResult);
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15017{
15018 NOREF(aProgress);
15019 ReturnComNotImplemented();
15020}
15021
15022HRESULT Machine::endPoweringDown(LONG aResult,
15023 const com::Utf8Str &aErrMsg)
15024{
15025 NOREF(aResult);
15026 NOREF(aErrMsg);
15027 ReturnComNotImplemented();
15028}
15029
15030HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15031 BOOL *aMatched,
15032 ULONG *aMaskedInterfaces)
15033{
15034 NOREF(aDevice);
15035 NOREF(aMatched);
15036 NOREF(aMaskedInterfaces);
15037 ReturnComNotImplemented();
15038
15039}
15040
15041HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15042{
15043 NOREF(aId); NOREF(aCaptureFilename);
15044 ReturnComNotImplemented();
15045}
15046
15047HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15048 BOOL aDone)
15049{
15050 NOREF(aId);
15051 NOREF(aDone);
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::autoCaptureUSBDevices()
15056{
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15061{
15062 NOREF(aDone);
15063 ReturnComNotImplemented();
15064}
15065
15066HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15067 ComPtr<IProgress> &aProgress)
15068{
15069 NOREF(aSession);
15070 NOREF(aProgress);
15071 ReturnComNotImplemented();
15072}
15073
15074HRESULT Machine::finishOnlineMergeMedium()
15075{
15076 ReturnComNotImplemented();
15077}
15078
15079HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15080 std::vector<com::Utf8Str> &aValues,
15081 std::vector<LONG64> &aTimestamps,
15082 std::vector<com::Utf8Str> &aFlags)
15083{
15084 NOREF(aNames);
15085 NOREF(aValues);
15086 NOREF(aTimestamps);
15087 NOREF(aFlags);
15088 ReturnComNotImplemented();
15089}
15090
15091HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15092 const com::Utf8Str &aValue,
15093 LONG64 aTimestamp,
15094 const com::Utf8Str &aFlags,
15095 BOOL fWasDeleted)
15096{
15097 NOREF(aName);
15098 NOREF(aValue);
15099 NOREF(aTimestamp);
15100 NOREF(aFlags);
15101 NOREF(fWasDeleted);
15102 ReturnComNotImplemented();
15103}
15104
15105HRESULT Machine::lockMedia()
15106{
15107 ReturnComNotImplemented();
15108}
15109
15110HRESULT Machine::unlockMedia()
15111{
15112 ReturnComNotImplemented();
15113}
15114
15115HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15116 ComPtr<IMediumAttachment> &aNewAttachment)
15117{
15118 NOREF(aAttachment);
15119 NOREF(aNewAttachment);
15120 ReturnComNotImplemented();
15121}
15122
15123HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15124 ULONG aCpuUser,
15125 ULONG aCpuKernel,
15126 ULONG aCpuIdle,
15127 ULONG aMemTotal,
15128 ULONG aMemFree,
15129 ULONG aMemBalloon,
15130 ULONG aMemShared,
15131 ULONG aMemCache,
15132 ULONG aPagedTotal,
15133 ULONG aMemAllocTotal,
15134 ULONG aMemFreeTotal,
15135 ULONG aMemBalloonTotal,
15136 ULONG aMemSharedTotal,
15137 ULONG aVmNetRx,
15138 ULONG aVmNetTx)
15139{
15140 NOREF(aValidStats);
15141 NOREF(aCpuUser);
15142 NOREF(aCpuKernel);
15143 NOREF(aCpuIdle);
15144 NOREF(aMemTotal);
15145 NOREF(aMemFree);
15146 NOREF(aMemBalloon);
15147 NOREF(aMemShared);
15148 NOREF(aMemCache);
15149 NOREF(aPagedTotal);
15150 NOREF(aMemAllocTotal);
15151 NOREF(aMemFreeTotal);
15152 NOREF(aMemBalloonTotal);
15153 NOREF(aMemSharedTotal);
15154 NOREF(aVmNetRx);
15155 NOREF(aVmNetTx);
15156 ReturnComNotImplemented();
15157}
15158
15159HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15160 com::Utf8Str &aResult)
15161{
15162 NOREF(aAuthParams);
15163 NOREF(aResult);
15164 ReturnComNotImplemented();
15165}
15166
15167com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15168{
15169 com::Utf8Str strControllerName = "Unknown";
15170 switch (aBusType)
15171 {
15172 case StorageBus_IDE:
15173 {
15174 strControllerName = "IDE";
15175 break;
15176 }
15177 case StorageBus_SATA:
15178 {
15179 strControllerName = "SATA";
15180 break;
15181 }
15182 case StorageBus_SCSI:
15183 {
15184 strControllerName = "SCSI";
15185 break;
15186 }
15187 case StorageBus_Floppy:
15188 {
15189 strControllerName = "Floppy";
15190 break;
15191 }
15192 case StorageBus_SAS:
15193 {
15194 strControllerName = "SAS";
15195 break;
15196 }
15197 case StorageBus_USB:
15198 {
15199 strControllerName = "USB";
15200 break;
15201 }
15202 default:
15203 break;
15204 }
15205 return strControllerName;
15206}
15207
15208HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15209{
15210 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15211
15212 AutoCaller autoCaller(this);
15213 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15214
15215 HRESULT rc = S_OK;
15216
15217 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15218 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15219 rc = getUSBDeviceFilters(usbDeviceFilters);
15220 if (FAILED(rc)) return rc;
15221
15222 NOREF(aFlags);
15223 com::Utf8Str osTypeId;
15224 ComObjPtr<GuestOSType> osType = NULL;
15225
15226 /* Get the guest os type as a string from the VB. */
15227 rc = getOSTypeId(osTypeId);
15228 if (FAILED(rc)) return rc;
15229
15230 /* Get the os type obj that coresponds, can be used to get
15231 * the defaults for this guest OS. */
15232 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15233 if (FAILED(rc)) return rc;
15234
15235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15236
15237 /* Let the OS type select 64-bit ness. */
15238 mHWData->mLongMode = osType->i_is64Bit()
15239 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15240
15241 /* Let the OS type enable the X2APIC */
15242 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15243
15244 /* This one covers IOAPICEnabled. */
15245 mBIOSSettings->i_applyDefaults(osType);
15246
15247 /* Initialize default record settings. */
15248 mRecordingSettings->i_applyDefaults();
15249
15250 /* Initialize default BIOS settings here */
15251 /* Hardware virtualization must be ON by default */
15252 mHWData->mAPIC = true;
15253 mHWData->mHWVirtExEnabled = true;
15254
15255 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15256 if (FAILED(rc)) return rc;
15257
15258 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15259 if (FAILED(rc)) return rc;
15260
15261 /* Graphics stuff. */
15262 GraphicsControllerType_T graphicsController;
15263 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15264 if (FAILED(rc)) return rc;
15265
15266 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15267 if (FAILED(rc)) return rc;
15268
15269 ULONG vramSize;
15270 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15271 if (FAILED(rc)) return rc;
15272
15273 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15274 if (FAILED(rc)) return rc;
15275
15276 BOOL fAccelerate2DVideoEnabled;
15277 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15278 if (FAILED(rc)) return rc;
15279
15280 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15281 if (FAILED(rc)) return rc;
15282
15283 BOOL fAccelerate3DEnabled;
15284 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15285 if (FAILED(rc)) return rc;
15286
15287 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15288 if (FAILED(rc)) return rc;
15289
15290 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15291 if (FAILED(rc)) return rc;
15292
15293 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15294 if (FAILED(rc)) return rc;
15295
15296 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15297 if (FAILED(rc)) return rc;
15298
15299 BOOL mRTCUseUTC;
15300 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15301 if (FAILED(rc)) return rc;
15302
15303 setRTCUseUTC(mRTCUseUTC);
15304 if (FAILED(rc)) return rc;
15305
15306 /* the setter does more than just the assignment, so use it */
15307 ChipsetType_T enmChipsetType;
15308 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15309 if (FAILED(rc)) return rc;
15310
15311 rc = COMSETTER(ChipsetType)(enmChipsetType);
15312 if (FAILED(rc)) return rc;
15313
15314 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15315 if (FAILED(rc)) return rc;
15316
15317 /* Apply IOMMU defaults. */
15318 IommuType_T enmIommuType;
15319 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15320 if (FAILED(rc)) return rc;
15321
15322 rc = COMSETTER(IommuType)(enmIommuType);
15323 if (FAILED(rc)) return rc;
15324
15325 /* Apply network adapters defaults */
15326 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15327 mNetworkAdapters[slot]->i_applyDefaults(osType);
15328
15329 /* Apply serial port defaults */
15330 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15331 mSerialPorts[slot]->i_applyDefaults(osType);
15332
15333 /* Apply parallel port defaults - not OS dependent*/
15334 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15335 mParallelPorts[slot]->i_applyDefaults();
15336
15337 /* This one covers the TPM type. */
15338 mTrustedPlatformModule->i_applyDefaults(osType);
15339
15340 /* This one covers secure boot. */
15341 rc = mNvramStore->i_applyDefaults(osType);
15342 if (FAILED(rc)) return rc;
15343
15344 /* Audio stuff. */
15345 AudioControllerType_T audioController;
15346 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15347 if (FAILED(rc)) return rc;
15348
15349 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15350 if (FAILED(rc)) return rc;
15351
15352 AudioCodecType_T audioCodec;
15353 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15354 if (FAILED(rc)) return rc;
15355
15356 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15357 if (FAILED(rc)) return rc;
15358
15359 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15360 if (FAILED(rc)) return rc;
15361
15362 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15363 if (FAILED(rc)) return rc;
15364
15365 /* Storage Controllers */
15366 StorageControllerType_T hdStorageControllerType;
15367 StorageBus_T hdStorageBusType;
15368 StorageControllerType_T dvdStorageControllerType;
15369 StorageBus_T dvdStorageBusType;
15370 BOOL recommendedFloppy;
15371 ComPtr<IStorageController> floppyController;
15372 ComPtr<IStorageController> hdController;
15373 ComPtr<IStorageController> dvdController;
15374 Utf8Str strFloppyName, strDVDName, strHDName;
15375
15376 /* GUI auto generates controller names using bus type. Do the same*/
15377 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15378
15379 /* Floppy recommended? add one. */
15380 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15381 if (FAILED(rc)) return rc;
15382 if (recommendedFloppy)
15383 {
15384 rc = addStorageController(strFloppyName,
15385 StorageBus_Floppy,
15386 floppyController);
15387 if (FAILED(rc)) return rc;
15388 }
15389
15390 /* Setup one DVD storage controller. */
15391 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15392 if (FAILED(rc)) return rc;
15393
15394 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15395 if (FAILED(rc)) return rc;
15396
15397 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15398
15399 rc = addStorageController(strDVDName,
15400 dvdStorageBusType,
15401 dvdController);
15402 if (FAILED(rc)) return rc;
15403
15404 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15405 if (FAILED(rc)) return rc;
15406
15407 /* Setup one HDD storage controller. */
15408 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15409 if (FAILED(rc)) return rc;
15410
15411 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15412 if (FAILED(rc)) return rc;
15413
15414 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15415
15416 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15417 {
15418 rc = addStorageController(strHDName,
15419 hdStorageBusType,
15420 hdController);
15421 if (FAILED(rc)) return rc;
15422
15423 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15424 if (FAILED(rc)) return rc;
15425 }
15426 else
15427 {
15428 /* The HD controller is the same as DVD: */
15429 hdController = dvdController;
15430 }
15431
15432 /* Limit the AHCI port count if it's used because windows has trouble with
15433 * too many ports and other guest (OS X in particular) may take extra long
15434 * boot: */
15435
15436 // pParent = static_cast<Medium*>(aP)
15437 IStorageController *temp = hdController;
15438 ComObjPtr<StorageController> storageController;
15439 storageController = static_cast<StorageController *>(temp);
15440
15441 // tempHDController = aHDController;
15442 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15443 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15444 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15445 storageController->COMSETTER(PortCount)(1);
15446
15447 /* USB stuff */
15448
15449 bool ohciEnabled = false;
15450
15451 ComPtr<IUSBController> usbController;
15452 BOOL recommendedUSB3;
15453 BOOL recommendedUSB;
15454 BOOL usbProxyAvailable;
15455
15456 getUSBProxyAvailable(&usbProxyAvailable);
15457 if (FAILED(rc)) return rc;
15458
15459 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15460 if (FAILED(rc)) return rc;
15461 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15462 if (FAILED(rc)) return rc;
15463
15464 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15465 {
15466#ifdef VBOX_WITH_EXTPACK
15467 /* USB 3.0 is only available if the proper ExtPack is installed. */
15468 ExtPackManager *aManager = mParent->i_getExtPackManager();
15469 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15470 {
15471 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15472 if (FAILED(rc)) return rc;
15473
15474 /* xHci includes OHCI */
15475 ohciEnabled = true;
15476 }
15477#endif
15478 }
15479 if ( !ohciEnabled
15480 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15481 {
15482 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15483 if (FAILED(rc)) return rc;
15484 ohciEnabled = true;
15485
15486#ifdef VBOX_WITH_EXTPACK
15487 /* USB 2.0 is only available if the proper ExtPack is installed.
15488 * Note. Configuring EHCI here and providing messages about
15489 * the missing extpack isn't exactly clean, but it is a
15490 * necessary evil to patch over legacy compatability issues
15491 * introduced by the new distribution model. */
15492 ExtPackManager *manager = mParent->i_getExtPackManager();
15493 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15494 {
15495 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15496 if (FAILED(rc)) return rc;
15497 }
15498#endif
15499 }
15500
15501 /* Set recommended human interface device types: */
15502 BOOL recommendedUSBHID;
15503 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15504 if (FAILED(rc)) return rc;
15505
15506 if (recommendedUSBHID)
15507 {
15508 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15509 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15510 if (!ohciEnabled && !usbDeviceFilters.isNull())
15511 {
15512 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15513 if (FAILED(rc)) return rc;
15514 }
15515 }
15516
15517 BOOL recommendedUSBTablet;
15518 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15519 if (FAILED(rc)) return rc;
15520
15521 if (recommendedUSBTablet)
15522 {
15523 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15524 if (!ohciEnabled && !usbDeviceFilters.isNull())
15525 {
15526 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15527 if (FAILED(rc)) return rc;
15528 }
15529 }
15530
15531 /* Enable the VMMDev testing feature for bootsector VMs: */
15532 if (osTypeId == "VBoxBS_64")
15533 {
15534 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15535 if (FAILED(rc))
15536 return rc;
15537 }
15538
15539 return S_OK;
15540}
15541
15542/* This isn't handled entirely by the wrapper generator yet. */
15543#ifdef VBOX_WITH_XPCOM
15544NS_DECL_CLASSINFO(SessionMachine)
15545NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15546
15547NS_DECL_CLASSINFO(SnapshotMachine)
15548NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15549#endif
Note: See TracBrowser for help on using the repository browser.

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