VirtualBox

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

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

Main: Guest Properties: improved property name and value validation, bugref:10185.

This commit also prevents guest properties loss if they were set while VM was running.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 542.5 KB
Line 
1/* $Id: MachineImpl.cpp 93891 2022-02-22 18:08:39Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63#include "StringifyEnums.h"
64
65#include <iprt/asm.h>
66#include <iprt/path.h>
67#include <iprt/dir.h>
68#include <iprt/env.h>
69#include <iprt/lockvalidator.h>
70#include <iprt/process.h>
71#include <iprt/cpp/utils.h>
72#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
73#include <iprt/sha.h>
74#include <iprt/string.h>
75#include <iprt/ctype.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/VMMDev.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#ifdef VBOX_WITH_SHARED_CLIPBOARD
92# include <VBox/HostServices/VBoxClipboardSvc.h>
93#endif
94
95#include "VBox/com/MultiResult.h"
96
97#include <algorithm>
98
99#ifdef VBOX_WITH_DTRACE_R3_MAIN
100# include "dtrace/VBoxAPI.h"
101#endif
102
103#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
104# define HOSTSUFF_EXE ".exe"
105#else /* !RT_OS_WINDOWS */
106# define HOSTSUFF_EXE ""
107#endif /* !RT_OS_WINDOWS */
108
109// defines / prototypes
110/////////////////////////////////////////////////////////////////////////////
111
112/////////////////////////////////////////////////////////////////////////////
113// Machine::Data structure
114/////////////////////////////////////////////////////////////////////////////
115
116Machine::Data::Data()
117{
118 mRegistered = FALSE;
119 pMachineConfigFile = NULL;
120 /* Contains hints on what has changed when the user is using the VM (config
121 * changes, running the VM, ...). This is used to decide if a config needs
122 * to be written to disk. */
123 flModifications = 0;
124 /* VM modification usually also trigger setting the current state to
125 * "Modified". Although this is not always the case. An e.g. is the VM
126 * initialization phase or when snapshot related data is changed. The
127 * actually behavior is controlled by the following flag. */
128 m_fAllowStateModification = false;
129 mAccessible = FALSE;
130 /* mUuid is initialized in Machine::init() */
131
132 mMachineState = MachineState_PoweredOff;
133 RTTimeNow(&mLastStateChange);
134
135 mMachineStateDeps = 0;
136 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
137 mMachineStateChangePending = 0;
138
139 mCurrentStateModified = TRUE;
140 mGuestPropertiesModified = FALSE;
141
142 mSession.mPID = NIL_RTPROCESS;
143 mSession.mLockType = LockType_Null;
144 mSession.mState = SessionState_Unlocked;
145}
146
147Machine::Data::~Data()
148{
149 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
150 {
151 RTSemEventMultiDestroy(mMachineStateDepsSem);
152 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
153 }
154 if (pMachineConfigFile)
155 {
156 delete pMachineConfigFile;
157 pMachineConfigFile = NULL;
158 }
159}
160
161/////////////////////////////////////////////////////////////////////////////
162// Machine::HWData structure
163/////////////////////////////////////////////////////////////////////////////
164
165Machine::HWData::HWData()
166{
167 /* default values for a newly created machine */
168 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
169 mMemorySize = 128;
170 mCPUCount = 1;
171 mCPUHotPlugEnabled = false;
172 mMemoryBalloonSize = 0;
173 mPageFusionEnabled = false;
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
177 mHWVirtExVPIDEnabled = true;
178 mHWVirtExUXEnabled = true;
179 mHWVirtExForceEnabled = false;
180 mHWVirtExUseNativeApi = false;
181 mHWVirtExVirtVmsaveVmload = true;
182#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
183 mPAEEnabled = true;
184#else
185 mPAEEnabled = false;
186#endif
187 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
188 mTripleFaultReset = false;
189 mAPIC = true;
190 mX2APIC = false;
191 mIBPBOnVMExit = false;
192 mIBPBOnVMEntry = false;
193 mSpecCtrl = false;
194 mSpecCtrlByHost = false;
195 mL1DFlushOnSched = true;
196 mL1DFlushOnVMEntry = false;
197 mMDSClearOnSched = true;
198 mMDSClearOnVMEntry = false;
199 mNestedHWVirt = false;
200 mHPETEnabled = false;
201 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
202 mCpuIdPortabilityLevel = 0;
203 mCpuProfile = "host";
204
205 /* default boot order: floppy - DVD - HDD */
206 mBootOrder[0] = DeviceType_Floppy;
207 mBootOrder[1] = DeviceType_DVD;
208 mBootOrder[2] = DeviceType_HardDisk;
209 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
210 mBootOrder[i] = DeviceType_Null;
211
212 mClipboardMode = ClipboardMode_Disabled;
213 mClipboardFileTransfersEnabled = FALSE;
214
215 mDnDMode = DnDMode_Disabled;
216
217 mFirmwareType = FirmwareType_BIOS;
218 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
219 mPointingHIDType = PointingHIDType_PS2Mouse;
220 mChipsetType = ChipsetType_PIIX3;
221 mIommuType = IommuType_None;
222 mParavirtProvider = ParavirtProvider_Default;
223 mEmulatedUSBCardReaderEnabled = FALSE;
224
225 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
226 mCPUAttached[i] = false;
227
228 mIOCacheEnabled = true;
229 mIOCacheSize = 5; /* 5MB */
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param strOsType OS Type string (stored as is if aOsType is NULL).
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
284 * scheme (includes the UUID).
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 const Utf8Str &strOsType,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 if (llGroups.size())
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Let the OS type select 64-bit ness. */
347 mHWData->mLongMode = aOsType->i_is64Bit()
348 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
349
350 /* Let the OS type enable the X2APIC */
351 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
352
353 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
354 AssertComRC(rc);
355 }
356 else if (!strOsType.isEmpty())
357 {
358 /* Store OS type */
359 mUserData->s.strOsType = strOsType;
360
361 /* No guest OS type object. Pick some plausible defaults which the
362 * host can handle. There's no way to know or validate anything. */
363 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 mHWData->mX2APIC = false;
365 }
366
367 /* Apply BIOS defaults. */
368 mBIOSSettings->i_applyDefaults(aOsType);
369
370 /* Apply TPM defaults. */
371 mTrustedPlatformModule->i_applyDefaults(aOsType);
372
373 /* Apply record defaults. */
374 mRecordingSettings->i_applyDefaults();
375
376 /* Apply network adapters defaults */
377 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
378 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
379
380 /* Apply serial port defaults */
381 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
382 mSerialPorts[slot]->i_applyDefaults(aOsType);
383
384 /* Apply parallel port defaults */
385 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
386 mParallelPorts[slot]->i_applyDefaults();
387
388 /* Enable the VMMDev testing feature for bootsector VMs: */
389 if (aOsType && aOsType->i_id() == "VBoxBS_64")
390 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
391
392 /* At this point the changing of the current state modification
393 * flag is allowed. */
394 i_allowStateModification();
395
396 /* commit all changes made during the initialization */
397 i_commit();
398 }
399
400 /* Confirm a successful initialization when it's the case */
401 if (SUCCEEDED(rc))
402 {
403 if (mData->mAccessible)
404 autoInitSpan.setSucceeded();
405 else
406 autoInitSpan.setLimited();
407 }
408
409 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
410 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
411 mData->mRegistered,
412 mData->mAccessible,
413 rc));
414
415 LogFlowThisFuncLeave();
416
417 return rc;
418}
419
420/**
421 * Initializes a new instance with data from machine XML (formerly Init_Registered).
422 * Gets called in two modes:
423 *
424 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
425 * UUID is specified and we mark the machine as "registered";
426 *
427 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
428 * and the machine remains unregistered until RegisterMachine() is called.
429 *
430 * @param aParent Associated parent object
431 * @param strConfigFile Local file system path to the VM settings file (can
432 * be relative to the VirtualBox config directory).
433 * @param aId UUID of the machine or NULL (see above).
434 *
435 * @return Success indicator. if not S_OK, the machine object is invalid
436 */
437HRESULT Machine::initFromSettings(VirtualBox *aParent,
438 const Utf8Str &strConfigFile,
439 const Guid *aId)
440{
441 LogFlowThisFuncEnter();
442 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
443
444 /* Enclose the state transition NotReady->InInit->Ready */
445 AutoInitSpan autoInitSpan(this);
446 AssertReturn(autoInitSpan.isOk(), E_FAIL);
447
448 HRESULT rc = initImpl(aParent, strConfigFile);
449 if (FAILED(rc)) return rc;
450
451 if (aId)
452 {
453 // loading a registered VM:
454 unconst(mData->mUuid) = *aId;
455 mData->mRegistered = TRUE;
456 // now load the settings from XML:
457 rc = i_registeredInit();
458 // this calls initDataAndChildObjects() and loadSettings()
459 }
460 else
461 {
462 // opening an unregistered VM (VirtualBox::OpenMachine()):
463 rc = initDataAndChildObjects();
464
465 if (SUCCEEDED(rc))
466 {
467 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
468 mData->mAccessible = TRUE;
469
470 try
471 {
472 // load and parse machine XML; this will throw on XML or logic errors
473 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
474
475 // reject VM UUID duplicates, they can happen if someone
476 // tries to register an already known VM config again
477 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
478 true /* fPermitInaccessible */,
479 false /* aDoSetError */,
480 NULL) != VBOX_E_OBJECT_NOT_FOUND)
481 {
482 throw setError(E_FAIL,
483 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
484 mData->m_strConfigFile.c_str());
485 }
486
487 // use UUID from machine config
488 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
489
490 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
491 NULL /* puuidRegistry */);
492 if (FAILED(rc)) throw rc;
493
494 /* At this point the changing of the current state modification
495 * flag is allowed. */
496 i_allowStateModification();
497
498 i_commit();
499 }
500 catch (HRESULT err)
501 {
502 /* we assume that error info is set by the thrower */
503 rc = err;
504 }
505 catch (...)
506 {
507 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
508 }
509 }
510 }
511
512 /* Confirm a successful initialization when it's the case */
513 if (SUCCEEDED(rc))
514 {
515 if (mData->mAccessible)
516 autoInitSpan.setSucceeded();
517 else
518 {
519 autoInitSpan.setLimited();
520
521 // uninit media from this machine's media registry, or else
522 // reloading the settings will fail
523 mParent->i_unregisterMachineMedia(i_getId());
524 }
525 }
526
527 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
528 "rc=%08X\n",
529 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
530 mData->mRegistered, mData->mAccessible, rc));
531
532 LogFlowThisFuncLeave();
533
534 return rc;
535}
536
537/**
538 * Initializes a new instance from a machine config that is already in memory
539 * (import OVF case). Since we are importing, the UUID in the machine
540 * config is ignored and we always generate a fresh one.
541 *
542 * @param aParent Associated parent object.
543 * @param strName Name for the new machine; this overrides what is specified in config.
544 * @param strSettingsFilename File name of .vbox file.
545 * @param config Machine configuration loaded and parsed from XML.
546 *
547 * @return Success indicator. if not S_OK, the machine object is invalid
548 */
549HRESULT Machine::init(VirtualBox *aParent,
550 const Utf8Str &strName,
551 const Utf8Str &strSettingsFilename,
552 const settings::MachineConfigFile &config)
553{
554 LogFlowThisFuncEnter();
555
556 /* Enclose the state transition NotReady->InInit->Ready */
557 AutoInitSpan autoInitSpan(this);
558 AssertReturn(autoInitSpan.isOk(), E_FAIL);
559
560 HRESULT rc = initImpl(aParent, strSettingsFilename);
561 if (FAILED(rc)) return rc;
562
563 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
564 if (FAILED(rc)) return rc;
565
566 rc = initDataAndChildObjects();
567
568 if (SUCCEEDED(rc))
569 {
570 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
571 mData->mAccessible = TRUE;
572
573 // create empty machine config for instance data
574 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
575
576 // generate fresh UUID, ignore machine config
577 unconst(mData->mUuid).create();
578
579 rc = i_loadMachineDataFromSettings(config,
580 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
581
582 // override VM name as well, it may be different
583 mUserData->s.strName = strName;
584
585 if (SUCCEEDED(rc))
586 {
587 /* At this point the changing of the current state modification
588 * flag is allowed. */
589 i_allowStateModification();
590
591 /* commit all changes made during the initialization */
592 i_commit();
593 }
594 }
595
596 /* Confirm a successful initialization when it's the case */
597 if (SUCCEEDED(rc))
598 {
599 if (mData->mAccessible)
600 autoInitSpan.setSucceeded();
601 else
602 {
603 /* Ignore all errors from unregistering, they would destroy
604- * the more interesting error information we already have,
605- * pinpointing the issue with the VM config. */
606 ErrorInfoKeeper eik;
607
608 autoInitSpan.setLimited();
609
610 // uninit media from this machine's media registry, or else
611 // reloading the settings will fail
612 mParent->i_unregisterMachineMedia(i_getId());
613 }
614 }
615
616 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
617 "rc=%08X\n",
618 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
619 mData->mRegistered, mData->mAccessible, rc));
620
621 LogFlowThisFuncLeave();
622
623 return rc;
624}
625
626/**
627 * Shared code between the various init() implementations.
628 * @param aParent The VirtualBox object.
629 * @param strConfigFile Settings file.
630 * @return
631 */
632HRESULT Machine::initImpl(VirtualBox *aParent,
633 const Utf8Str &strConfigFile)
634{
635 LogFlowThisFuncEnter();
636
637 AssertReturn(aParent, E_INVALIDARG);
638 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
639
640 HRESULT rc = S_OK;
641
642 /* share the parent weakly */
643 unconst(mParent) = aParent;
644
645 /* allocate the essential machine data structure (the rest will be
646 * allocated later by initDataAndChildObjects() */
647 mData.allocate();
648
649 /* memorize the config file name (as provided) */
650 mData->m_strConfigFile = strConfigFile;
651
652 /* get the full file name */
653 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
654 if (RT_FAILURE(vrc1))
655 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
656 tr("Invalid machine settings file name '%s' (%Rrc)"),
657 strConfigFile.c_str(),
658 vrc1);
659
660 LogFlowThisFuncLeave();
661
662 return rc;
663}
664
665/**
666 * Tries to create a machine settings file in the path stored in the machine
667 * instance data. Used when a new machine is created to fail gracefully if
668 * the settings file could not be written (e.g. because machine dir is read-only).
669 * @return
670 */
671HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
672{
673 HRESULT rc = S_OK;
674
675 // when we create a new machine, we must be able to create the settings file
676 RTFILE f = NIL_RTFILE;
677 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
678 if ( RT_SUCCESS(vrc)
679 || vrc == VERR_SHARING_VIOLATION
680 )
681 {
682 if (RT_SUCCESS(vrc))
683 RTFileClose(f);
684 if (!fForceOverwrite)
685 rc = setError(VBOX_E_FILE_ERROR,
686 tr("Machine settings file '%s' already exists"),
687 mData->m_strConfigFileFull.c_str());
688 else
689 {
690 /* try to delete the config file, as otherwise the creation
691 * of a new settings file will fail. */
692 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
693 if (RT_FAILURE(vrc2))
694 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
695 tr("Could not delete the existing settings file '%s' (%Rrc)"),
696 mData->m_strConfigFileFull.c_str(), vrc2);
697 }
698 }
699 else if ( vrc != VERR_FILE_NOT_FOUND
700 && vrc != VERR_PATH_NOT_FOUND
701 )
702 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
703 tr("Invalid machine settings file name '%s' (%Rrc)"),
704 mData->m_strConfigFileFull.c_str(),
705 vrc);
706 return rc;
707}
708
709/**
710 * Initializes the registered machine by loading the settings file.
711 * This method is separated from #init() in order to make it possible to
712 * retry the operation after VirtualBox startup instead of refusing to
713 * startup the whole VirtualBox server in case if the settings file of some
714 * registered VM is invalid or inaccessible.
715 *
716 * @note Must be always called from this object's write lock
717 * (unless called from #init() that doesn't need any locking).
718 * @note Locks the mUSBController method for writing.
719 * @note Subclasses must not call this method.
720 */
721HRESULT Machine::i_registeredInit()
722{
723 AssertReturn(!i_isSessionMachine(), E_FAIL);
724 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
725 AssertReturn(mData->mUuid.isValid(), E_FAIL);
726 AssertReturn(!mData->mAccessible, E_FAIL);
727
728 HRESULT rc = initDataAndChildObjects();
729
730 if (SUCCEEDED(rc))
731 {
732 /* Temporarily reset the registered flag in order to let setters
733 * potentially called from loadSettings() succeed (isMutable() used in
734 * all setters will return FALSE for a Machine instance if mRegistered
735 * is TRUE). */
736 mData->mRegistered = FALSE;
737
738 try
739 {
740 // load and parse machine XML; this will throw on XML or logic errors
741 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
742
743 if (mData->mUuid != mData->pMachineConfigFile->uuid)
744 throw setError(E_FAIL,
745 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
746 mData->pMachineConfigFile->uuid.raw(),
747 mData->m_strConfigFileFull.c_str(),
748 mData->mUuid.toString().c_str(),
749 mParent->i_settingsFilePath().c_str());
750
751 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
752 NULL /* const Guid *puuidRegistry */);
753 if (FAILED(rc)) throw rc;
754 }
755 catch (HRESULT err)
756 {
757 /* we assume that error info is set by the thrower */
758 rc = err;
759 }
760 catch (...)
761 {
762 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
763 }
764
765 /* Restore the registered flag (even on failure) */
766 mData->mRegistered = TRUE;
767 }
768
769 if (SUCCEEDED(rc))
770 {
771 /* Set mAccessible to TRUE only if we successfully locked and loaded
772 * the settings file */
773 mData->mAccessible = TRUE;
774
775 /* commit all changes made during loading the settings file */
776 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
777 /// @todo r=klaus for some reason the settings loading logic backs up
778 // the settings, and therefore a commit is needed. Should probably be changed.
779 }
780 else
781 {
782 /* If the machine is registered, then, instead of returning a
783 * failure, we mark it as inaccessible and set the result to
784 * success to give it a try later */
785
786 /* fetch the current error info */
787 mData->mAccessError = com::ErrorInfo();
788 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
789
790 /* rollback all changes */
791 i_rollback(false /* aNotify */);
792
793 // uninit media from this machine's media registry, or else
794 // reloading the settings will fail
795 mParent->i_unregisterMachineMedia(i_getId());
796
797 /* uninitialize the common part to make sure all data is reset to
798 * default (null) values */
799 uninitDataAndChildObjects();
800
801 rc = S_OK;
802 }
803
804 return rc;
805}
806
807/**
808 * Uninitializes the instance.
809 * Called either from FinalRelease() or by the parent when it gets destroyed.
810 *
811 * @note The caller of this method must make sure that this object
812 * a) doesn't have active callers on the current thread and b) is not locked
813 * by the current thread; otherwise uninit() will hang either a) due to
814 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
815 * a dead-lock caused by this thread waiting for all callers on the other
816 * threads are done but preventing them from doing so by holding a lock.
817 */
818void Machine::uninit()
819{
820 LogFlowThisFuncEnter();
821
822 Assert(!isWriteLockOnCurrentThread());
823
824 Assert(!uRegistryNeedsSaving);
825 if (uRegistryNeedsSaving)
826 {
827 AutoCaller autoCaller(this);
828 if (SUCCEEDED(autoCaller.rc()))
829 {
830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
831 i_saveSettings(NULL, alock, Machine::SaveS_Force);
832 }
833 }
834
835 /* Enclose the state transition Ready->InUninit->NotReady */
836 AutoUninitSpan autoUninitSpan(this);
837 if (autoUninitSpan.uninitDone())
838 return;
839
840 Assert(!i_isSnapshotMachine());
841 Assert(!i_isSessionMachine());
842 Assert(!!mData);
843
844 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
845 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
846
847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
848
849 if (!mData->mSession.mMachine.isNull())
850 {
851 /* Theoretically, this can only happen if the VirtualBox server has been
852 * terminated while there were clients running that owned open direct
853 * sessions. Since in this case we are definitely called by
854 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
855 * won't happen on the client watcher thread (because it has a
856 * VirtualBox caller for the duration of the
857 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
858 * cannot happen until the VirtualBox caller is released). This is
859 * important, because SessionMachine::uninit() cannot correctly operate
860 * after we return from this method (it expects the Machine instance is
861 * still valid). We'll call it ourselves below.
862 */
863 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
864 (SessionMachine*)mData->mSession.mMachine));
865
866 if (Global::IsOnlineOrTransient(mData->mMachineState))
867 {
868 Log1WarningThisFunc(("Setting state to Aborted!\n"));
869 /* set machine state using SessionMachine reimplementation */
870 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
871 }
872
873 /*
874 * Uninitialize SessionMachine using public uninit() to indicate
875 * an unexpected uninitialization.
876 */
877 mData->mSession.mMachine->uninit();
878 /* SessionMachine::uninit() must set mSession.mMachine to null */
879 Assert(mData->mSession.mMachine.isNull());
880 }
881
882 // uninit media from this machine's media registry, if they're still there
883 Guid uuidMachine(i_getId());
884
885 /* the lock is no more necessary (SessionMachine is uninitialized) */
886 alock.release();
887
888 /* XXX This will fail with
889 * "cannot be closed because it is still attached to 1 virtual machines"
890 * because at this point we did not call uninitDataAndChildObjects() yet
891 * and therefore also removeBackReference() for all these mediums was not called! */
892
893 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
894 mParent->i_unregisterMachineMedia(uuidMachine);
895
896 // has machine been modified?
897 if (mData->flModifications)
898 {
899 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
900 i_rollback(false /* aNotify */);
901 }
902
903 if (mData->mAccessible)
904 uninitDataAndChildObjects();
905
906 /* free the essential data structure last */
907 mData.free();
908
909 LogFlowThisFuncLeave();
910}
911
912// Wrapped IMachine properties
913/////////////////////////////////////////////////////////////////////////////
914HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
915{
916 /* mParent is constant during life time, no need to lock */
917 ComObjPtr<VirtualBox> pVirtualBox(mParent);
918 aParent = pVirtualBox;
919
920 return S_OK;
921}
922
923
924HRESULT Machine::getAccessible(BOOL *aAccessible)
925{
926 /* In some cases (medium registry related), it is necessary to be able to
927 * go through the list of all machines. Happens when an inaccessible VM
928 * has a sensible medium registry. */
929 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
931
932 HRESULT rc = S_OK;
933
934 if (!mData->mAccessible)
935 {
936 /* try to initialize the VM once more if not accessible */
937
938 AutoReinitSpan autoReinitSpan(this);
939 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
940
941#ifdef DEBUG
942 LogFlowThisFunc(("Dumping media backreferences\n"));
943 mParent->i_dumpAllBackRefs();
944#endif
945
946 if (mData->pMachineConfigFile)
947 {
948 // reset the XML file to force loadSettings() (called from i_registeredInit())
949 // to parse it again; the file might have changed
950 delete mData->pMachineConfigFile;
951 mData->pMachineConfigFile = NULL;
952 }
953
954 rc = i_registeredInit();
955
956 if (SUCCEEDED(rc) && mData->mAccessible)
957 {
958 autoReinitSpan.setSucceeded();
959
960 /* make sure interesting parties will notice the accessibility
961 * state change */
962 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
963 mParent->i_onMachineDataChanged(mData->mUuid);
964 }
965 }
966
967 if (SUCCEEDED(rc))
968 *aAccessible = mData->mAccessible;
969
970 LogFlowThisFuncLeave();
971
972 return rc;
973}
974
975HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
976{
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
980 {
981 /* return shortly */
982 aAccessError = NULL;
983 return S_OK;
984 }
985
986 HRESULT rc = S_OK;
987
988 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
989 rc = errorInfo.createObject();
990 if (SUCCEEDED(rc))
991 {
992 errorInfo->init(mData->mAccessError.getResultCode(),
993 mData->mAccessError.getInterfaceID().ref(),
994 Utf8Str(mData->mAccessError.getComponent()).c_str(),
995 Utf8Str(mData->mAccessError.getText()));
996 aAccessError = errorInfo;
997 }
998
999 return rc;
1000}
1001
1002HRESULT Machine::getName(com::Utf8Str &aName)
1003{
1004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1005
1006 aName = mUserData->s.strName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::setName(const com::Utf8Str &aName)
1012{
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 i_setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1033{
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 aDescription = mUserData->s.strDescription;
1037
1038 return S_OK;
1039}
1040
1041HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1042{
1043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 // this can be done in principle in any state as it doesn't affect the VM
1046 // significantly, but play safe by not messing around while complex
1047 // activities are going on
1048 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1049 if (FAILED(rc)) return rc;
1050
1051 i_setModified(IsModified_MachineData);
1052 mUserData.backup();
1053 mUserData->s.strDescription = aDescription;
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::getId(com::Guid &aId)
1059{
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 aId = mData->mUuid;
1063
1064 return S_OK;
1065}
1066
1067HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1068{
1069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1070 aGroups.resize(mUserData->s.llGroups.size());
1071 size_t i = 0;
1072 for (StringsList::const_iterator
1073 it = mUserData->s.llGroups.begin();
1074 it != mUserData->s.llGroups.end();
1075 ++it, ++i)
1076 aGroups[i] = (*it);
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1082{
1083 StringsList llGroups;
1084 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1085 if (FAILED(rc))
1086 return rc;
1087
1088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1089
1090 rc = i_checkStateDependency(MutableOrSavedStateDep);
1091 if (FAILED(rc)) return rc;
1092
1093 i_setModified(IsModified_MachineData);
1094 mUserData.backup();
1095 mUserData->s.llGroups = llGroups;
1096
1097 return S_OK;
1098}
1099
1100HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1101{
1102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 aOSTypeId = mUserData->s.strOsType;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1110{
1111 /* look up the object by Id to check it is valid */
1112 ComObjPtr<GuestOSType> pGuestOSType;
1113 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1114
1115 /* when setting, always use the "etalon" value for consistency -- lookup
1116 * by ID is case-insensitive and the input value may have different case */
1117 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1118
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mUserData.backup();
1126 mUserData->s.strOsType = osTypeId;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aFirmwareType = mHWData->mFirmwareType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mFirmwareType = aFirmwareType;
1150 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1151 alock.release();
1152
1153 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aPointingHIDType = mHWData->mPointingHIDType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 i_setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mPointingHIDType = aPointingHIDType;
1200
1201 return S_OK;
1202}
1203
1204HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1205{
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *aChipsetType = mHWData->mChipsetType;
1209
1210 return S_OK;
1211}
1212
1213HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1214{
1215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1216
1217 HRESULT rc = i_checkStateDependency(MutableStateDep);
1218 if (FAILED(rc)) return rc;
1219
1220 if (aChipsetType != mHWData->mChipsetType)
1221 {
1222 i_setModified(IsModified_MachineData);
1223 mHWData.backup();
1224 mHWData->mChipsetType = aChipsetType;
1225
1226 // Resize network adapter array, to be finalized on commit/rollback.
1227 // We must not throw away entries yet, otherwise settings are lost
1228 // without a way to roll back.
1229 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1230 size_t oldCount = mNetworkAdapters.size();
1231 if (newCount > oldCount)
1232 {
1233 mNetworkAdapters.resize(newCount);
1234 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1235 {
1236 unconst(mNetworkAdapters[slot]).createObject();
1237 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1238 }
1239 }
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aIommuType = mHWData->mIommuType;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setIommuType(IommuType_T aIommuType)
1255{
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = i_checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aIommuType != mHWData->mIommuType)
1262 {
1263 if (aIommuType == IommuType_Intel)
1264 {
1265#ifndef VBOX_WITH_IOMMU_INTEL
1266 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1267 return E_UNEXPECTED;
1268#endif
1269 }
1270
1271 i_setModified(IsModified_MachineData);
1272 mHWData.backup();
1273 mHWData->mIommuType = aIommuType;
1274 }
1275
1276 return S_OK;
1277}
1278
1279HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1280{
1281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1282
1283 aParavirtDebug = mHWData->mParavirtDebug;
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1288{
1289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1290
1291 HRESULT rc = i_checkStateDependency(MutableStateDep);
1292 if (FAILED(rc)) return rc;
1293
1294 /** @todo Parse/validate options? */
1295 if (aParavirtDebug != mHWData->mParavirtDebug)
1296 {
1297 i_setModified(IsModified_MachineData);
1298 mHWData.backup();
1299 mHWData->mParavirtDebug = aParavirtDebug;
1300 }
1301
1302 return S_OK;
1303}
1304
1305HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1306{
1307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 *aParavirtProvider = mHWData->mParavirtProvider;
1310
1311 return S_OK;
1312}
1313
1314HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1315{
1316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1317
1318 HRESULT rc = i_checkStateDependency(MutableStateDep);
1319 if (FAILED(rc)) return rc;
1320
1321 if (aParavirtProvider != mHWData->mParavirtProvider)
1322 {
1323 i_setModified(IsModified_MachineData);
1324 mHWData.backup();
1325 mHWData->mParavirtProvider = aParavirtProvider;
1326 }
1327
1328 return S_OK;
1329}
1330
1331HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1332{
1333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 *aParavirtProvider = mHWData->mParavirtProvider;
1336 switch (mHWData->mParavirtProvider)
1337 {
1338 case ParavirtProvider_None:
1339 case ParavirtProvider_HyperV:
1340 case ParavirtProvider_KVM:
1341 case ParavirtProvider_Minimal:
1342 break;
1343
1344 /* Resolve dynamic provider types to the effective types. */
1345 default:
1346 {
1347 ComObjPtr<GuestOSType> pGuestOSType;
1348 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1349 pGuestOSType);
1350 if (FAILED(hrc2) || pGuestOSType.isNull())
1351 {
1352 *aParavirtProvider = ParavirtProvider_None;
1353 break;
1354 }
1355
1356 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1357 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1358
1359 switch (mHWData->mParavirtProvider)
1360 {
1361 case ParavirtProvider_Legacy:
1362 {
1363 if (fOsXGuest)
1364 *aParavirtProvider = ParavirtProvider_Minimal;
1365 else
1366 *aParavirtProvider = ParavirtProvider_None;
1367 break;
1368 }
1369
1370 case ParavirtProvider_Default:
1371 {
1372 if (fOsXGuest)
1373 *aParavirtProvider = ParavirtProvider_Minimal;
1374 else if ( mUserData->s.strOsType == "Windows11_64"
1375 || mUserData->s.strOsType == "Windows10"
1376 || mUserData->s.strOsType == "Windows10_64"
1377 || mUserData->s.strOsType == "Windows81"
1378 || mUserData->s.strOsType == "Windows81_64"
1379 || mUserData->s.strOsType == "Windows8"
1380 || mUserData->s.strOsType == "Windows8_64"
1381 || mUserData->s.strOsType == "Windows7"
1382 || mUserData->s.strOsType == "Windows7_64"
1383 || mUserData->s.strOsType == "WindowsVista"
1384 || mUserData->s.strOsType == "WindowsVista_64"
1385 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1386 || mUserData->s.strOsType.startsWith("Windows201"))
1387 && mUserData->s.strOsType.endsWith("_64"))
1388 || mUserData->s.strOsType == "Windows2012"
1389 || mUserData->s.strOsType == "Windows2012_64"
1390 || mUserData->s.strOsType == "Windows2008"
1391 || mUserData->s.strOsType == "Windows2008_64")
1392 {
1393 *aParavirtProvider = ParavirtProvider_HyperV;
1394 }
1395 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1396 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1397 || mUserData->s.strOsType == "Linux"
1398 || mUserData->s.strOsType == "Linux_64"
1399 || mUserData->s.strOsType == "ArchLinux"
1400 || mUserData->s.strOsType == "ArchLinux_64"
1401 || mUserData->s.strOsType == "Debian"
1402 || mUserData->s.strOsType == "Debian_64"
1403 || mUserData->s.strOsType == "Fedora"
1404 || mUserData->s.strOsType == "Fedora_64"
1405 || mUserData->s.strOsType == "Gentoo"
1406 || mUserData->s.strOsType == "Gentoo_64"
1407 || mUserData->s.strOsType == "Mandriva"
1408 || mUserData->s.strOsType == "Mandriva_64"
1409 || mUserData->s.strOsType == "OpenSUSE"
1410 || mUserData->s.strOsType == "OpenSUSE_64"
1411 || mUserData->s.strOsType == "Oracle"
1412 || mUserData->s.strOsType == "Oracle_64"
1413 || mUserData->s.strOsType == "RedHat"
1414 || mUserData->s.strOsType == "RedHat_64"
1415 || mUserData->s.strOsType == "Turbolinux"
1416 || mUserData->s.strOsType == "Turbolinux_64"
1417 || mUserData->s.strOsType == "Ubuntu"
1418 || mUserData->s.strOsType == "Ubuntu_64"
1419 || mUserData->s.strOsType == "Xandros"
1420 || mUserData->s.strOsType == "Xandros_64")
1421 {
1422 *aParavirtProvider = ParavirtProvider_KVM;
1423 }
1424 else
1425 *aParavirtProvider = ParavirtProvider_None;
1426 break;
1427 }
1428
1429 default: AssertFailedBreak(); /* Shut up MSC. */
1430 }
1431 break;
1432 }
1433 }
1434
1435 Assert( *aParavirtProvider == ParavirtProvider_None
1436 || *aParavirtProvider == ParavirtProvider_Minimal
1437 || *aParavirtProvider == ParavirtProvider_HyperV
1438 || *aParavirtProvider == ParavirtProvider_KVM);
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 aHardwareVersion = mHWData->mHWVersion;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1452{
1453 /* check known version */
1454 Utf8Str hwVersion = aHardwareVersion;
1455 if ( hwVersion.compare("1") != 0
1456 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1457 return setError(E_INVALIDARG,
1458 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1459
1460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 HRESULT rc = i_checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mHWVersion = aHardwareVersion;
1468
1469 return S_OK;
1470}
1471
1472HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1473{
1474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1475
1476 if (!mHWData->mHardwareUUID.isZero())
1477 aHardwareUUID = mHWData->mHardwareUUID;
1478 else
1479 aHardwareUUID = mData->mUuid;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1485{
1486 if (!aHardwareUUID.isValid())
1487 return E_INVALIDARG;
1488
1489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1490
1491 HRESULT rc = i_checkStateDependency(MutableStateDep);
1492 if (FAILED(rc)) return rc;
1493
1494 i_setModified(IsModified_MachineData);
1495 mHWData.backup();
1496 if (aHardwareUUID == mData->mUuid)
1497 mHWData->mHardwareUUID.clear();
1498 else
1499 mHWData->mHardwareUUID = aHardwareUUID;
1500
1501 return S_OK;
1502}
1503
1504HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1505{
1506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 *aMemorySize = mHWData->mMemorySize;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::setMemorySize(ULONG aMemorySize)
1514{
1515 /* check RAM limits */
1516 if ( aMemorySize < MM_RAM_MIN_IN_MB
1517 || aMemorySize > MM_RAM_MAX_IN_MB
1518 )
1519 return setError(E_INVALIDARG,
1520 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1521 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1522
1523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 HRESULT rc = i_checkStateDependency(MutableStateDep);
1526 if (FAILED(rc)) return rc;
1527
1528 i_setModified(IsModified_MachineData);
1529 mHWData.backup();
1530 mHWData->mMemorySize = aMemorySize;
1531
1532 return S_OK;
1533}
1534
1535HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1536{
1537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 *aCPUCount = mHWData->mCPUCount;
1540
1541 return S_OK;
1542}
1543
1544HRESULT Machine::setCPUCount(ULONG aCPUCount)
1545{
1546 /* check CPU limits */
1547 if ( aCPUCount < SchemaDefs::MinCPUCount
1548 || aCPUCount > SchemaDefs::MaxCPUCount
1549 )
1550 return setError(E_INVALIDARG,
1551 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1552 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1553
1554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1557 if (mHWData->mCPUHotPlugEnabled)
1558 {
1559 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1560 {
1561 if (mHWData->mCPUAttached[idx])
1562 return setError(E_INVALIDARG,
1563 tr("There is still a CPU attached to socket %lu."
1564 "Detach the CPU before removing the socket"),
1565 aCPUCount, idx+1);
1566 }
1567 }
1568
1569 HRESULT rc = i_checkStateDependency(MutableStateDep);
1570 if (FAILED(rc)) return rc;
1571
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574 mHWData->mCPUCount = aCPUCount;
1575
1576 return S_OK;
1577}
1578
1579HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1580{
1581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1582
1583 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1584
1585 return S_OK;
1586}
1587
1588HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1589{
1590 HRESULT rc = S_OK;
1591
1592 /* check throttle limits */
1593 if ( aCPUExecutionCap < 1
1594 || aCPUExecutionCap > 100
1595 )
1596 return setError(E_INVALIDARG,
1597 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1598 aCPUExecutionCap, 1, 100);
1599
1600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 alock.release();
1603 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1604 alock.acquire();
1605 if (FAILED(rc)) return rc;
1606
1607 i_setModified(IsModified_MachineData);
1608 mHWData.backup();
1609 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1610
1611 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1612 if (Global::IsOnline(mData->mMachineState))
1613 i_saveSettings(NULL, alock);
1614
1615 return S_OK;
1616}
1617
1618HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1619{
1620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1621
1622 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1623
1624 return S_OK;
1625}
1626
1627HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1628{
1629 HRESULT rc = S_OK;
1630
1631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1632
1633 rc = i_checkStateDependency(MutableStateDep);
1634 if (FAILED(rc)) return rc;
1635
1636 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1637 {
1638 if (aCPUHotPlugEnabled)
1639 {
1640 i_setModified(IsModified_MachineData);
1641 mHWData.backup();
1642
1643 /* Add the amount of CPUs currently attached */
1644 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1645 mHWData->mCPUAttached[i] = true;
1646 }
1647 else
1648 {
1649 /*
1650 * We can disable hotplug only if the amount of maximum CPUs is equal
1651 * to the amount of attached CPUs
1652 */
1653 unsigned cCpusAttached = 0;
1654 unsigned iHighestId = 0;
1655
1656 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1657 {
1658 if (mHWData->mCPUAttached[i])
1659 {
1660 cCpusAttached++;
1661 iHighestId = i;
1662 }
1663 }
1664
1665 if ( (cCpusAttached != mHWData->mCPUCount)
1666 || (iHighestId >= mHWData->mCPUCount))
1667 return setError(E_INVALIDARG,
1668 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1669
1670 i_setModified(IsModified_MachineData);
1671 mHWData.backup();
1672 }
1673 }
1674
1675 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1676
1677 return rc;
1678}
1679
1680HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1681{
1682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1683
1684 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1685
1686 return S_OK;
1687}
1688
1689HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1690{
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1694 if (SUCCEEDED(hrc))
1695 {
1696 i_setModified(IsModified_MachineData);
1697 mHWData.backup();
1698 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1699 }
1700 return hrc;
1701}
1702
1703HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1704{
1705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1706 aCPUProfile = mHWData->mCpuProfile;
1707 return S_OK;
1708}
1709
1710HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1711{
1712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1713 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1714 if (SUCCEEDED(hrc))
1715 {
1716 i_setModified(IsModified_MachineData);
1717 mHWData.backup();
1718 /* Empty equals 'host'. */
1719 if (aCPUProfile.isNotEmpty())
1720 mHWData->mCpuProfile = aCPUProfile;
1721 else
1722 mHWData->mCpuProfile = "host";
1723 }
1724 return hrc;
1725}
1726
1727HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1728{
1729#ifdef VBOX_WITH_USB_CARDREADER
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1733
1734 return S_OK;
1735#else
1736 NOREF(aEmulatedUSBCardReaderEnabled);
1737 return E_NOTIMPL;
1738#endif
1739}
1740
1741HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1742{
1743#ifdef VBOX_WITH_USB_CARDREADER
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1747 if (FAILED(rc)) return rc;
1748
1749 i_setModified(IsModified_MachineData);
1750 mHWData.backup();
1751 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1752
1753 return S_OK;
1754#else
1755 NOREF(aEmulatedUSBCardReaderEnabled);
1756 return E_NOTIMPL;
1757#endif
1758}
1759
1760HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763
1764 *aHPETEnabled = mHWData->mHPETEnabled;
1765
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1770{
1771 HRESULT rc = S_OK;
1772
1773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1774
1775 rc = i_checkStateDependency(MutableStateDep);
1776 if (FAILED(rc)) return rc;
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780
1781 mHWData->mHPETEnabled = aHPETEnabled;
1782
1783 return rc;
1784}
1785
1786/** @todo this method should not be public */
1787HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1788{
1789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1790
1791 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1792
1793 return S_OK;
1794}
1795
1796/**
1797 * Set the memory balloon size.
1798 *
1799 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1800 * we have to make sure that we never call IGuest from here.
1801 */
1802HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1803{
1804 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1805#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1806 /* check limits */
1807 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1808 return setError(E_INVALIDARG,
1809 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1810 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1811
1812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1813
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1817
1818 return S_OK;
1819#else
1820 NOREF(aMemoryBalloonSize);
1821 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1822#endif
1823}
1824
1825HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1826{
1827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1830 return S_OK;
1831}
1832
1833HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1834{
1835#ifdef VBOX_WITH_PAGE_SHARING
1836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1839 i_setModified(IsModified_MachineData);
1840 mHWData.backup();
1841 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1842 return S_OK;
1843#else
1844 NOREF(aPageFusionEnabled);
1845 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1846#endif
1847}
1848
1849HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1850{
1851 /* mBIOSSettings is constant during life time, no need to lock */
1852 aBIOSSettings = mBIOSSettings;
1853
1854 return S_OK;
1855}
1856
1857HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1858{
1859 /* mTrustedPlatformModule is constant during life time, no need to lock */
1860 aTrustedPlatformModule = mTrustedPlatformModule;
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1866{
1867 /* mNvramStore is constant during life time, no need to lock */
1868 aNvramStore = mNvramStore;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 aRecordingSettings = mRecordingSettings;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 aGraphicsAdapter = mGraphicsAdapter;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1892{
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 switch (aProperty)
1896 {
1897 case CPUPropertyType_PAE:
1898 *aValue = mHWData->mPAEEnabled;
1899 break;
1900
1901 case CPUPropertyType_LongMode:
1902 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1903 *aValue = TRUE;
1904 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1905 *aValue = FALSE;
1906#if HC_ARCH_BITS == 64
1907 else
1908 *aValue = TRUE;
1909#else
1910 else
1911 {
1912 *aValue = FALSE;
1913
1914 ComObjPtr<GuestOSType> pGuestOSType;
1915 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1916 pGuestOSType);
1917 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1918 {
1919 if (pGuestOSType->i_is64Bit())
1920 {
1921 ComObjPtr<Host> pHost = mParent->i_host();
1922 alock.release();
1923
1924 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1925 if (FAILED(hrc2))
1926 *aValue = FALSE;
1927 }
1928 }
1929 }
1930#endif
1931 break;
1932
1933 case CPUPropertyType_TripleFaultReset:
1934 *aValue = mHWData->mTripleFaultReset;
1935 break;
1936
1937 case CPUPropertyType_APIC:
1938 *aValue = mHWData->mAPIC;
1939 break;
1940
1941 case CPUPropertyType_X2APIC:
1942 *aValue = mHWData->mX2APIC;
1943 break;
1944
1945 case CPUPropertyType_IBPBOnVMExit:
1946 *aValue = mHWData->mIBPBOnVMExit;
1947 break;
1948
1949 case CPUPropertyType_IBPBOnVMEntry:
1950 *aValue = mHWData->mIBPBOnVMEntry;
1951 break;
1952
1953 case CPUPropertyType_SpecCtrl:
1954 *aValue = mHWData->mSpecCtrl;
1955 break;
1956
1957 case CPUPropertyType_SpecCtrlByHost:
1958 *aValue = mHWData->mSpecCtrlByHost;
1959 break;
1960
1961 case CPUPropertyType_HWVirt:
1962 *aValue = mHWData->mNestedHWVirt;
1963 break;
1964
1965 case CPUPropertyType_L1DFlushOnEMTScheduling:
1966 *aValue = mHWData->mL1DFlushOnSched;
1967 break;
1968
1969 case CPUPropertyType_L1DFlushOnVMEntry:
1970 *aValue = mHWData->mL1DFlushOnVMEntry;
1971 break;
1972
1973 case CPUPropertyType_MDSClearOnEMTScheduling:
1974 *aValue = mHWData->mMDSClearOnSched;
1975 break;
1976
1977 case CPUPropertyType_MDSClearOnVMEntry:
1978 *aValue = mHWData->mMDSClearOnVMEntry;
1979 break;
1980
1981 default:
1982 return E_INVALIDARG;
1983 }
1984 return S_OK;
1985}
1986
1987HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1988{
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 HRESULT rc = i_checkStateDependency(MutableStateDep);
1992 if (FAILED(rc)) return rc;
1993
1994 switch (aProperty)
1995 {
1996 case CPUPropertyType_PAE:
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mPAEEnabled = !!aValue;
2000 break;
2001
2002 case CPUPropertyType_LongMode:
2003 i_setModified(IsModified_MachineData);
2004 mHWData.backup();
2005 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2006 break;
2007
2008 case CPUPropertyType_TripleFaultReset:
2009 i_setModified(IsModified_MachineData);
2010 mHWData.backup();
2011 mHWData->mTripleFaultReset = !!aValue;
2012 break;
2013
2014 case CPUPropertyType_APIC:
2015 if (mHWData->mX2APIC)
2016 aValue = TRUE;
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mAPIC = !!aValue;
2020 break;
2021
2022 case CPUPropertyType_X2APIC:
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mX2APIC = !!aValue;
2026 if (aValue)
2027 mHWData->mAPIC = !!aValue;
2028 break;
2029
2030 case CPUPropertyType_IBPBOnVMExit:
2031 i_setModified(IsModified_MachineData);
2032 mHWData.backup();
2033 mHWData->mIBPBOnVMExit = !!aValue;
2034 break;
2035
2036 case CPUPropertyType_IBPBOnVMEntry:
2037 i_setModified(IsModified_MachineData);
2038 mHWData.backup();
2039 mHWData->mIBPBOnVMEntry = !!aValue;
2040 break;
2041
2042 case CPUPropertyType_SpecCtrl:
2043 i_setModified(IsModified_MachineData);
2044 mHWData.backup();
2045 mHWData->mSpecCtrl = !!aValue;
2046 break;
2047
2048 case CPUPropertyType_SpecCtrlByHost:
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mSpecCtrlByHost = !!aValue;
2052 break;
2053
2054 case CPUPropertyType_HWVirt:
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mNestedHWVirt = !!aValue;
2058 break;
2059
2060 case CPUPropertyType_L1DFlushOnEMTScheduling:
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mL1DFlushOnSched = !!aValue;
2064 break;
2065
2066 case CPUPropertyType_L1DFlushOnVMEntry:
2067 i_setModified(IsModified_MachineData);
2068 mHWData.backup();
2069 mHWData->mL1DFlushOnVMEntry = !!aValue;
2070 break;
2071
2072 case CPUPropertyType_MDSClearOnEMTScheduling:
2073 i_setModified(IsModified_MachineData);
2074 mHWData.backup();
2075 mHWData->mMDSClearOnSched = !!aValue;
2076 break;
2077
2078 case CPUPropertyType_MDSClearOnVMEntry:
2079 i_setModified(IsModified_MachineData);
2080 mHWData.backup();
2081 mHWData->mMDSClearOnVMEntry = !!aValue;
2082 break;
2083
2084 default:
2085 return E_INVALIDARG;
2086 }
2087 return S_OK;
2088}
2089
2090HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2091 ULONG *aValEcx, ULONG *aValEdx)
2092{
2093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2094 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2095 {
2096 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2097 it != mHWData->mCpuIdLeafList.end();
2098 ++it)
2099 {
2100 if (aOrdinal == 0)
2101 {
2102 const settings::CpuIdLeaf &rLeaf= *it;
2103 *aIdx = rLeaf.idx;
2104 *aSubIdx = rLeaf.idxSub;
2105 *aValEax = rLeaf.uEax;
2106 *aValEbx = rLeaf.uEbx;
2107 *aValEcx = rLeaf.uEcx;
2108 *aValEdx = rLeaf.uEdx;
2109 return S_OK;
2110 }
2111 aOrdinal--;
2112 }
2113 }
2114 return E_INVALIDARG;
2115}
2116
2117HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 /*
2122 * Search the list.
2123 */
2124 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2125 {
2126 const settings::CpuIdLeaf &rLeaf= *it;
2127 if ( rLeaf.idx == aIdx
2128 && ( aSubIdx == UINT32_MAX
2129 || rLeaf.idxSub == aSubIdx) )
2130 {
2131 *aValEax = rLeaf.uEax;
2132 *aValEbx = rLeaf.uEbx;
2133 *aValEcx = rLeaf.uEcx;
2134 *aValEdx = rLeaf.uEdx;
2135 return S_OK;
2136 }
2137 }
2138
2139 return E_INVALIDARG;
2140}
2141
2142
2143HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2144{
2145 /*
2146 * Validate input before taking locks and checking state.
2147 */
2148 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2149 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2150 if ( aIdx >= UINT32_C(0x20)
2151 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2152 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2153 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2154
2155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2156 HRESULT rc = i_checkStateDependency(MutableStateDep);
2157 if (FAILED(rc)) return rc;
2158
2159 /*
2160 * Impose a maximum number of leaves.
2161 */
2162 if (mHWData->mCpuIdLeafList.size() > 256)
2163 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2164
2165 /*
2166 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2167 */
2168 i_setModified(IsModified_MachineData);
2169 mHWData.backup();
2170
2171 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2172 {
2173 settings::CpuIdLeaf &rLeaf= *it;
2174 if ( rLeaf.idx == aIdx
2175 && ( aSubIdx == UINT32_MAX
2176 || rLeaf.idxSub == aSubIdx) )
2177 it = mHWData->mCpuIdLeafList.erase(it);
2178 else
2179 ++it;
2180 }
2181
2182 settings::CpuIdLeaf NewLeaf;
2183 NewLeaf.idx = aIdx;
2184 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2185 NewLeaf.uEax = aValEax;
2186 NewLeaf.uEbx = aValEbx;
2187 NewLeaf.uEcx = aValEcx;
2188 NewLeaf.uEdx = aValEdx;
2189 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2190 return S_OK;
2191}
2192
2193HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2194{
2195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 HRESULT rc = i_checkStateDependency(MutableStateDep);
2198 if (FAILED(rc)) return rc;
2199
2200 /*
2201 * Do the removal.
2202 */
2203 bool fModified = mHWData.isBackedUp();
2204 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2205 {
2206 settings::CpuIdLeaf &rLeaf= *it;
2207 if ( rLeaf.idx == aIdx
2208 && ( aSubIdx == UINT32_MAX
2209 || rLeaf.idxSub == aSubIdx) )
2210 {
2211 if (!fModified)
2212 {
2213 fModified = true;
2214 i_setModified(IsModified_MachineData);
2215 mHWData.backup();
2216 // Start from the beginning, since mHWData.backup() creates
2217 // a new list, causing iterator mixup. This makes sure that
2218 // the settings are not unnecessarily marked as modified,
2219 // at the price of extra list walking.
2220 it = mHWData->mCpuIdLeafList.begin();
2221 }
2222 else
2223 it = mHWData->mCpuIdLeafList.erase(it);
2224 }
2225 else
2226 ++it;
2227 }
2228
2229 return S_OK;
2230}
2231
2232HRESULT Machine::removeAllCPUIDLeaves()
2233{
2234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2235
2236 HRESULT rc = i_checkStateDependency(MutableStateDep);
2237 if (FAILED(rc)) return rc;
2238
2239 if (mHWData->mCpuIdLeafList.size() > 0)
2240 {
2241 i_setModified(IsModified_MachineData);
2242 mHWData.backup();
2243
2244 mHWData->mCpuIdLeafList.clear();
2245 }
2246
2247 return S_OK;
2248}
2249HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2250{
2251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2252
2253 switch(aProperty)
2254 {
2255 case HWVirtExPropertyType_Enabled:
2256 *aValue = mHWData->mHWVirtExEnabled;
2257 break;
2258
2259 case HWVirtExPropertyType_VPID:
2260 *aValue = mHWData->mHWVirtExVPIDEnabled;
2261 break;
2262
2263 case HWVirtExPropertyType_NestedPaging:
2264 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2265 break;
2266
2267 case HWVirtExPropertyType_UnrestrictedExecution:
2268 *aValue = mHWData->mHWVirtExUXEnabled;
2269 break;
2270
2271 case HWVirtExPropertyType_LargePages:
2272 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2273 break;
2274
2275 case HWVirtExPropertyType_Force:
2276 *aValue = mHWData->mHWVirtExForceEnabled;
2277 break;
2278
2279 case HWVirtExPropertyType_UseNativeApi:
2280 *aValue = mHWData->mHWVirtExUseNativeApi;
2281 break;
2282
2283 case HWVirtExPropertyType_VirtVmsaveVmload:
2284 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2285 break;
2286
2287 default:
2288 return E_INVALIDARG;
2289 }
2290 return S_OK;
2291}
2292
2293HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2294{
2295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 HRESULT rc = i_checkStateDependency(MutableStateDep);
2298 if (FAILED(rc)) return rc;
2299
2300 switch (aProperty)
2301 {
2302 case HWVirtExPropertyType_Enabled:
2303 i_setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mHWVirtExEnabled = !!aValue;
2306 break;
2307
2308 case HWVirtExPropertyType_VPID:
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2312 break;
2313
2314 case HWVirtExPropertyType_NestedPaging:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2318 break;
2319
2320 case HWVirtExPropertyType_UnrestrictedExecution:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mHWVirtExUXEnabled = !!aValue;
2324 break;
2325
2326 case HWVirtExPropertyType_LargePages:
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2330 break;
2331
2332 case HWVirtExPropertyType_Force:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mHWVirtExForceEnabled = !!aValue;
2336 break;
2337
2338 case HWVirtExPropertyType_UseNativeApi:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mHWVirtExUseNativeApi = !!aValue;
2342 break;
2343
2344 case HWVirtExPropertyType_VirtVmsaveVmload:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2348 break;
2349
2350 default:
2351 return E_INVALIDARG;
2352 }
2353
2354 return S_OK;
2355}
2356
2357HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2358{
2359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2362
2363 return S_OK;
2364}
2365
2366HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2367{
2368 /** @todo (r=dmik):
2369 * 1. Allow to change the name of the snapshot folder containing snapshots
2370 * 2. Rename the folder on disk instead of just changing the property
2371 * value (to be smart and not to leave garbage). Note that it cannot be
2372 * done here because the change may be rolled back. Thus, the right
2373 * place is #saveSettings().
2374 */
2375
2376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2377
2378 HRESULT rc = i_checkStateDependency(MutableStateDep);
2379 if (FAILED(rc)) return rc;
2380
2381 if (!mData->mCurrentSnapshot.isNull())
2382 return setError(E_FAIL,
2383 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2384
2385 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2386
2387 if (strSnapshotFolder.isEmpty())
2388 strSnapshotFolder = "Snapshots";
2389 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2390 if (RT_FAILURE(vrc))
2391 return setErrorBoth(E_FAIL, vrc,
2392 tr("Invalid snapshot folder '%s' (%Rrc)"),
2393 strSnapshotFolder.c_str(), vrc);
2394
2395 i_setModified(IsModified_MachineData);
2396 mUserData.backup();
2397
2398 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2404{
2405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 aMediumAttachments.resize(mMediumAttachments->size());
2408 size_t i = 0;
2409 for (MediumAttachmentList::const_iterator
2410 it = mMediumAttachments->begin();
2411 it != mMediumAttachments->end();
2412 ++it, ++i)
2413 aMediumAttachments[i] = *it;
2414
2415 return S_OK;
2416}
2417
2418HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2419{
2420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 Assert(!!mVRDEServer);
2423
2424 aVRDEServer = mVRDEServer;
2425
2426 return S_OK;
2427}
2428
2429HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2430{
2431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2432
2433 aAudioAdapter = mAudioAdapter;
2434
2435 return S_OK;
2436}
2437
2438HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2439{
2440#ifdef VBOX_WITH_VUSB
2441 clearError();
2442 MultiResult rc(S_OK);
2443
2444# ifdef VBOX_WITH_USB
2445 rc = mParent->i_host()->i_checkUSBProxyService();
2446 if (FAILED(rc)) return rc;
2447# endif
2448
2449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2450
2451 aUSBControllers.resize(mUSBControllers->size());
2452 size_t i = 0;
2453 for (USBControllerList::const_iterator
2454 it = mUSBControllers->begin();
2455 it != mUSBControllers->end();
2456 ++it, ++i)
2457 aUSBControllers[i] = *it;
2458
2459 return S_OK;
2460#else
2461 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2462 * extended error info to indicate that USB is simply not available
2463 * (w/o treating it as a failure), for example, as in OSE */
2464 NOREF(aUSBControllers);
2465 ReturnComNotImplemented();
2466#endif /* VBOX_WITH_VUSB */
2467}
2468
2469HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2470{
2471#ifdef VBOX_WITH_VUSB
2472 clearError();
2473 MultiResult rc(S_OK);
2474
2475# ifdef VBOX_WITH_USB
2476 rc = mParent->i_host()->i_checkUSBProxyService();
2477 if (FAILED(rc)) return rc;
2478# endif
2479
2480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 aUSBDeviceFilters = mUSBDeviceFilters;
2483 return rc;
2484#else
2485 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2486 * extended error info to indicate that USB is simply not available
2487 * (w/o treating it as a failure), for example, as in OSE */
2488 NOREF(aUSBDeviceFilters);
2489 ReturnComNotImplemented();
2490#endif /* VBOX_WITH_VUSB */
2491}
2492
2493HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2494{
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 aSettingsFilePath = mData->m_strConfigFileFull;
2498
2499 return S_OK;
2500}
2501
2502HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2503{
2504 RT_NOREF(aSettingsFilePath);
2505 ReturnComNotImplemented();
2506}
2507
2508HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2509{
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2513 if (FAILED(rc)) return rc;
2514
2515 if (!mData->pMachineConfigFile->fileExists())
2516 // this is a new machine, and no config file exists yet:
2517 *aSettingsModified = TRUE;
2518 else
2519 *aSettingsModified = (mData->flModifications != 0);
2520
2521 return S_OK;
2522}
2523
2524HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2525{
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 *aSessionState = mData->mSession.mState;
2529
2530 return S_OK;
2531}
2532
2533HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2534{
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 aSessionName = mData->mSession.mName;
2538
2539 return S_OK;
2540}
2541
2542HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2543{
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 *aSessionPID = mData->mSession.mPID;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getState(MachineState_T *aState)
2552{
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555 *aState = mData->mMachineState;
2556 Assert(mData->mMachineState != MachineState_Null);
2557
2558 return S_OK;
2559}
2560
2561HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2562{
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2566
2567 return S_OK;
2568}
2569
2570HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 aStateFilePath = mSSData->strStateFilePath;
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 i_getLogFolder(aLogFolder);
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 aCurrentSnapshot = mData->mCurrentSnapshot;
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2598{
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2602 ? 0
2603 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2604
2605 return S_OK;
2606}
2607
2608HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2609{
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 /* Note: for machines with no snapshots, we always return FALSE
2613 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2614 * reasons :) */
2615
2616 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2617 ? FALSE
2618 : mData->mCurrentStateModified;
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2624{
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aSharedFolders.resize(mHWData->mSharedFolders.size());
2628 size_t i = 0;
2629 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2630 it = mHWData->mSharedFolders.begin();
2631 it != mHWData->mSharedFolders.end();
2632 ++it, ++i)
2633 aSharedFolders[i] = *it;
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 *aClipboardMode = mHWData->mClipboardMode;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2648{
2649 HRESULT rc = S_OK;
2650
2651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 alock.release();
2654 rc = i_onClipboardModeChange(aClipboardMode);
2655 alock.acquire();
2656 if (FAILED(rc)) return rc;
2657
2658 i_setModified(IsModified_MachineData);
2659 mHWData.backup();
2660 mHWData->mClipboardMode = aClipboardMode;
2661
2662 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2663 if (Global::IsOnline(mData->mMachineState))
2664 i_saveSettings(NULL, alock);
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2679{
2680 HRESULT rc = S_OK;
2681
2682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 alock.release();
2685 rc = i_onClipboardFileTransferModeChange(aEnabled);
2686 alock.acquire();
2687 if (FAILED(rc)) return rc;
2688
2689 i_setModified(IsModified_MachineData);
2690 mHWData.backup();
2691 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2692
2693 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2694 if (Global::IsOnline(mData->mMachineState))
2695 i_saveSettings(NULL, alock);
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aDnDMode = mHWData->mDnDMode;
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2710{
2711 HRESULT rc = S_OK;
2712
2713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 alock.release();
2716 rc = i_onDnDModeChange(aDnDMode);
2717
2718 alock.acquire();
2719 if (FAILED(rc)) return rc;
2720
2721 i_setModified(IsModified_MachineData);
2722 mHWData.backup();
2723 mHWData->mDnDMode = aDnDMode;
2724
2725 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2726 if (Global::IsOnline(mData->mMachineState))
2727 i_saveSettings(NULL, alock);
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2733{
2734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 aStorageControllers.resize(mStorageControllers->size());
2737 size_t i = 0;
2738 for (StorageControllerList::const_iterator
2739 it = mStorageControllers->begin();
2740 it != mStorageControllers->end();
2741 ++it, ++i)
2742 aStorageControllers[i] = *it;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *aEnabled = mUserData->s.fTeleporterEnabled;
2752
2753 return S_OK;
2754}
2755
2756HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2757{
2758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 /* Only allow it to be set to true when PoweredOff or Aborted.
2761 (Clearing it is always permitted.) */
2762 if ( aTeleporterEnabled
2763 && mData->mRegistered
2764 && ( !i_isSessionMachine()
2765 || ( mData->mMachineState != MachineState_PoweredOff
2766 && mData->mMachineState != MachineState_Teleported
2767 && mData->mMachineState != MachineState_Aborted
2768 )
2769 )
2770 )
2771 return setError(VBOX_E_INVALID_VM_STATE,
2772 tr("The machine is not powered off (state is %s)"),
2773 Global::stringifyMachineState(mData->mMachineState));
2774
2775 i_setModified(IsModified_MachineData);
2776 mUserData.backup();
2777 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2778
2779 return S_OK;
2780}
2781
2782HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2783{
2784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2787
2788 return S_OK;
2789}
2790
2791HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2792{
2793 if (aTeleporterPort >= _64K)
2794 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2795
2796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2799 if (FAILED(rc)) return rc;
2800
2801 i_setModified(IsModified_MachineData);
2802 mUserData.backup();
2803 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2818{
2819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2822 if (FAILED(rc)) return rc;
2823
2824 i_setModified(IsModified_MachineData);
2825 mUserData.backup();
2826 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2827
2828 return S_OK;
2829}
2830
2831HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2840{
2841 /*
2842 * Hash the password first.
2843 */
2844 com::Utf8Str aT = aTeleporterPassword;
2845
2846 if (!aT.isEmpty())
2847 {
2848 if (VBoxIsPasswordHashed(&aT))
2849 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2850 VBoxHashPassword(&aT);
2851 }
2852
2853 /*
2854 * Do the update.
2855 */
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2858 if (SUCCEEDED(hrc))
2859 {
2860 i_setModified(IsModified_MachineData);
2861 mUserData.backup();
2862 mUserData->s.strTeleporterPassword = aT;
2863 }
2864
2865 return hrc;
2866}
2867
2868HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2869{
2870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2871
2872 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /* Only allow it to be set to true when PoweredOff or Aborted.
2882 (Clearing it is always permitted.) */
2883 if ( aRTCUseUTC
2884 && mData->mRegistered
2885 && ( !i_isSessionMachine()
2886 || ( mData->mMachineState != MachineState_PoweredOff
2887 && mData->mMachineState != MachineState_Teleported
2888 && mData->mMachineState != MachineState_Aborted
2889 )
2890 )
2891 )
2892 return setError(VBOX_E_INVALID_VM_STATE,
2893 tr("The machine is not powered off (state is %s)"),
2894 Global::stringifyMachineState(mData->mMachineState));
2895
2896 i_setModified(IsModified_MachineData);
2897 mUserData.backup();
2898 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2904{
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2913{
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 HRESULT rc = i_checkStateDependency(MutableStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 i_setModified(IsModified_MachineData);
2920 mHWData.backup();
2921 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2922
2923 return S_OK;
2924}
2925
2926HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2927{
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 *aIOCacheSize = mHWData->mIOCacheSize;
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2936{
2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 HRESULT rc = i_checkStateDependency(MutableStateDep);
2940 if (FAILED(rc)) return rc;
2941
2942 i_setModified(IsModified_MachineData);
2943 mHWData.backup();
2944 mHWData->mIOCacheSize = aIOCacheSize;
2945
2946 return S_OK;
2947}
2948
2949
2950/**
2951 * @note Locks objects!
2952 */
2953HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2954 LockType_T aLockType)
2955{
2956 /* check the session state */
2957 SessionState_T state;
2958 HRESULT rc = aSession->COMGETTER(State)(&state);
2959 if (FAILED(rc)) return rc;
2960
2961 if (state != SessionState_Unlocked)
2962 return setError(VBOX_E_INVALID_OBJECT_STATE,
2963 tr("The given session is busy"));
2964
2965 // get the client's IInternalSessionControl interface
2966 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2967 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2968 E_INVALIDARG);
2969
2970 // session name (only used in some code paths)
2971 Utf8Str strSessionName;
2972
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 if (!mData->mRegistered)
2976 return setError(E_UNEXPECTED,
2977 tr("The machine '%s' is not registered"),
2978 mUserData->s.strName.c_str());
2979
2980 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2981
2982 SessionState_T oldState = mData->mSession.mState;
2983 /* Hack: in case the session is closing and there is a progress object
2984 * which allows waiting for the session to be closed, take the opportunity
2985 * and do a limited wait (max. 1 second). This helps a lot when the system
2986 * is busy and thus session closing can take a little while. */
2987 if ( mData->mSession.mState == SessionState_Unlocking
2988 && mData->mSession.mProgress)
2989 {
2990 alock.release();
2991 mData->mSession.mProgress->WaitForCompletion(1000);
2992 alock.acquire();
2993 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2994 }
2995
2996 // try again now
2997 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2998 // (i.e. session machine exists)
2999 && (aLockType == LockType_Shared) // caller wants a shared link to the
3000 // existing session that holds the write lock:
3001 )
3002 {
3003 // OK, share the session... we are now dealing with three processes:
3004 // 1) VBoxSVC (where this code runs);
3005 // 2) process C: the caller's client process (who wants a shared session);
3006 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3007
3008 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3009 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3010 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3011 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3012 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3013
3014 /*
3015 * Release the lock before calling the client process. It's safe here
3016 * since the only thing to do after we get the lock again is to add
3017 * the remote control to the list (which doesn't directly influence
3018 * anything).
3019 */
3020 alock.release();
3021
3022 // get the console of the session holding the write lock (this is a remote call)
3023 ComPtr<IConsole> pConsoleW;
3024 if (mData->mSession.mLockType == LockType_VM)
3025 {
3026 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3027 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3028 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3029 if (FAILED(rc))
3030 // the failure may occur w/o any error info (from RPC), so provide one
3031 return setError(VBOX_E_VM_ERROR,
3032 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3033 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3034 }
3035
3036 // share the session machine and W's console with the caller's session
3037 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3038 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3039 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3040
3041 if (FAILED(rc))
3042 // the failure may occur w/o any error info (from RPC), so provide one
3043 return setError(VBOX_E_VM_ERROR,
3044 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3045 alock.acquire();
3046
3047 // need to revalidate the state after acquiring the lock again
3048 if (mData->mSession.mState != SessionState_Locked)
3049 {
3050 pSessionControl->Uninitialize();
3051 return setError(VBOX_E_INVALID_SESSION_STATE,
3052 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3053 mUserData->s.strName.c_str());
3054 }
3055
3056 // add the caller's session to the list
3057 mData->mSession.mRemoteControls.push_back(pSessionControl);
3058 }
3059 else if ( mData->mSession.mState == SessionState_Locked
3060 || mData->mSession.mState == SessionState_Unlocking
3061 )
3062 {
3063 // sharing not permitted, or machine still unlocking:
3064 return setError(VBOX_E_INVALID_OBJECT_STATE,
3065 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3066 mUserData->s.strName.c_str());
3067 }
3068 else
3069 {
3070 // machine is not locked: then write-lock the machine (create the session machine)
3071
3072 // must not be busy
3073 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3074
3075 // get the caller's session PID
3076 RTPROCESS pid = NIL_RTPROCESS;
3077 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3078 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3079 Assert(pid != NIL_RTPROCESS);
3080
3081 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3082
3083 if (fLaunchingVMProcess)
3084 {
3085 if (mData->mSession.mPID == NIL_RTPROCESS)
3086 {
3087 // two or more clients racing for a lock, the one which set the
3088 // session state to Spawning will win, the others will get an
3089 // error as we can't decide here if waiting a little would help
3090 // (only for shared locks this would avoid an error)
3091 return setError(VBOX_E_INVALID_OBJECT_STATE,
3092 tr("The machine '%s' already has a lock request pending"),
3093 mUserData->s.strName.c_str());
3094 }
3095
3096 // this machine is awaiting for a spawning session to be opened:
3097 // then the calling process must be the one that got started by
3098 // LaunchVMProcess()
3099
3100 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3101 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3102
3103#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3104 /* Hardened windows builds spawns three processes when a VM is
3105 launched, the 3rd one is the one that will end up here. */
3106 RTPROCESS pidParent;
3107 int vrc = RTProcQueryParent(pid, &pidParent);
3108 if (RT_SUCCESS(vrc))
3109 vrc = RTProcQueryParent(pidParent, &pidParent);
3110 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3111 || vrc == VERR_ACCESS_DENIED)
3112 {
3113 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3114 mData->mSession.mPID = pid;
3115 }
3116#endif
3117
3118 if (mData->mSession.mPID != pid)
3119 return setError(E_ACCESSDENIED,
3120 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3121 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3122 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3123 }
3124
3125 // create the mutable SessionMachine from the current machine
3126 ComObjPtr<SessionMachine> sessionMachine;
3127 sessionMachine.createObject();
3128 rc = sessionMachine->init(this);
3129 AssertComRC(rc);
3130
3131 /* NOTE: doing return from this function after this point but
3132 * before the end is forbidden since it may call SessionMachine::uninit()
3133 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3134 * lock while still holding the Machine lock in alock so that a deadlock
3135 * is possible due to the wrong lock order. */
3136
3137 if (SUCCEEDED(rc))
3138 {
3139 /*
3140 * Set the session state to Spawning to protect against subsequent
3141 * attempts to open a session and to unregister the machine after
3142 * we release the lock.
3143 */
3144 SessionState_T origState = mData->mSession.mState;
3145 mData->mSession.mState = SessionState_Spawning;
3146
3147#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3148 /* Get the client token ID to be passed to the client process */
3149 Utf8Str strTokenId;
3150 sessionMachine->i_getTokenId(strTokenId);
3151 Assert(!strTokenId.isEmpty());
3152#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3153 /* Get the client token to be passed to the client process */
3154 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3155 /* The token is now "owned" by pToken, fix refcount */
3156 if (!pToken.isNull())
3157 pToken->Release();
3158#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3159
3160 /*
3161 * Release the lock before calling the client process -- it will call
3162 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3163 * because the state is Spawning, so that LaunchVMProcess() and
3164 * LockMachine() calls will fail. This method, called before we
3165 * acquire the lock again, will fail because of the wrong PID.
3166 *
3167 * Note that mData->mSession.mRemoteControls accessed outside
3168 * the lock may not be modified when state is Spawning, so it's safe.
3169 */
3170 alock.release();
3171
3172 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3173#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3174 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3175#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3176 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3177 /* Now the token is owned by the client process. */
3178 pToken.setNull();
3179#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3180 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3181
3182 /* The failure may occur w/o any error info (from RPC), so provide one */
3183 if (FAILED(rc))
3184 setError(VBOX_E_VM_ERROR,
3185 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3186
3187 // get session name, either to remember or to compare against
3188 // the already known session name.
3189 {
3190 Bstr bstrSessionName;
3191 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3192 if (SUCCEEDED(rc2))
3193 strSessionName = bstrSessionName;
3194 }
3195
3196 if ( SUCCEEDED(rc)
3197 && fLaunchingVMProcess
3198 )
3199 {
3200 /* complete the remote session initialization */
3201
3202 /* get the console from the direct session */
3203 ComPtr<IConsole> console;
3204 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3205 ComAssertComRC(rc);
3206
3207 if (SUCCEEDED(rc) && !console)
3208 {
3209 ComAssert(!!console);
3210 rc = E_FAIL;
3211 }
3212
3213 /* assign machine & console to the remote session */
3214 if (SUCCEEDED(rc))
3215 {
3216 /*
3217 * after LaunchVMProcess(), the first and the only
3218 * entry in remoteControls is that remote session
3219 */
3220 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3221 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3222 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3223
3224 /* The failure may occur w/o any error info (from RPC), so provide one */
3225 if (FAILED(rc))
3226 setError(VBOX_E_VM_ERROR,
3227 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3228 }
3229
3230 if (FAILED(rc))
3231 pSessionControl->Uninitialize();
3232 }
3233
3234 /* acquire the lock again */
3235 alock.acquire();
3236
3237 /* Restore the session state */
3238 mData->mSession.mState = origState;
3239 }
3240
3241 // finalize spawning anyway (this is why we don't return on errors above)
3242 if (fLaunchingVMProcess)
3243 {
3244 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3245 /* Note that the progress object is finalized later */
3246 /** @todo Consider checking mData->mSession.mProgress for cancellation
3247 * around here. */
3248
3249 /* We don't reset mSession.mPID here because it is necessary for
3250 * SessionMachine::uninit() to reap the child process later. */
3251
3252 if (FAILED(rc))
3253 {
3254 /* Close the remote session, remove the remote control from the list
3255 * and reset session state to Closed (@note keep the code in sync
3256 * with the relevant part in checkForSpawnFailure()). */
3257
3258 Assert(mData->mSession.mRemoteControls.size() == 1);
3259 if (mData->mSession.mRemoteControls.size() == 1)
3260 {
3261 ErrorInfoKeeper eik;
3262 mData->mSession.mRemoteControls.front()->Uninitialize();
3263 }
3264
3265 mData->mSession.mRemoteControls.clear();
3266 mData->mSession.mState = SessionState_Unlocked;
3267 }
3268 }
3269 else
3270 {
3271 /* memorize PID of the directly opened session */
3272 if (SUCCEEDED(rc))
3273 mData->mSession.mPID = pid;
3274 }
3275
3276 if (SUCCEEDED(rc))
3277 {
3278 mData->mSession.mLockType = aLockType;
3279 /* memorize the direct session control and cache IUnknown for it */
3280 mData->mSession.mDirectControl = pSessionControl;
3281 mData->mSession.mState = SessionState_Locked;
3282 if (!fLaunchingVMProcess)
3283 mData->mSession.mName = strSessionName;
3284 /* associate the SessionMachine with this Machine */
3285 mData->mSession.mMachine = sessionMachine;
3286
3287 /* request an IUnknown pointer early from the remote party for later
3288 * identity checks (it will be internally cached within mDirectControl
3289 * at least on XPCOM) */
3290 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3291 NOREF(unk);
3292 }
3293
3294 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3295 * would break the lock order */
3296 alock.release();
3297
3298 /* uninitialize the created session machine on failure */
3299 if (FAILED(rc))
3300 sessionMachine->uninit();
3301 }
3302
3303 if (SUCCEEDED(rc))
3304 {
3305 /*
3306 * tell the client watcher thread to update the set of
3307 * machines that have open sessions
3308 */
3309 mParent->i_updateClientWatcher();
3310
3311 if (oldState != SessionState_Locked)
3312 /* fire an event */
3313 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3314 }
3315
3316 return rc;
3317}
3318
3319/**
3320 * @note Locks objects!
3321 */
3322HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3323 const com::Utf8Str &aName,
3324 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3325 ComPtr<IProgress> &aProgress)
3326{
3327 Utf8Str strFrontend(aName);
3328 /* "emergencystop" doesn't need the session, so skip the checks/interface
3329 * retrieval. This code doesn't quite fit in here, but introducing a
3330 * special API method would be even more effort, and would require explicit
3331 * support by every API client. It's better to hide the feature a bit. */
3332 if (strFrontend != "emergencystop")
3333 CheckComArgNotNull(aSession);
3334
3335 HRESULT rc = S_OK;
3336 if (strFrontend.isEmpty())
3337 {
3338 Bstr bstrFrontend;
3339 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3340 if (FAILED(rc))
3341 return rc;
3342 strFrontend = bstrFrontend;
3343 if (strFrontend.isEmpty())
3344 {
3345 ComPtr<ISystemProperties> systemProperties;
3346 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3347 if (FAILED(rc))
3348 return rc;
3349 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3350 if (FAILED(rc))
3351 return rc;
3352 strFrontend = bstrFrontend;
3353 }
3354 /* paranoia - emergencystop is not a valid default */
3355 if (strFrontend == "emergencystop")
3356 strFrontend = Utf8Str::Empty;
3357 }
3358 /* default frontend: Qt GUI */
3359 if (strFrontend.isEmpty())
3360 strFrontend = "GUI/Qt";
3361
3362 if (strFrontend != "emergencystop")
3363 {
3364 /* check the session state */
3365 SessionState_T state;
3366 rc = aSession->COMGETTER(State)(&state);
3367 if (FAILED(rc))
3368 return rc;
3369
3370 if (state != SessionState_Unlocked)
3371 return setError(VBOX_E_INVALID_OBJECT_STATE,
3372 tr("The given session is busy"));
3373
3374 /* get the IInternalSessionControl interface */
3375 ComPtr<IInternalSessionControl> control(aSession);
3376 ComAssertMsgRet(!control.isNull(),
3377 ("No IInternalSessionControl interface"),
3378 E_INVALIDARG);
3379
3380 /* get the teleporter enable state for the progress object init. */
3381 BOOL fTeleporterEnabled;
3382 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3383 if (FAILED(rc))
3384 return rc;
3385
3386 /* create a progress object */
3387 ComObjPtr<ProgressProxy> progress;
3388 progress.createObject();
3389 rc = progress->init(mParent,
3390 static_cast<IMachine*>(this),
3391 Bstr(tr("Starting VM")).raw(),
3392 TRUE /* aCancelable */,
3393 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3394 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3395 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3396 2 /* uFirstOperationWeight */,
3397 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3398
3399 if (SUCCEEDED(rc))
3400 {
3401 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3402 if (SUCCEEDED(rc))
3403 {
3404 aProgress = progress;
3405
3406 /* signal the client watcher thread */
3407 mParent->i_updateClientWatcher();
3408
3409 /* fire an event */
3410 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3411 }
3412 }
3413 }
3414 else
3415 {
3416 /* no progress object - either instant success or failure */
3417 aProgress = NULL;
3418
3419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3420
3421 if (mData->mSession.mState != SessionState_Locked)
3422 return setError(VBOX_E_INVALID_OBJECT_STATE,
3423 tr("The machine '%s' is not locked by a session"),
3424 mUserData->s.strName.c_str());
3425
3426 /* must have a VM process associated - do not kill normal API clients
3427 * with an open session */
3428 if (!Global::IsOnline(mData->mMachineState))
3429 return setError(VBOX_E_INVALID_OBJECT_STATE,
3430 tr("The machine '%s' does not have a VM process"),
3431 mUserData->s.strName.c_str());
3432
3433 /* forcibly terminate the VM process */
3434 if (mData->mSession.mPID != NIL_RTPROCESS)
3435 RTProcTerminate(mData->mSession.mPID);
3436
3437 /* signal the client watcher thread, as most likely the client has
3438 * been terminated */
3439 mParent->i_updateClientWatcher();
3440 }
3441
3442 return rc;
3443}
3444
3445HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3446{
3447 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3448 return setError(E_INVALIDARG,
3449 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3450 aPosition, SchemaDefs::MaxBootPosition);
3451
3452 if (aDevice == DeviceType_USB)
3453 return setError(E_NOTIMPL,
3454 tr("Booting from USB device is currently not supported"));
3455
3456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3457
3458 HRESULT rc = i_checkStateDependency(MutableStateDep);
3459 if (FAILED(rc)) return rc;
3460
3461 i_setModified(IsModified_MachineData);
3462 mHWData.backup();
3463 mHWData->mBootOrder[aPosition - 1] = aDevice;
3464
3465 return S_OK;
3466}
3467
3468HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3469{
3470 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3471 return setError(E_INVALIDARG,
3472 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3473 aPosition, SchemaDefs::MaxBootPosition);
3474
3475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3476
3477 *aDevice = mHWData->mBootOrder[aPosition - 1];
3478
3479 return S_OK;
3480}
3481
3482HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3483 LONG aControllerPort,
3484 LONG aDevice,
3485 DeviceType_T aType,
3486 const ComPtr<IMedium> &aMedium)
3487{
3488 IMedium *aM = aMedium;
3489 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3490 aName.c_str(), aControllerPort, aDevice, aType, aM));
3491
3492 // request the host lock first, since might be calling Host methods for getting host drives;
3493 // next, protect the media tree all the while we're in here, as well as our member variables
3494 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3495 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3496
3497 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3498 if (FAILED(rc)) return rc;
3499
3500 /// @todo NEWMEDIA implicit machine registration
3501 if (!mData->mRegistered)
3502 return setError(VBOX_E_INVALID_OBJECT_STATE,
3503 tr("Cannot attach storage devices to an unregistered machine"));
3504
3505 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3506
3507 /* Check for an existing controller. */
3508 ComObjPtr<StorageController> ctl;
3509 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3510 if (FAILED(rc)) return rc;
3511
3512 StorageControllerType_T ctrlType;
3513 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3514 if (FAILED(rc))
3515 return setError(E_FAIL,
3516 tr("Could not get type of controller '%s'"),
3517 aName.c_str());
3518
3519 bool fSilent = false;
3520 Utf8Str strReconfig;
3521
3522 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3523 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3524 if ( mData->mMachineState == MachineState_Paused
3525 && strReconfig == "1")
3526 fSilent = true;
3527
3528 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3529 bool fHotplug = false;
3530 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3531 fHotplug = true;
3532
3533 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3534 return setError(VBOX_E_INVALID_VM_STATE,
3535 tr("Controller '%s' does not support hotplugging"),
3536 aName.c_str());
3537
3538 // check that the port and device are not out of range
3539 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3540 if (FAILED(rc)) return rc;
3541
3542 /* check if the device slot is already busy */
3543 MediumAttachment *pAttachTemp;
3544 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3545 aName,
3546 aControllerPort,
3547 aDevice)))
3548 {
3549 Medium *pMedium = pAttachTemp->i_getMedium();
3550 if (pMedium)
3551 {
3552 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3553 return setError(VBOX_E_OBJECT_IN_USE,
3554 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3555 pMedium->i_getLocationFull().c_str(),
3556 aControllerPort,
3557 aDevice,
3558 aName.c_str());
3559 }
3560 else
3561 return setError(VBOX_E_OBJECT_IN_USE,
3562 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3563 aControllerPort, aDevice, aName.c_str());
3564 }
3565
3566 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3567 if (aMedium && medium.isNull())
3568 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3569
3570 AutoCaller mediumCaller(medium);
3571 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3572
3573 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3574
3575 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3576 && !medium.isNull()
3577 && ( medium->i_getType() != MediumType_Readonly
3578 || medium->i_getDeviceType() != DeviceType_DVD)
3579 )
3580 return setError(VBOX_E_OBJECT_IN_USE,
3581 tr("Medium '%s' is already attached to this virtual machine"),
3582 medium->i_getLocationFull().c_str());
3583
3584 if (!medium.isNull())
3585 {
3586 MediumType_T mtype = medium->i_getType();
3587 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3588 // For DVDs it's not written to the config file, so needs no global config
3589 // version bump. For floppies it's a new attribute "type", which is ignored
3590 // by older VirtualBox version, so needs no global config version bump either.
3591 // For hard disks this type is not accepted.
3592 if (mtype == MediumType_MultiAttach)
3593 {
3594 // This type is new with VirtualBox 4.0 and therefore requires settings
3595 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3596 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3597 // two reasons: The medium type is a property of the media registry tree, which
3598 // can reside in the global config file (for pre-4.0 media); we would therefore
3599 // possibly need to bump the global config version. We don't want to do that though
3600 // because that might make downgrading to pre-4.0 impossible.
3601 // As a result, we can only use these two new types if the medium is NOT in the
3602 // global registry:
3603 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3604 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3605 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3606 )
3607 return setError(VBOX_E_INVALID_OBJECT_STATE,
3608 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3609 "to machines that were created with VirtualBox 4.0 or later"),
3610 medium->i_getLocationFull().c_str());
3611 }
3612 }
3613
3614 bool fIndirect = false;
3615 if (!medium.isNull())
3616 fIndirect = medium->i_isReadOnly();
3617 bool associate = true;
3618
3619 do
3620 {
3621 if ( aType == DeviceType_HardDisk
3622 && mMediumAttachments.isBackedUp())
3623 {
3624 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3625
3626 /* check if the medium was attached to the VM before we started
3627 * changing attachments in which case the attachment just needs to
3628 * be restored */
3629 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3630 {
3631 AssertReturn(!fIndirect, E_FAIL);
3632
3633 /* see if it's the same bus/channel/device */
3634 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3635 {
3636 /* the simplest case: restore the whole attachment
3637 * and return, nothing else to do */
3638 mMediumAttachments->push_back(pAttachTemp);
3639
3640 /* Reattach the medium to the VM. */
3641 if (fHotplug || fSilent)
3642 {
3643 mediumLock.release();
3644 treeLock.release();
3645 alock.release();
3646
3647 MediumLockList *pMediumLockList(new MediumLockList());
3648
3649 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3650 medium /* pToLockWrite */,
3651 false /* fMediumLockWriteAll */,
3652 NULL,
3653 *pMediumLockList);
3654 alock.acquire();
3655 if (FAILED(rc))
3656 delete pMediumLockList;
3657 else
3658 {
3659 mData->mSession.mLockedMedia.Unlock();
3660 alock.release();
3661 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3662 mData->mSession.mLockedMedia.Lock();
3663 alock.acquire();
3664 }
3665 alock.release();
3666
3667 if (SUCCEEDED(rc))
3668 {
3669 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3670 /* Remove lock list in case of error. */
3671 if (FAILED(rc))
3672 {
3673 mData->mSession.mLockedMedia.Unlock();
3674 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3675 mData->mSession.mLockedMedia.Lock();
3676 }
3677 }
3678 }
3679
3680 return S_OK;
3681 }
3682
3683 /* bus/channel/device differ; we need a new attachment object,
3684 * but don't try to associate it again */
3685 associate = false;
3686 break;
3687 }
3688 }
3689
3690 /* go further only if the attachment is to be indirect */
3691 if (!fIndirect)
3692 break;
3693
3694 /* perform the so called smart attachment logic for indirect
3695 * attachments. Note that smart attachment is only applicable to base
3696 * hard disks. */
3697
3698 if (medium->i_getParent().isNull())
3699 {
3700 /* first, investigate the backup copy of the current hard disk
3701 * attachments to make it possible to re-attach existing diffs to
3702 * another device slot w/o losing their contents */
3703 if (mMediumAttachments.isBackedUp())
3704 {
3705 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3706
3707 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3708 uint32_t foundLevel = 0;
3709
3710 for (MediumAttachmentList::const_iterator
3711 it = oldAtts.begin();
3712 it != oldAtts.end();
3713 ++it)
3714 {
3715 uint32_t level = 0;
3716 MediumAttachment *pAttach = *it;
3717 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3718 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3719 if (pMedium.isNull())
3720 continue;
3721
3722 if (pMedium->i_getBase(&level) == medium)
3723 {
3724 /* skip the hard disk if its currently attached (we
3725 * cannot attach the same hard disk twice) */
3726 if (i_findAttachment(*mMediumAttachments.data(),
3727 pMedium))
3728 continue;
3729
3730 /* matched device, channel and bus (i.e. attached to the
3731 * same place) will win and immediately stop the search;
3732 * otherwise the attachment that has the youngest
3733 * descendant of medium will be used
3734 */
3735 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3736 {
3737 /* the simplest case: restore the whole attachment
3738 * and return, nothing else to do */
3739 mMediumAttachments->push_back(*it);
3740
3741 /* Reattach the medium to the VM. */
3742 if (fHotplug || fSilent)
3743 {
3744 mediumLock.release();
3745 treeLock.release();
3746 alock.release();
3747
3748 MediumLockList *pMediumLockList(new MediumLockList());
3749
3750 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3751 medium /* pToLockWrite */,
3752 false /* fMediumLockWriteAll */,
3753 NULL,
3754 *pMediumLockList);
3755 alock.acquire();
3756 if (FAILED(rc))
3757 delete pMediumLockList;
3758 else
3759 {
3760 mData->mSession.mLockedMedia.Unlock();
3761 alock.release();
3762 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3763 mData->mSession.mLockedMedia.Lock();
3764 alock.acquire();
3765 }
3766 alock.release();
3767
3768 if (SUCCEEDED(rc))
3769 {
3770 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3771 /* Remove lock list in case of error. */
3772 if (FAILED(rc))
3773 {
3774 mData->mSession.mLockedMedia.Unlock();
3775 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3776 mData->mSession.mLockedMedia.Lock();
3777 }
3778 }
3779 }
3780
3781 return S_OK;
3782 }
3783 else if ( foundIt == oldAtts.end()
3784 || level > foundLevel /* prefer younger */
3785 )
3786 {
3787 foundIt = it;
3788 foundLevel = level;
3789 }
3790 }
3791 }
3792
3793 if (foundIt != oldAtts.end())
3794 {
3795 /* use the previously attached hard disk */
3796 medium = (*foundIt)->i_getMedium();
3797 mediumCaller.attach(medium);
3798 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3799 mediumLock.attach(medium);
3800 /* not implicit, doesn't require association with this VM */
3801 fIndirect = false;
3802 associate = false;
3803 /* go right to the MediumAttachment creation */
3804 break;
3805 }
3806 }
3807
3808 /* must give up the medium lock and medium tree lock as below we
3809 * go over snapshots, which needs a lock with higher lock order. */
3810 mediumLock.release();
3811 treeLock.release();
3812
3813 /* then, search through snapshots for the best diff in the given
3814 * hard disk's chain to base the new diff on */
3815
3816 ComObjPtr<Medium> base;
3817 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3818 while (snap)
3819 {
3820 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3821
3822 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3823
3824 MediumAttachment *pAttachFound = NULL;
3825 uint32_t foundLevel = 0;
3826
3827 for (MediumAttachmentList::const_iterator
3828 it = snapAtts.begin();
3829 it != snapAtts.end();
3830 ++it)
3831 {
3832 MediumAttachment *pAttach = *it;
3833 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3834 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3835 if (pMedium.isNull())
3836 continue;
3837
3838 uint32_t level = 0;
3839 if (pMedium->i_getBase(&level) == medium)
3840 {
3841 /* matched device, channel and bus (i.e. attached to the
3842 * same place) will win and immediately stop the search;
3843 * otherwise the attachment that has the youngest
3844 * descendant of medium will be used
3845 */
3846 if ( pAttach->i_getDevice() == aDevice
3847 && pAttach->i_getPort() == aControllerPort
3848 && pAttach->i_getControllerName() == aName
3849 )
3850 {
3851 pAttachFound = pAttach;
3852 break;
3853 }
3854 else if ( !pAttachFound
3855 || level > foundLevel /* prefer younger */
3856 )
3857 {
3858 pAttachFound = pAttach;
3859 foundLevel = level;
3860 }
3861 }
3862 }
3863
3864 if (pAttachFound)
3865 {
3866 base = pAttachFound->i_getMedium();
3867 break;
3868 }
3869
3870 snap = snap->i_getParent();
3871 }
3872
3873 /* re-lock medium tree and the medium, as we need it below */
3874 treeLock.acquire();
3875 mediumLock.acquire();
3876
3877 /* found a suitable diff, use it as a base */
3878 if (!base.isNull())
3879 {
3880 medium = base;
3881 mediumCaller.attach(medium);
3882 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3883 mediumLock.attach(medium);
3884 }
3885 }
3886
3887 Utf8Str strFullSnapshotFolder;
3888 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3889
3890 ComObjPtr<Medium> diff;
3891 diff.createObject();
3892 // store this diff in the same registry as the parent
3893 Guid uuidRegistryParent;
3894 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3895 {
3896 // parent image has no registry: this can happen if we're attaching a new immutable
3897 // image that has not yet been attached (medium then points to the base and we're
3898 // creating the diff image for the immutable, and the parent is not yet registered);
3899 // put the parent in the machine registry then
3900 mediumLock.release();
3901 treeLock.release();
3902 alock.release();
3903 i_addMediumToRegistry(medium);
3904 alock.acquire();
3905 treeLock.acquire();
3906 mediumLock.acquire();
3907 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3908 }
3909 rc = diff->init(mParent,
3910 medium->i_getPreferredDiffFormat(),
3911 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3912 uuidRegistryParent,
3913 DeviceType_HardDisk);
3914 if (FAILED(rc)) return rc;
3915
3916 /* Apply the normal locking logic to the entire chain. */
3917 MediumLockList *pMediumLockList(new MediumLockList());
3918 mediumLock.release();
3919 treeLock.release();
3920 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3921 diff /* pToLockWrite */,
3922 false /* fMediumLockWriteAll */,
3923 medium,
3924 *pMediumLockList);
3925 treeLock.acquire();
3926 mediumLock.acquire();
3927 if (SUCCEEDED(rc))
3928 {
3929 mediumLock.release();
3930 treeLock.release();
3931 rc = pMediumLockList->Lock();
3932 treeLock.acquire();
3933 mediumLock.acquire();
3934 if (FAILED(rc))
3935 setError(rc,
3936 tr("Could not lock medium when creating diff '%s'"),
3937 diff->i_getLocationFull().c_str());
3938 else
3939 {
3940 /* will release the lock before the potentially lengthy
3941 * operation, so protect with the special state */
3942 MachineState_T oldState = mData->mMachineState;
3943 i_setMachineState(MachineState_SettingUp);
3944
3945 mediumLock.release();
3946 treeLock.release();
3947 alock.release();
3948
3949 rc = medium->i_createDiffStorage(diff,
3950 medium->i_getPreferredDiffVariant(),
3951 pMediumLockList,
3952 NULL /* aProgress */,
3953 true /* aWait */,
3954 false /* aNotify */);
3955
3956 alock.acquire();
3957 treeLock.acquire();
3958 mediumLock.acquire();
3959
3960 i_setMachineState(oldState);
3961 }
3962 }
3963
3964 /* Unlock the media and free the associated memory. */
3965 delete pMediumLockList;
3966
3967 if (FAILED(rc)) return rc;
3968
3969 /* use the created diff for the actual attachment */
3970 medium = diff;
3971 mediumCaller.attach(medium);
3972 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3973 mediumLock.attach(medium);
3974 }
3975 while (0);
3976
3977 ComObjPtr<MediumAttachment> attachment;
3978 attachment.createObject();
3979 rc = attachment->init(this,
3980 medium,
3981 aName,
3982 aControllerPort,
3983 aDevice,
3984 aType,
3985 fIndirect,
3986 false /* fPassthrough */,
3987 false /* fTempEject */,
3988 false /* fNonRotational */,
3989 false /* fDiscard */,
3990 fHotplug /* fHotPluggable */,
3991 Utf8Str::Empty);
3992 if (FAILED(rc)) return rc;
3993
3994 if (associate && !medium.isNull())
3995 {
3996 // as the last step, associate the medium to the VM
3997 rc = medium->i_addBackReference(mData->mUuid);
3998 // here we can fail because of Deleting, or being in process of creating a Diff
3999 if (FAILED(rc)) return rc;
4000
4001 mediumLock.release();
4002 treeLock.release();
4003 alock.release();
4004 i_addMediumToRegistry(medium);
4005 alock.acquire();
4006 treeLock.acquire();
4007 mediumLock.acquire();
4008 }
4009
4010 /* success: finally remember the attachment */
4011 i_setModified(IsModified_Storage);
4012 mMediumAttachments.backup();
4013 mMediumAttachments->push_back(attachment);
4014
4015 mediumLock.release();
4016 treeLock.release();
4017 alock.release();
4018
4019 if (fHotplug || fSilent)
4020 {
4021 if (!medium.isNull())
4022 {
4023 MediumLockList *pMediumLockList(new MediumLockList());
4024
4025 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4026 medium /* pToLockWrite */,
4027 false /* fMediumLockWriteAll */,
4028 NULL,
4029 *pMediumLockList);
4030 alock.acquire();
4031 if (FAILED(rc))
4032 delete pMediumLockList;
4033 else
4034 {
4035 mData->mSession.mLockedMedia.Unlock();
4036 alock.release();
4037 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4038 mData->mSession.mLockedMedia.Lock();
4039 alock.acquire();
4040 }
4041 alock.release();
4042 }
4043
4044 if (SUCCEEDED(rc))
4045 {
4046 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4047 /* Remove lock list in case of error. */
4048 if (FAILED(rc))
4049 {
4050 mData->mSession.mLockedMedia.Unlock();
4051 mData->mSession.mLockedMedia.Remove(attachment);
4052 mData->mSession.mLockedMedia.Lock();
4053 }
4054 }
4055 }
4056
4057 /* Save modified registries, but skip this machine as it's the caller's
4058 * job to save its settings like all other settings changes. */
4059 mParent->i_unmarkRegistryModified(i_getId());
4060 mParent->i_saveModifiedRegistries();
4061
4062 if (SUCCEEDED(rc))
4063 {
4064 if (fIndirect && medium != aM)
4065 mParent->i_onMediumConfigChanged(medium);
4066 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4067 }
4068
4069 return rc;
4070}
4071
4072HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4073 LONG aDevice)
4074{
4075 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4076 aName.c_str(), aControllerPort, aDevice));
4077
4078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4079
4080 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4081 if (FAILED(rc)) return rc;
4082
4083 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4084
4085 /* Check for an existing controller. */
4086 ComObjPtr<StorageController> ctl;
4087 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4088 if (FAILED(rc)) return rc;
4089
4090 StorageControllerType_T ctrlType;
4091 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4092 if (FAILED(rc))
4093 return setError(E_FAIL,
4094 tr("Could not get type of controller '%s'"),
4095 aName.c_str());
4096
4097 bool fSilent = false;
4098 Utf8Str strReconfig;
4099
4100 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4101 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4102 if ( mData->mMachineState == MachineState_Paused
4103 && strReconfig == "1")
4104 fSilent = true;
4105
4106 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4107 bool fHotplug = false;
4108 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4109 fHotplug = true;
4110
4111 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4112 return setError(VBOX_E_INVALID_VM_STATE,
4113 tr("Controller '%s' does not support hotplugging"),
4114 aName.c_str());
4115
4116 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4117 aName,
4118 aControllerPort,
4119 aDevice);
4120 if (!pAttach)
4121 return setError(VBOX_E_OBJECT_NOT_FOUND,
4122 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4123 aDevice, aControllerPort, aName.c_str());
4124
4125 if (fHotplug && !pAttach->i_getHotPluggable())
4126 return setError(VBOX_E_NOT_SUPPORTED,
4127 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4128 aDevice, aControllerPort, aName.c_str());
4129
4130 /*
4131 * The VM has to detach the device before we delete any implicit diffs.
4132 * If this fails we can roll back without loosing data.
4133 */
4134 if (fHotplug || fSilent)
4135 {
4136 alock.release();
4137 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4138 alock.acquire();
4139 }
4140 if (FAILED(rc)) return rc;
4141
4142 /* If we are here everything went well and we can delete the implicit now. */
4143 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4144
4145 alock.release();
4146
4147 /* Save modified registries, but skip this machine as it's the caller's
4148 * job to save its settings like all other settings changes. */
4149 mParent->i_unmarkRegistryModified(i_getId());
4150 mParent->i_saveModifiedRegistries();
4151
4152 if (SUCCEEDED(rc))
4153 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4154
4155 return rc;
4156}
4157
4158HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4159 LONG aDevice, BOOL aPassthrough)
4160{
4161 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4162 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4163
4164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4165
4166 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4167 if (FAILED(rc)) return rc;
4168
4169 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4170
4171 /* Check for an existing controller. */
4172 ComObjPtr<StorageController> ctl;
4173 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4174 if (FAILED(rc)) return rc;
4175
4176 StorageControllerType_T ctrlType;
4177 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4178 if (FAILED(rc))
4179 return setError(E_FAIL,
4180 tr("Could not get type of controller '%s'"),
4181 aName.c_str());
4182
4183 bool fSilent = false;
4184 Utf8Str strReconfig;
4185
4186 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4187 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4188 if ( mData->mMachineState == MachineState_Paused
4189 && strReconfig == "1")
4190 fSilent = true;
4191
4192 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4193 bool fHotplug = false;
4194 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4195 fHotplug = true;
4196
4197 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4198 return setError(VBOX_E_INVALID_VM_STATE,
4199 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4200 aName.c_str());
4201
4202 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4203 aName,
4204 aControllerPort,
4205 aDevice);
4206 if (!pAttach)
4207 return setError(VBOX_E_OBJECT_NOT_FOUND,
4208 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4209 aDevice, aControllerPort, aName.c_str());
4210
4211
4212 i_setModified(IsModified_Storage);
4213 mMediumAttachments.backup();
4214
4215 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4216
4217 if (pAttach->i_getType() != DeviceType_DVD)
4218 return setError(E_INVALIDARG,
4219 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4220 aDevice, aControllerPort, aName.c_str());
4221
4222 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4223
4224 pAttach->i_updatePassthrough(!!aPassthrough);
4225
4226 attLock.release();
4227 alock.release();
4228 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4229 if (SUCCEEDED(rc) && fValueChanged)
4230 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4231
4232 return rc;
4233}
4234
4235HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4236 LONG aDevice, BOOL aTemporaryEject)
4237{
4238
4239 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4240 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4241
4242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4243
4244 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4245 if (FAILED(rc)) return rc;
4246
4247 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4248 aName,
4249 aControllerPort,
4250 aDevice);
4251 if (!pAttach)
4252 return setError(VBOX_E_OBJECT_NOT_FOUND,
4253 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4254 aDevice, aControllerPort, aName.c_str());
4255
4256
4257 i_setModified(IsModified_Storage);
4258 mMediumAttachments.backup();
4259
4260 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4261
4262 if (pAttach->i_getType() != DeviceType_DVD)
4263 return setError(E_INVALIDARG,
4264 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4265 aDevice, aControllerPort, aName.c_str());
4266 pAttach->i_updateTempEject(!!aTemporaryEject);
4267
4268 return S_OK;
4269}
4270
4271HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4272 LONG aDevice, BOOL aNonRotational)
4273{
4274
4275 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4276 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4277
4278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4279
4280 HRESULT rc = i_checkStateDependency(MutableStateDep);
4281 if (FAILED(rc)) return rc;
4282
4283 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4284
4285 if (Global::IsOnlineOrTransient(mData->mMachineState))
4286 return setError(VBOX_E_INVALID_VM_STATE,
4287 tr("Invalid machine state: %s"),
4288 Global::stringifyMachineState(mData->mMachineState));
4289
4290 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4291 aName,
4292 aControllerPort,
4293 aDevice);
4294 if (!pAttach)
4295 return setError(VBOX_E_OBJECT_NOT_FOUND,
4296 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4297 aDevice, aControllerPort, aName.c_str());
4298
4299
4300 i_setModified(IsModified_Storage);
4301 mMediumAttachments.backup();
4302
4303 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4304
4305 if (pAttach->i_getType() != DeviceType_HardDisk)
4306 return setError(E_INVALIDARG,
4307 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4308 aDevice, aControllerPort, aName.c_str());
4309 pAttach->i_updateNonRotational(!!aNonRotational);
4310
4311 return S_OK;
4312}
4313
4314HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4315 LONG aDevice, BOOL aDiscard)
4316{
4317
4318 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4319 aName.c_str(), aControllerPort, aDevice, aDiscard));
4320
4321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4322
4323 HRESULT rc = i_checkStateDependency(MutableStateDep);
4324 if (FAILED(rc)) return rc;
4325
4326 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4327
4328 if (Global::IsOnlineOrTransient(mData->mMachineState))
4329 return setError(VBOX_E_INVALID_VM_STATE,
4330 tr("Invalid machine state: %s"),
4331 Global::stringifyMachineState(mData->mMachineState));
4332
4333 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4334 aName,
4335 aControllerPort,
4336 aDevice);
4337 if (!pAttach)
4338 return setError(VBOX_E_OBJECT_NOT_FOUND,
4339 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4340 aDevice, aControllerPort, aName.c_str());
4341
4342
4343 i_setModified(IsModified_Storage);
4344 mMediumAttachments.backup();
4345
4346 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4347
4348 if (pAttach->i_getType() != DeviceType_HardDisk)
4349 return setError(E_INVALIDARG,
4350 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4351 aDevice, aControllerPort, aName.c_str());
4352 pAttach->i_updateDiscard(!!aDiscard);
4353
4354 return S_OK;
4355}
4356
4357HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4358 LONG aDevice, BOOL aHotPluggable)
4359{
4360 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4361 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4362
4363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4364
4365 HRESULT rc = i_checkStateDependency(MutableStateDep);
4366 if (FAILED(rc)) return rc;
4367
4368 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4369
4370 if (Global::IsOnlineOrTransient(mData->mMachineState))
4371 return setError(VBOX_E_INVALID_VM_STATE,
4372 tr("Invalid machine state: %s"),
4373 Global::stringifyMachineState(mData->mMachineState));
4374
4375 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4376 aName,
4377 aControllerPort,
4378 aDevice);
4379 if (!pAttach)
4380 return setError(VBOX_E_OBJECT_NOT_FOUND,
4381 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4382 aDevice, aControllerPort, aName.c_str());
4383
4384 /* Check for an existing controller. */
4385 ComObjPtr<StorageController> ctl;
4386 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4387 if (FAILED(rc)) return rc;
4388
4389 StorageControllerType_T ctrlType;
4390 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4391 if (FAILED(rc))
4392 return setError(E_FAIL,
4393 tr("Could not get type of controller '%s'"),
4394 aName.c_str());
4395
4396 if (!i_isControllerHotplugCapable(ctrlType))
4397 return setError(VBOX_E_NOT_SUPPORTED,
4398 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4399 aName.c_str());
4400
4401 i_setModified(IsModified_Storage);
4402 mMediumAttachments.backup();
4403
4404 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4405
4406 if (pAttach->i_getType() == DeviceType_Floppy)
4407 return setError(E_INVALIDARG,
4408 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4409 aDevice, aControllerPort, aName.c_str());
4410 pAttach->i_updateHotPluggable(!!aHotPluggable);
4411
4412 return S_OK;
4413}
4414
4415HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4416 LONG aDevice)
4417{
4418 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4419 aName.c_str(), aControllerPort, aDevice));
4420
4421 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4422}
4423
4424HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4425 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4426{
4427 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4428 aName.c_str(), aControllerPort, aDevice));
4429
4430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4431
4432 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4433 if (FAILED(rc)) return rc;
4434
4435 if (Global::IsOnlineOrTransient(mData->mMachineState))
4436 return setError(VBOX_E_INVALID_VM_STATE,
4437 tr("Invalid machine state: %s"),
4438 Global::stringifyMachineState(mData->mMachineState));
4439
4440 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4441 aName,
4442 aControllerPort,
4443 aDevice);
4444 if (!pAttach)
4445 return setError(VBOX_E_OBJECT_NOT_FOUND,
4446 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4447 aDevice, aControllerPort, aName.c_str());
4448
4449
4450 i_setModified(IsModified_Storage);
4451 mMediumAttachments.backup();
4452
4453 IBandwidthGroup *iB = aBandwidthGroup;
4454 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4455 if (aBandwidthGroup && group.isNull())
4456 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4457
4458 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4459
4460 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4461 if (strBandwidthGroupOld.isNotEmpty())
4462 {
4463 /* Get the bandwidth group object and release it - this must not fail. */
4464 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4465 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4466 Assert(SUCCEEDED(rc));
4467
4468 pBandwidthGroupOld->i_release();
4469 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4470 }
4471
4472 if (!group.isNull())
4473 {
4474 group->i_reference();
4475 pAttach->i_updateBandwidthGroup(group->i_getName());
4476 }
4477
4478 return S_OK;
4479}
4480
4481HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 DeviceType_T aType)
4485{
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aType));
4488
4489 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4490}
4491
4492
4493HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4494 LONG aControllerPort,
4495 LONG aDevice,
4496 BOOL aForce)
4497{
4498 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4499 aName.c_str(), aControllerPort, aForce));
4500
4501 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4502}
4503
4504HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4505 LONG aControllerPort,
4506 LONG aDevice,
4507 const ComPtr<IMedium> &aMedium,
4508 BOOL aForce)
4509{
4510 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4511 aName.c_str(), aControllerPort, aDevice, aForce));
4512
4513 // request the host lock first, since might be calling Host methods for getting host drives;
4514 // next, protect the media tree all the while we're in here, as well as our member variables
4515 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4516 this->lockHandle(),
4517 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4518
4519 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4520 aName,
4521 aControllerPort,
4522 aDevice);
4523 if (pAttach.isNull())
4524 return setError(VBOX_E_OBJECT_NOT_FOUND,
4525 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4526 aDevice, aControllerPort, aName.c_str());
4527
4528 /* Remember previously mounted medium. The medium before taking the
4529 * backup is not necessarily the same thing. */
4530 ComObjPtr<Medium> oldmedium;
4531 oldmedium = pAttach->i_getMedium();
4532
4533 IMedium *iM = aMedium;
4534 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4535 if (aMedium && pMedium.isNull())
4536 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4537
4538 AutoCaller mediumCaller(pMedium);
4539 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4540
4541 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4542 if (pMedium)
4543 {
4544 DeviceType_T mediumType = pAttach->i_getType();
4545 switch (mediumType)
4546 {
4547 case DeviceType_DVD:
4548 case DeviceType_Floppy:
4549 break;
4550
4551 default:
4552 return setError(VBOX_E_INVALID_OBJECT_STATE,
4553 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4554 aControllerPort,
4555 aDevice,
4556 aName.c_str());
4557 }
4558 }
4559
4560 i_setModified(IsModified_Storage);
4561 mMediumAttachments.backup();
4562
4563 {
4564 // The backup operation makes the pAttach reference point to the
4565 // old settings. Re-get the correct reference.
4566 pAttach = i_findAttachment(*mMediumAttachments.data(),
4567 aName,
4568 aControllerPort,
4569 aDevice);
4570 if (!oldmedium.isNull())
4571 oldmedium->i_removeBackReference(mData->mUuid);
4572 if (!pMedium.isNull())
4573 {
4574 pMedium->i_addBackReference(mData->mUuid);
4575
4576 mediumLock.release();
4577 multiLock.release();
4578 i_addMediumToRegistry(pMedium);
4579 multiLock.acquire();
4580 mediumLock.acquire();
4581 }
4582
4583 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4584 pAttach->i_updateMedium(pMedium);
4585 }
4586
4587 i_setModified(IsModified_Storage);
4588
4589 mediumLock.release();
4590 multiLock.release();
4591 HRESULT rc = i_onMediumChange(pAttach, aForce);
4592 multiLock.acquire();
4593 mediumLock.acquire();
4594
4595 /* On error roll back this change only. */
4596 if (FAILED(rc))
4597 {
4598 if (!pMedium.isNull())
4599 pMedium->i_removeBackReference(mData->mUuid);
4600 pAttach = i_findAttachment(*mMediumAttachments.data(),
4601 aName,
4602 aControllerPort,
4603 aDevice);
4604 /* If the attachment is gone in the meantime, bail out. */
4605 if (pAttach.isNull())
4606 return rc;
4607 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4608 if (!oldmedium.isNull())
4609 oldmedium->i_addBackReference(mData->mUuid);
4610 pAttach->i_updateMedium(oldmedium);
4611 }
4612
4613 mediumLock.release();
4614 multiLock.release();
4615
4616 /* Save modified registries, but skip this machine as it's the caller's
4617 * job to save its settings like all other settings changes. */
4618 mParent->i_unmarkRegistryModified(i_getId());
4619 mParent->i_saveModifiedRegistries();
4620
4621 return rc;
4622}
4623HRESULT Machine::getMedium(const com::Utf8Str &aName,
4624 LONG aControllerPort,
4625 LONG aDevice,
4626 ComPtr<IMedium> &aMedium)
4627{
4628 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4629 aName.c_str(), aControllerPort, aDevice));
4630
4631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 aMedium = NULL;
4634
4635 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4636 aName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4642 aDevice, aControllerPort, aName.c_str());
4643
4644 aMedium = pAttach->i_getMedium();
4645
4646 return S_OK;
4647}
4648
4649HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4650{
4651 if (aSlot < RT_ELEMENTS(mSerialPorts))
4652 {
4653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4654 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4655 return S_OK;
4656 }
4657 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4658}
4659
4660HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4661{
4662 if (aSlot < RT_ELEMENTS(mParallelPorts))
4663 {
4664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4665 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4666 return S_OK;
4667 }
4668 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4669}
4670
4671
4672HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4673{
4674 /* Do not assert if slot is out of range, just return the advertised
4675 status. testdriver/vbox.py triggers this in logVmInfo. */
4676 if (aSlot >= mNetworkAdapters.size())
4677 return setError(E_INVALIDARG,
4678 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4679 aSlot, mNetworkAdapters.size());
4680
4681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4684
4685 return S_OK;
4686}
4687
4688HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4689{
4690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4691
4692 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4693 size_t i = 0;
4694 for (settings::StringsMap::const_iterator
4695 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4696 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4697 ++it, ++i)
4698 aKeys[i] = it->first;
4699
4700 return S_OK;
4701}
4702
4703 /**
4704 * @note Locks this object for reading.
4705 */
4706HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4707 com::Utf8Str &aValue)
4708{
4709 /* start with nothing found */
4710 aValue = "";
4711
4712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4713
4714 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4715 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4716 // found:
4717 aValue = it->second; // source is a Utf8Str
4718
4719 /* return the result to caller (may be empty) */
4720 return S_OK;
4721}
4722
4723 /**
4724 * @note Locks mParent for writing + this object for writing.
4725 */
4726HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4727{
4728 /* Because control characters in aKey have caused problems in the settings
4729 * they are rejected unless the key should be deleted. */
4730 if (!aValue.isEmpty())
4731 {
4732 for (size_t i = 0; i < aKey.length(); ++i)
4733 {
4734 char ch = aKey[i];
4735 if (RTLocCIsCntrl(ch))
4736 return E_INVALIDARG;
4737 }
4738 }
4739
4740 Utf8Str strOldValue; // empty
4741
4742 // locking note: we only hold the read lock briefly to look up the old value,
4743 // then release it and call the onExtraCanChange callbacks. There is a small
4744 // chance of a race insofar as the callback might be called twice if two callers
4745 // change the same key at the same time, but that's a much better solution
4746 // than the deadlock we had here before. The actual changing of the extradata
4747 // is then performed under the write lock and race-free.
4748
4749 // look up the old value first; if nothing has changed then we need not do anything
4750 {
4751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4752
4753 // For snapshots don't even think about allowing changes, extradata
4754 // is global for a machine, so there is nothing snapshot specific.
4755 if (i_isSnapshotMachine())
4756 return setError(VBOX_E_INVALID_VM_STATE,
4757 tr("Cannot set extradata for a snapshot"));
4758
4759 // check if the right IMachine instance is used
4760 if (mData->mRegistered && !i_isSessionMachine())
4761 return setError(VBOX_E_INVALID_VM_STATE,
4762 tr("Cannot set extradata for an immutable machine"));
4763
4764 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4765 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4766 strOldValue = it->second;
4767 }
4768
4769 bool fChanged;
4770 if ((fChanged = (strOldValue != aValue)))
4771 {
4772 // ask for permission from all listeners outside the locks;
4773 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4774 // lock to copy the list of callbacks to invoke
4775 Bstr bstrError;
4776 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4777 {
4778 const char *sep = bstrError.isEmpty() ? "" : ": ";
4779 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4780 return setError(E_ACCESSDENIED,
4781 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4782 aKey.c_str(),
4783 aValue.c_str(),
4784 sep,
4785 bstrError.raw());
4786 }
4787
4788 // data is changing and change not vetoed: then write it out under the lock
4789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4790
4791 if (aValue.isEmpty())
4792 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4793 else
4794 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4795 // creates a new key if needed
4796
4797 bool fNeedsGlobalSaveSettings = false;
4798 // This saving of settings is tricky: there is no "old state" for the
4799 // extradata items at all (unlike all other settings), so the old/new
4800 // settings comparison would give a wrong result!
4801 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4802
4803 if (fNeedsGlobalSaveSettings)
4804 {
4805 // save the global settings; for that we should hold only the VirtualBox lock
4806 alock.release();
4807 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4808 mParent->i_saveSettings();
4809 }
4810 }
4811
4812 // fire notification outside the lock
4813 if (fChanged)
4814 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4815
4816 return S_OK;
4817}
4818
4819HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4820{
4821 aProgress = NULL;
4822 NOREF(aSettingsFilePath);
4823 ReturnComNotImplemented();
4824}
4825
4826HRESULT Machine::saveSettings()
4827{
4828 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4831 if (FAILED(rc)) return rc;
4832
4833 /* the settings file path may never be null */
4834 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4835
4836 /* save all VM data excluding snapshots */
4837 bool fNeedsGlobalSaveSettings = false;
4838 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4839 mlock.release();
4840
4841 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4842 {
4843 // save the global settings; for that we should hold only the VirtualBox lock
4844 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4845 rc = mParent->i_saveSettings();
4846 }
4847
4848 return rc;
4849}
4850
4851
4852HRESULT Machine::discardSettings()
4853{
4854 /*
4855 * We need to take the machine list lock here as well as the machine one
4856 * or we'll get into trouble should any media stuff require rolling back.
4857 *
4858 * Details:
4859 *
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4861 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4862 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
4863 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4864 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4865 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4866 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4867 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4869 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4870 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4872 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4873 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
4874 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
4875 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4876 * 0:005> k
4877 * # Child-SP RetAddr Call Site
4878 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4879 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4880 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4881 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4882 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4883 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4884 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4885 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4886 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4887 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4888 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4889 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4890 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4891 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4892 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4893 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4894 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4895 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4896 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4897 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4898 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4899 *
4900 */
4901 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4903
4904 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4905 if (FAILED(rc)) return rc;
4906
4907 /*
4908 * during this rollback, the session will be notified if data has
4909 * been actually changed
4910 */
4911 i_rollback(true /* aNotify */);
4912
4913 return S_OK;
4914}
4915
4916/** @note Locks objects! */
4917HRESULT Machine::unregister(AutoCaller &autoCaller,
4918 CleanupMode_T aCleanupMode,
4919 std::vector<ComPtr<IMedium> > &aMedia)
4920{
4921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4922
4923 Guid id(i_getId());
4924
4925 if (mData->mSession.mState != SessionState_Unlocked)
4926 return setError(VBOX_E_INVALID_OBJECT_STATE,
4927 tr("Cannot unregister the machine '%s' while it is locked"),
4928 mUserData->s.strName.c_str());
4929
4930 // wait for state dependents to drop to zero
4931 i_ensureNoStateDependencies(alock);
4932
4933 if (!mData->mAccessible)
4934 {
4935 // inaccessible machines can only be unregistered; uninitialize ourselves
4936 // here because currently there may be no unregistered that are inaccessible
4937 // (this state combination is not supported). Note releasing the caller and
4938 // leaving the lock before calling uninit()
4939 alock.release();
4940 autoCaller.release();
4941
4942 uninit();
4943
4944 mParent->i_unregisterMachine(this, id);
4945 // calls VirtualBox::i_saveSettings()
4946
4947 return S_OK;
4948 }
4949
4950 HRESULT rc = S_OK;
4951 mData->llFilesToDelete.clear();
4952
4953 if (!mSSData->strStateFilePath.isEmpty())
4954 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4955
4956 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4957 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4958 mData->llFilesToDelete.push_back(strNVRAMFile);
4959
4960 // This list collects the medium objects from all medium attachments
4961 // which we will detach from the machine and its snapshots, in a specific
4962 // order which allows for closing all media without getting "media in use"
4963 // errors, simply by going through the list from the front to the back:
4964 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4965 // and must be closed before the parent media from the snapshots, or closing the parents
4966 // will fail because they still have children);
4967 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4968 // the root ("first") snapshot of the machine.
4969 MediaList llMedia;
4970
4971 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4972 && mMediumAttachments->size()
4973 )
4974 {
4975 // we have media attachments: detach them all and add the Medium objects to our list
4976 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4977 }
4978
4979 if (mData->mFirstSnapshot)
4980 {
4981 // add the media from the medium attachments of the snapshots to llMedia
4982 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4983 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4984 // into the children first
4985
4986 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4987 MachineState_T oldState = mData->mMachineState;
4988 mData->mMachineState = MachineState_DeletingSnapshot;
4989
4990 // make a copy of the first snapshot reference so the refcount does not
4991 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4992 // (would hang due to the AutoCaller voodoo)
4993 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4994
4995 // GO!
4996 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4997
4998 mData->mMachineState = oldState;
4999 }
5000
5001 if (FAILED(rc))
5002 {
5003 i_rollbackMedia();
5004 return rc;
5005 }
5006
5007 // commit all the media changes made above
5008 i_commitMedia();
5009
5010 mData->mRegistered = false;
5011
5012 // machine lock no longer needed
5013 alock.release();
5014
5015 /* Make sure that the settings of the current VM are not saved, because
5016 * they are rather crippled at this point to meet the cleanup expectations
5017 * and there's no point destroying the VM config on disk just because. */
5018 mParent->i_unmarkRegistryModified(id);
5019
5020 // return media to caller
5021 aMedia.resize(llMedia.size());
5022 size_t i = 0;
5023 for (MediaList::const_iterator
5024 it = llMedia.begin();
5025 it != llMedia.end();
5026 ++it, ++i)
5027 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5028
5029 mParent->i_unregisterMachine(this, id);
5030 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5031
5032 return S_OK;
5033}
5034
5035/**
5036 * Task record for deleting a machine config.
5037 */
5038class Machine::DeleteConfigTask
5039 : public Machine::Task
5040{
5041public:
5042 DeleteConfigTask(Machine *m,
5043 Progress *p,
5044 const Utf8Str &t,
5045 const RTCList<ComPtr<IMedium> > &llMediums,
5046 const StringsList &llFilesToDelete)
5047 : Task(m, p, t),
5048 m_llMediums(llMediums),
5049 m_llFilesToDelete(llFilesToDelete)
5050 {}
5051
5052private:
5053 void handler()
5054 {
5055 try
5056 {
5057 m_pMachine->i_deleteConfigHandler(*this);
5058 }
5059 catch (...)
5060 {
5061 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5062 }
5063 }
5064
5065 RTCList<ComPtr<IMedium> > m_llMediums;
5066 StringsList m_llFilesToDelete;
5067
5068 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5069};
5070
5071/**
5072 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5073 * SessionMachine::taskHandler().
5074 *
5075 * @note Locks this object for writing.
5076 *
5077 * @param task
5078 * @return
5079 */
5080void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5081{
5082 LogFlowThisFuncEnter();
5083
5084 AutoCaller autoCaller(this);
5085 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5086 if (FAILED(autoCaller.rc()))
5087 {
5088 /* we might have been uninitialized because the session was accidentally
5089 * closed by the client, so don't assert */
5090 HRESULT rc = setError(E_FAIL,
5091 tr("The session has been accidentally closed"));
5092 task.m_pProgress->i_notifyComplete(rc);
5093 LogFlowThisFuncLeave();
5094 return;
5095 }
5096
5097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5098
5099 HRESULT rc = S_OK;
5100
5101 try
5102 {
5103 ULONG uLogHistoryCount = 3;
5104 ComPtr<ISystemProperties> systemProperties;
5105 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5106 if (FAILED(rc)) throw rc;
5107
5108 if (!systemProperties.isNull())
5109 {
5110 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5111 if (FAILED(rc)) throw rc;
5112 }
5113
5114 MachineState_T oldState = mData->mMachineState;
5115 i_setMachineState(MachineState_SettingUp);
5116 alock.release();
5117 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5118 {
5119 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5120 {
5121 AutoCaller mac(pMedium);
5122 if (FAILED(mac.rc())) throw mac.rc();
5123 Utf8Str strLocation = pMedium->i_getLocationFull();
5124 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5125 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5126 if (FAILED(rc)) throw rc;
5127 }
5128 if (pMedium->i_isMediumFormatFile())
5129 {
5130 ComPtr<IProgress> pProgress2;
5131 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5132 if (FAILED(rc)) throw rc;
5133 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5134 if (FAILED(rc)) throw rc;
5135 }
5136
5137 /* Close the medium, deliberately without checking the return
5138 * code, and without leaving any trace in the error info, as
5139 * a failure here is a very minor issue, which shouldn't happen
5140 * as above we even managed to delete the medium. */
5141 {
5142 ErrorInfoKeeper eik;
5143 pMedium->Close();
5144 }
5145 }
5146 i_setMachineState(oldState);
5147 alock.acquire();
5148
5149 // delete the files pushed on the task list by Machine::Delete()
5150 // (this includes saved states of the machine and snapshots and
5151 // medium storage files from the IMedium list passed in, and the
5152 // machine XML file)
5153 for (StringsList::const_iterator
5154 it = task.m_llFilesToDelete.begin();
5155 it != task.m_llFilesToDelete.end();
5156 ++it)
5157 {
5158 const Utf8Str &strFile = *it;
5159 LogFunc(("Deleting file %s\n", strFile.c_str()));
5160 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5161 if (FAILED(rc)) throw rc;
5162
5163 int vrc = RTFileDelete(strFile.c_str());
5164 if (RT_FAILURE(vrc))
5165 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5166 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5167 }
5168
5169 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5170 if (FAILED(rc)) throw rc;
5171
5172 /* delete the settings only when the file actually exists */
5173 if (mData->pMachineConfigFile->fileExists())
5174 {
5175 /* Delete any backup or uncommitted XML files. Ignore failures.
5176 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5177 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5178 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5179 RTFileDelete(otherXml.c_str());
5180 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5181 RTFileDelete(otherXml.c_str());
5182
5183 /* delete the Logs folder, nothing important should be left
5184 * there (we don't check for errors because the user might have
5185 * some private files there that we don't want to delete) */
5186 Utf8Str logFolder;
5187 getLogFolder(logFolder);
5188 Assert(logFolder.length());
5189 if (RTDirExists(logFolder.c_str()))
5190 {
5191 /* Delete all VBox.log[.N] files from the Logs folder
5192 * (this must be in sync with the rotation logic in
5193 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5194 * files that may have been created by the GUI. */
5195 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5196 RTFileDelete(log.c_str());
5197 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5198 RTFileDelete(log.c_str());
5199 for (ULONG i = uLogHistoryCount; i > 0; i--)
5200 {
5201 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5202 RTFileDelete(log.c_str());
5203 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5204 RTFileDelete(log.c_str());
5205 }
5206 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5207 RTFileDelete(log.c_str());
5208#if defined(RT_OS_WINDOWS)
5209 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5210 RTFileDelete(log.c_str());
5211 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5212 RTFileDelete(log.c_str());
5213#endif
5214
5215 RTDirRemove(logFolder.c_str());
5216 }
5217
5218 /* delete the Snapshots folder, nothing important should be left
5219 * there (we don't check for errors because the user might have
5220 * some private files there that we don't want to delete) */
5221 Utf8Str strFullSnapshotFolder;
5222 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5223 Assert(!strFullSnapshotFolder.isEmpty());
5224 if (RTDirExists(strFullSnapshotFolder.c_str()))
5225 RTDirRemove(strFullSnapshotFolder.c_str());
5226
5227 // delete the directory that contains the settings file, but only
5228 // if it matches the VM name
5229 Utf8Str settingsDir;
5230 if (i_isInOwnDir(&settingsDir))
5231 RTDirRemove(settingsDir.c_str());
5232 }
5233
5234 alock.release();
5235
5236 mParent->i_saveModifiedRegistries();
5237 }
5238 catch (HRESULT aRC) { rc = aRC; }
5239
5240 task.m_pProgress->i_notifyComplete(rc);
5241
5242 LogFlowThisFuncLeave();
5243}
5244
5245HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5246{
5247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5248
5249 HRESULT rc = i_checkStateDependency(MutableStateDep);
5250 if (FAILED(rc)) return rc;
5251
5252 if (mData->mRegistered)
5253 return setError(VBOX_E_INVALID_VM_STATE,
5254 tr("Cannot delete settings of a registered machine"));
5255
5256 // collect files to delete
5257 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5258 // machine config file
5259 if (mData->pMachineConfigFile->fileExists())
5260 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5261 // backup of machine config file
5262 Utf8Str strTmp(mData->m_strConfigFileFull);
5263 strTmp.append("-prev");
5264 if (RTFileExists(strTmp.c_str()))
5265 llFilesToDelete.push_back(strTmp);
5266
5267 RTCList<ComPtr<IMedium> > llMediums;
5268 for (size_t i = 0; i < aMedia.size(); ++i)
5269 {
5270 IMedium *pIMedium(aMedia[i]);
5271 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5272 if (pMedium.isNull())
5273 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5274 SafeArray<BSTR> ids;
5275 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5276 if (FAILED(rc)) return rc;
5277 /* At this point the medium should not have any back references
5278 * anymore. If it has it is attached to another VM and *must* not
5279 * deleted. */
5280 if (ids.size() < 1)
5281 llMediums.append(pMedium);
5282 }
5283
5284 ComObjPtr<Progress> pProgress;
5285 pProgress.createObject();
5286 rc = pProgress->init(i_getVirtualBox(),
5287 static_cast<IMachine*>(this) /* aInitiator */,
5288 tr("Deleting files"),
5289 true /* fCancellable */,
5290 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5291 tr("Collecting file inventory"));
5292 if (FAILED(rc))
5293 return rc;
5294
5295 /* create and start the task on a separate thread (note that it will not
5296 * start working until we release alock) */
5297 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5298 rc = pTask->createThread();
5299 pTask = NULL;
5300 if (FAILED(rc))
5301 return rc;
5302
5303 pProgress.queryInterfaceTo(aProgress.asOutParam());
5304
5305 LogFlowFuncLeave();
5306
5307 return S_OK;
5308}
5309
5310HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5311{
5312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5313
5314 ComObjPtr<Snapshot> pSnapshot;
5315 HRESULT rc;
5316
5317 if (aNameOrId.isEmpty())
5318 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5319 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5320 else
5321 {
5322 Guid uuid(aNameOrId);
5323 if (uuid.isValid())
5324 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5325 else
5326 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5327 }
5328 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5329
5330 return rc;
5331}
5332
5333HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5334 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5335{
5336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5337
5338 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5339 if (FAILED(rc)) return rc;
5340
5341 ComObjPtr<SharedFolder> sharedFolder;
5342 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5343 if (SUCCEEDED(rc))
5344 return setError(VBOX_E_OBJECT_IN_USE,
5345 tr("Shared folder named '%s' already exists"),
5346 aName.c_str());
5347
5348 sharedFolder.createObject();
5349 rc = sharedFolder->init(i_getMachine(),
5350 aName,
5351 aHostPath,
5352 !!aWritable,
5353 !!aAutomount,
5354 aAutoMountPoint,
5355 true /* fFailOnError */);
5356 if (FAILED(rc)) return rc;
5357
5358 i_setModified(IsModified_SharedFolders);
5359 mHWData.backup();
5360 mHWData->mSharedFolders.push_back(sharedFolder);
5361
5362 /* inform the direct session if any */
5363 alock.release();
5364 i_onSharedFolderChange();
5365
5366 return S_OK;
5367}
5368
5369HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5370{
5371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5372
5373 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5374 if (FAILED(rc)) return rc;
5375
5376 ComObjPtr<SharedFolder> sharedFolder;
5377 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5378 if (FAILED(rc)) return rc;
5379
5380 i_setModified(IsModified_SharedFolders);
5381 mHWData.backup();
5382 mHWData->mSharedFolders.remove(sharedFolder);
5383
5384 /* inform the direct session if any */
5385 alock.release();
5386 i_onSharedFolderChange();
5387
5388 return S_OK;
5389}
5390
5391HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5392{
5393 /* start with No */
5394 *aCanShow = FALSE;
5395
5396 ComPtr<IInternalSessionControl> directControl;
5397 {
5398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5399
5400 if (mData->mSession.mState != SessionState_Locked)
5401 return setError(VBOX_E_INVALID_VM_STATE,
5402 tr("Machine is not locked for session (session state: %s)"),
5403 Global::stringifySessionState(mData->mSession.mState));
5404
5405 if (mData->mSession.mLockType == LockType_VM)
5406 directControl = mData->mSession.mDirectControl;
5407 }
5408
5409 /* ignore calls made after #OnSessionEnd() is called */
5410 if (!directControl)
5411 return S_OK;
5412
5413 LONG64 dummy;
5414 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5415}
5416
5417HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5418{
5419 ComPtr<IInternalSessionControl> directControl;
5420 {
5421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5422
5423 if (mData->mSession.mState != SessionState_Locked)
5424 return setError(E_FAIL,
5425 tr("Machine is not locked for session (session state: %s)"),
5426 Global::stringifySessionState(mData->mSession.mState));
5427
5428 if (mData->mSession.mLockType == LockType_VM)
5429 directControl = mData->mSession.mDirectControl;
5430 }
5431
5432 /* ignore calls made after #OnSessionEnd() is called */
5433 if (!directControl)
5434 return S_OK;
5435
5436 BOOL dummy;
5437 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5438}
5439
5440#ifdef VBOX_WITH_GUEST_PROPS
5441/**
5442 * Look up a guest property in VBoxSVC's internal structures.
5443 */
5444HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5445 com::Utf8Str &aValue,
5446 LONG64 *aTimestamp,
5447 com::Utf8Str &aFlags) const
5448{
5449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5450
5451 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5452 if (it != mHWData->mGuestProperties.end())
5453 {
5454 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5455 aValue = it->second.strValue;
5456 *aTimestamp = it->second.mTimestamp;
5457 GuestPropWriteFlags(it->second.mFlags, szFlags);
5458 aFlags = Utf8Str(szFlags);
5459 }
5460
5461 return S_OK;
5462}
5463
5464/**
5465 * Query the VM that a guest property belongs to for the property.
5466 * @returns E_ACCESSDENIED if the VM process is not available or not
5467 * currently handling queries and the lookup should then be done in
5468 * VBoxSVC.
5469 */
5470HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5471 com::Utf8Str &aValue,
5472 LONG64 *aTimestamp,
5473 com::Utf8Str &aFlags) const
5474{
5475 HRESULT rc = S_OK;
5476 Bstr bstrValue;
5477 Bstr bstrFlags;
5478
5479 ComPtr<IInternalSessionControl> directControl;
5480 {
5481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5482 if (mData->mSession.mLockType == LockType_VM)
5483 directControl = mData->mSession.mDirectControl;
5484 }
5485
5486 /* ignore calls made after #OnSessionEnd() is called */
5487 if (!directControl)
5488 rc = E_ACCESSDENIED;
5489 else
5490 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5491 0 /* accessMode */,
5492 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5493
5494 aValue = bstrValue;
5495 aFlags = bstrFlags;
5496
5497 return rc;
5498}
5499#endif // VBOX_WITH_GUEST_PROPS
5500
5501HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5502 com::Utf8Str &aValue,
5503 LONG64 *aTimestamp,
5504 com::Utf8Str &aFlags)
5505{
5506#ifndef VBOX_WITH_GUEST_PROPS
5507 ReturnComNotImplemented();
5508#else // VBOX_WITH_GUEST_PROPS
5509
5510 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5511
5512 if (rc == E_ACCESSDENIED)
5513 /* The VM is not running or the service is not (yet) accessible */
5514 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5515 return rc;
5516#endif // VBOX_WITH_GUEST_PROPS
5517}
5518
5519HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5520{
5521 LONG64 dummyTimestamp;
5522 com::Utf8Str dummyFlags;
5523 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5524 return rc;
5525
5526}
5527HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5528{
5529 com::Utf8Str dummyFlags;
5530 com::Utf8Str dummyValue;
5531 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5532 return rc;
5533}
5534
5535#ifdef VBOX_WITH_GUEST_PROPS
5536/**
5537 * Set a guest property in VBoxSVC's internal structures.
5538 */
5539HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5540 const com::Utf8Str &aFlags, bool fDelete)
5541{
5542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5543 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5544 if (FAILED(rc)) return rc;
5545
5546 try
5547 {
5548 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5549 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5550 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5551
5552 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5553 if (it == mHWData->mGuestProperties.end())
5554 {
5555 if (!fDelete)
5556 {
5557 i_setModified(IsModified_MachineData);
5558 mHWData.backupEx();
5559
5560 RTTIMESPEC time;
5561 HWData::GuestProperty prop;
5562 prop.strValue = aValue;
5563 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5564 prop.mFlags = fFlags;
5565 mHWData->mGuestProperties[aName] = prop;
5566 }
5567 }
5568 else
5569 {
5570 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5571 {
5572 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5573 }
5574 else
5575 {
5576 i_setModified(IsModified_MachineData);
5577 mHWData.backupEx();
5578
5579 /* The backupEx() operation invalidates our iterator,
5580 * so get a new one. */
5581 it = mHWData->mGuestProperties.find(aName);
5582 Assert(it != mHWData->mGuestProperties.end());
5583
5584 if (!fDelete)
5585 {
5586 RTTIMESPEC time;
5587 it->second.strValue = aValue;
5588 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5589 it->second.mFlags = fFlags;
5590 }
5591 else
5592 mHWData->mGuestProperties.erase(it);
5593 }
5594 }
5595
5596 if (SUCCEEDED(rc))
5597 {
5598 alock.release();
5599
5600 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5601 }
5602 }
5603 catch (std::bad_alloc &)
5604 {
5605 rc = E_OUTOFMEMORY;
5606 }
5607
5608 return rc;
5609}
5610
5611/**
5612 * Set a property on the VM that that property belongs to.
5613 * @returns E_ACCESSDENIED if the VM process is not available or not
5614 * currently handling queries and the setting should then be done in
5615 * VBoxSVC.
5616 */
5617HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5618 const com::Utf8Str &aFlags, bool fDelete)
5619{
5620 HRESULT rc;
5621
5622 try
5623 {
5624 ComPtr<IInternalSessionControl> directControl;
5625 {
5626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5627 if (mData->mSession.mLockType == LockType_VM)
5628 directControl = mData->mSession.mDirectControl;
5629 }
5630
5631 Bstr dummy1; /* will not be changed (setter) */
5632 Bstr dummy2; /* will not be changed (setter) */
5633 LONG64 dummy64;
5634 if (!directControl)
5635 rc = E_ACCESSDENIED;
5636 else
5637 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5638 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5639 fDelete ? 2 : 1 /* accessMode */,
5640 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5641 }
5642 catch (std::bad_alloc &)
5643 {
5644 rc = E_OUTOFMEMORY;
5645 }
5646
5647 return rc;
5648}
5649#endif // VBOX_WITH_GUEST_PROPS
5650
5651HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5652 const com::Utf8Str &aFlags)
5653{
5654#ifndef VBOX_WITH_GUEST_PROPS
5655 ReturnComNotImplemented();
5656#else // VBOX_WITH_GUEST_PROPS
5657 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5658 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5659
5660 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5661 if (rc == E_ACCESSDENIED)
5662 /* The VM is not running or the service is not (yet) accessible */
5663 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5664 return rc;
5665#endif // VBOX_WITH_GUEST_PROPS
5666}
5667
5668HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5669{
5670 return setGuestProperty(aProperty, aValue, "");
5671}
5672
5673HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5674{
5675#ifndef VBOX_WITH_GUEST_PROPS
5676 ReturnComNotImplemented();
5677#else // VBOX_WITH_GUEST_PROPS
5678 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5679 if (rc == E_ACCESSDENIED)
5680 /* The VM is not running or the service is not (yet) accessible */
5681 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5682 return rc;
5683#endif // VBOX_WITH_GUEST_PROPS
5684}
5685
5686#ifdef VBOX_WITH_GUEST_PROPS
5687/**
5688 * Enumerate the guest properties in VBoxSVC's internal structures.
5689 */
5690HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5691 std::vector<com::Utf8Str> &aNames,
5692 std::vector<com::Utf8Str> &aValues,
5693 std::vector<LONG64> &aTimestamps,
5694 std::vector<com::Utf8Str> &aFlags)
5695{
5696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5697 Utf8Str strPatterns(aPatterns);
5698
5699 /*
5700 * Look for matching patterns and build up a list.
5701 */
5702 HWData::GuestPropertyMap propMap;
5703 for (HWData::GuestPropertyMap::const_iterator
5704 it = mHWData->mGuestProperties.begin();
5705 it != mHWData->mGuestProperties.end();
5706 ++it)
5707 {
5708 if ( strPatterns.isEmpty()
5709 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5710 RTSTR_MAX,
5711 it->first.c_str(),
5712 RTSTR_MAX,
5713 NULL)
5714 )
5715 propMap.insert(*it);
5716 }
5717
5718 alock.release();
5719
5720 /*
5721 * And build up the arrays for returning the property information.
5722 */
5723 size_t cEntries = propMap.size();
5724
5725 aNames.resize(cEntries);
5726 aValues.resize(cEntries);
5727 aTimestamps.resize(cEntries);
5728 aFlags.resize(cEntries);
5729
5730 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5731 size_t i = 0;
5732 for (HWData::GuestPropertyMap::const_iterator
5733 it = propMap.begin();
5734 it != propMap.end();
5735 ++it, ++i)
5736 {
5737 aNames[i] = it->first;
5738 aValues[i] = it->second.strValue;
5739 aTimestamps[i] = it->second.mTimestamp;
5740 GuestPropWriteFlags(it->second.mFlags, szFlags);
5741 aFlags[i] = Utf8Str(szFlags);
5742
5743 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5744 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5745 }
5746
5747 return S_OK;
5748}
5749
5750/**
5751 * Enumerate the properties managed by a VM.
5752 * @returns E_ACCESSDENIED if the VM process is not available or not
5753 * currently handling queries and the setting should then be done in
5754 * VBoxSVC.
5755 */
5756HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5757 std::vector<com::Utf8Str> &aNames,
5758 std::vector<com::Utf8Str> &aValues,
5759 std::vector<LONG64> &aTimestamps,
5760 std::vector<com::Utf8Str> &aFlags)
5761{
5762 HRESULT rc;
5763 ComPtr<IInternalSessionControl> directControl;
5764 {
5765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5766 if (mData->mSession.mLockType == LockType_VM)
5767 directControl = mData->mSession.mDirectControl;
5768 }
5769
5770 com::SafeArray<BSTR> bNames;
5771 com::SafeArray<BSTR> bValues;
5772 com::SafeArray<LONG64> bTimestamps;
5773 com::SafeArray<BSTR> bFlags;
5774
5775 if (!directControl)
5776 rc = E_ACCESSDENIED;
5777 else
5778 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5779 ComSafeArrayAsOutParam(bNames),
5780 ComSafeArrayAsOutParam(bValues),
5781 ComSafeArrayAsOutParam(bTimestamps),
5782 ComSafeArrayAsOutParam(bFlags));
5783 size_t i;
5784 aNames.resize(bNames.size());
5785 for (i = 0; i < bNames.size(); ++i)
5786 aNames[i] = Utf8Str(bNames[i]);
5787 aValues.resize(bValues.size());
5788 for (i = 0; i < bValues.size(); ++i)
5789 aValues[i] = Utf8Str(bValues[i]);
5790 aTimestamps.resize(bTimestamps.size());
5791 for (i = 0; i < bTimestamps.size(); ++i)
5792 aTimestamps[i] = bTimestamps[i];
5793 aFlags.resize(bFlags.size());
5794 for (i = 0; i < bFlags.size(); ++i)
5795 aFlags[i] = Utf8Str(bFlags[i]);
5796
5797 return rc;
5798}
5799#endif // VBOX_WITH_GUEST_PROPS
5800HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5801 std::vector<com::Utf8Str> &aNames,
5802 std::vector<com::Utf8Str> &aValues,
5803 std::vector<LONG64> &aTimestamps,
5804 std::vector<com::Utf8Str> &aFlags)
5805{
5806#ifndef VBOX_WITH_GUEST_PROPS
5807 ReturnComNotImplemented();
5808#else // VBOX_WITH_GUEST_PROPS
5809
5810 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5811
5812 if (rc == E_ACCESSDENIED)
5813 /* The VM is not running or the service is not (yet) accessible */
5814 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5815 return rc;
5816#endif // VBOX_WITH_GUEST_PROPS
5817}
5818
5819HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5820 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5821{
5822 MediumAttachmentList atts;
5823
5824 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5825 if (FAILED(rc)) return rc;
5826
5827 aMediumAttachments.resize(atts.size());
5828 size_t i = 0;
5829 for (MediumAttachmentList::const_iterator
5830 it = atts.begin();
5831 it != atts.end();
5832 ++it, ++i)
5833 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5834
5835 return S_OK;
5836}
5837
5838HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5839 LONG aControllerPort,
5840 LONG aDevice,
5841 ComPtr<IMediumAttachment> &aAttachment)
5842{
5843 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5844 aName.c_str(), aControllerPort, aDevice));
5845
5846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5847
5848 aAttachment = NULL;
5849
5850 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5851 aName,
5852 aControllerPort,
5853 aDevice);
5854 if (pAttach.isNull())
5855 return setError(VBOX_E_OBJECT_NOT_FOUND,
5856 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5857 aDevice, aControllerPort, aName.c_str());
5858
5859 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5860
5861 return S_OK;
5862}
5863
5864
5865HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5866 StorageBus_T aConnectionType,
5867 ComPtr<IStorageController> &aController)
5868{
5869 if ( (aConnectionType <= StorageBus_Null)
5870 || (aConnectionType > StorageBus_VirtioSCSI))
5871 return setError(E_INVALIDARG,
5872 tr("Invalid connection type: %d"),
5873 aConnectionType);
5874
5875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5876
5877 HRESULT rc = i_checkStateDependency(MutableStateDep);
5878 if (FAILED(rc)) return rc;
5879
5880 /* try to find one with the name first. */
5881 ComObjPtr<StorageController> ctrl;
5882
5883 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5884 if (SUCCEEDED(rc))
5885 return setError(VBOX_E_OBJECT_IN_USE,
5886 tr("Storage controller named '%s' already exists"),
5887 aName.c_str());
5888
5889 ctrl.createObject();
5890
5891 /* get a new instance number for the storage controller */
5892 ULONG ulInstance = 0;
5893 bool fBootable = true;
5894 for (StorageControllerList::const_iterator
5895 it = mStorageControllers->begin();
5896 it != mStorageControllers->end();
5897 ++it)
5898 {
5899 if ((*it)->i_getStorageBus() == aConnectionType)
5900 {
5901 ULONG ulCurInst = (*it)->i_getInstance();
5902
5903 if (ulCurInst >= ulInstance)
5904 ulInstance = ulCurInst + 1;
5905
5906 /* Only one controller of each type can be marked as bootable. */
5907 if ((*it)->i_getBootable())
5908 fBootable = false;
5909 }
5910 }
5911
5912 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5913 if (FAILED(rc)) return rc;
5914
5915 i_setModified(IsModified_Storage);
5916 mStorageControllers.backup();
5917 mStorageControllers->push_back(ctrl);
5918
5919 ctrl.queryInterfaceTo(aController.asOutParam());
5920
5921 /* inform the direct session if any */
5922 alock.release();
5923 i_onStorageControllerChange(i_getId(), aName);
5924
5925 return S_OK;
5926}
5927
5928HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5929 ComPtr<IStorageController> &aStorageController)
5930{
5931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5932
5933 ComObjPtr<StorageController> ctrl;
5934
5935 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5936 if (SUCCEEDED(rc))
5937 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5938
5939 return rc;
5940}
5941
5942HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5943 ULONG aInstance,
5944 ComPtr<IStorageController> &aStorageController)
5945{
5946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5947
5948 for (StorageControllerList::const_iterator
5949 it = mStorageControllers->begin();
5950 it != mStorageControllers->end();
5951 ++it)
5952 {
5953 if ( (*it)->i_getStorageBus() == aConnectionType
5954 && (*it)->i_getInstance() == aInstance)
5955 {
5956 (*it).queryInterfaceTo(aStorageController.asOutParam());
5957 return S_OK;
5958 }
5959 }
5960
5961 return setError(VBOX_E_OBJECT_NOT_FOUND,
5962 tr("Could not find a storage controller with instance number '%lu'"),
5963 aInstance);
5964}
5965
5966HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5967{
5968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5969
5970 HRESULT rc = i_checkStateDependency(MutableStateDep);
5971 if (FAILED(rc)) return rc;
5972
5973 ComObjPtr<StorageController> ctrl;
5974
5975 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5976 if (SUCCEEDED(rc))
5977 {
5978 /* Ensure that only one controller of each type is marked as bootable. */
5979 if (aBootable == TRUE)
5980 {
5981 for (StorageControllerList::const_iterator
5982 it = mStorageControllers->begin();
5983 it != mStorageControllers->end();
5984 ++it)
5985 {
5986 ComObjPtr<StorageController> aCtrl = (*it);
5987
5988 if ( (aCtrl->i_getName() != aName)
5989 && aCtrl->i_getBootable() == TRUE
5990 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5991 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5992 {
5993 aCtrl->i_setBootable(FALSE);
5994 break;
5995 }
5996 }
5997 }
5998
5999 if (SUCCEEDED(rc))
6000 {
6001 ctrl->i_setBootable(aBootable);
6002 i_setModified(IsModified_Storage);
6003 }
6004 }
6005
6006 if (SUCCEEDED(rc))
6007 {
6008 /* inform the direct session if any */
6009 alock.release();
6010 i_onStorageControllerChange(i_getId(), aName);
6011 }
6012
6013 return rc;
6014}
6015
6016HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6017{
6018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6019
6020 HRESULT rc = i_checkStateDependency(MutableStateDep);
6021 if (FAILED(rc)) return rc;
6022
6023 ComObjPtr<StorageController> ctrl;
6024 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6025 if (FAILED(rc)) return rc;
6026
6027 MediumAttachmentList llDetachedAttachments;
6028 {
6029 /* find all attached devices to the appropriate storage controller and detach them all */
6030 // make a temporary list because detachDevice invalidates iterators into
6031 // mMediumAttachments
6032 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6033
6034 for (MediumAttachmentList::const_iterator
6035 it = llAttachments2.begin();
6036 it != llAttachments2.end();
6037 ++it)
6038 {
6039 MediumAttachment *pAttachTemp = *it;
6040
6041 AutoCaller localAutoCaller(pAttachTemp);
6042 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6043
6044 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6045
6046 if (pAttachTemp->i_getControllerName() == aName)
6047 {
6048 llDetachedAttachments.push_back(pAttachTemp);
6049 rc = i_detachDevice(pAttachTemp, alock, NULL);
6050 if (FAILED(rc)) return rc;
6051 }
6052 }
6053 }
6054
6055 /* send event about detached devices before removing parent controller */
6056 for (MediumAttachmentList::const_iterator
6057 it = llDetachedAttachments.begin();
6058 it != llDetachedAttachments.end();
6059 ++it)
6060 {
6061 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6062 }
6063
6064 /* We can remove it now. */
6065 i_setModified(IsModified_Storage);
6066 mStorageControllers.backup();
6067
6068 ctrl->i_unshare();
6069
6070 mStorageControllers->remove(ctrl);
6071
6072 /* inform the direct session if any */
6073 alock.release();
6074 i_onStorageControllerChange(i_getId(), aName);
6075
6076 return S_OK;
6077}
6078
6079HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6080 ComPtr<IUSBController> &aController)
6081{
6082 if ( (aType <= USBControllerType_Null)
6083 || (aType >= USBControllerType_Last))
6084 return setError(E_INVALIDARG,
6085 tr("Invalid USB controller type: %d"),
6086 aType);
6087
6088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6089
6090 HRESULT rc = i_checkStateDependency(MutableStateDep);
6091 if (FAILED(rc)) return rc;
6092
6093 /* try to find one with the same type first. */
6094 ComObjPtr<USBController> ctrl;
6095
6096 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6097 if (SUCCEEDED(rc))
6098 return setError(VBOX_E_OBJECT_IN_USE,
6099 tr("USB controller named '%s' already exists"),
6100 aName.c_str());
6101
6102 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6103 ULONG maxInstances;
6104 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6105 if (FAILED(rc))
6106 return rc;
6107
6108 ULONG cInstances = i_getUSBControllerCountByType(aType);
6109 if (cInstances >= maxInstances)
6110 return setError(E_INVALIDARG,
6111 tr("Too many USB controllers of this type"));
6112
6113 ctrl.createObject();
6114
6115 rc = ctrl->init(this, aName, aType);
6116 if (FAILED(rc)) return rc;
6117
6118 i_setModified(IsModified_USB);
6119 mUSBControllers.backup();
6120 mUSBControllers->push_back(ctrl);
6121
6122 ctrl.queryInterfaceTo(aController.asOutParam());
6123
6124 /* inform the direct session if any */
6125 alock.release();
6126 i_onUSBControllerChange();
6127
6128 return S_OK;
6129}
6130
6131HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6132{
6133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6134
6135 ComObjPtr<USBController> ctrl;
6136
6137 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6138 if (SUCCEEDED(rc))
6139 ctrl.queryInterfaceTo(aController.asOutParam());
6140
6141 return rc;
6142}
6143
6144HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6145 ULONG *aControllers)
6146{
6147 if ( (aType <= USBControllerType_Null)
6148 || (aType >= USBControllerType_Last))
6149 return setError(E_INVALIDARG,
6150 tr("Invalid USB controller type: %d"),
6151 aType);
6152
6153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6154
6155 ComObjPtr<USBController> ctrl;
6156
6157 *aControllers = i_getUSBControllerCountByType(aType);
6158
6159 return S_OK;
6160}
6161
6162HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6163{
6164
6165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6166
6167 HRESULT rc = i_checkStateDependency(MutableStateDep);
6168 if (FAILED(rc)) return rc;
6169
6170 ComObjPtr<USBController> ctrl;
6171 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6172 if (FAILED(rc)) return rc;
6173
6174 i_setModified(IsModified_USB);
6175 mUSBControllers.backup();
6176
6177 ctrl->i_unshare();
6178
6179 mUSBControllers->remove(ctrl);
6180
6181 /* inform the direct session if any */
6182 alock.release();
6183 i_onUSBControllerChange();
6184
6185 return S_OK;
6186}
6187
6188HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6189 ULONG *aOriginX,
6190 ULONG *aOriginY,
6191 ULONG *aWidth,
6192 ULONG *aHeight,
6193 BOOL *aEnabled)
6194{
6195 uint32_t u32OriginX= 0;
6196 uint32_t u32OriginY= 0;
6197 uint32_t u32Width = 0;
6198 uint32_t u32Height = 0;
6199 uint16_t u16Flags = 0;
6200
6201 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6202 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6203 if (RT_FAILURE(vrc))
6204 {
6205#ifdef RT_OS_WINDOWS
6206 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6207 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6208 * So just assign fEnable to TRUE again.
6209 * The right fix would be to change GUI API wrappers to make sure that parameters
6210 * are changed only if API succeeds.
6211 */
6212 *aEnabled = TRUE;
6213#endif
6214 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6215 tr("Saved guest size is not available (%Rrc)"),
6216 vrc);
6217 }
6218
6219 *aOriginX = u32OriginX;
6220 *aOriginY = u32OriginY;
6221 *aWidth = u32Width;
6222 *aHeight = u32Height;
6223 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6224
6225 return S_OK;
6226}
6227
6228HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6229 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6230{
6231 if (aScreenId != 0)
6232 return E_NOTIMPL;
6233
6234 if ( aBitmapFormat != BitmapFormat_BGR0
6235 && aBitmapFormat != BitmapFormat_BGRA
6236 && aBitmapFormat != BitmapFormat_RGBA
6237 && aBitmapFormat != BitmapFormat_PNG)
6238 return setError(E_NOTIMPL,
6239 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6240
6241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6242
6243 uint8_t *pu8Data = NULL;
6244 uint32_t cbData = 0;
6245 uint32_t u32Width = 0;
6246 uint32_t u32Height = 0;
6247
6248 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6249
6250 if (RT_FAILURE(vrc))
6251 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6252 tr("Saved thumbnail data is not available (%Rrc)"),
6253 vrc);
6254
6255 HRESULT hr = S_OK;
6256
6257 *aWidth = u32Width;
6258 *aHeight = u32Height;
6259
6260 if (cbData > 0)
6261 {
6262 /* Convert pixels to the format expected by the API caller. */
6263 if (aBitmapFormat == BitmapFormat_BGR0)
6264 {
6265 /* [0] B, [1] G, [2] R, [3] 0. */
6266 aData.resize(cbData);
6267 memcpy(&aData.front(), pu8Data, cbData);
6268 }
6269 else if (aBitmapFormat == BitmapFormat_BGRA)
6270 {
6271 /* [0] B, [1] G, [2] R, [3] A. */
6272 aData.resize(cbData);
6273 for (uint32_t i = 0; i < cbData; i += 4)
6274 {
6275 aData[i] = pu8Data[i];
6276 aData[i + 1] = pu8Data[i + 1];
6277 aData[i + 2] = pu8Data[i + 2];
6278 aData[i + 3] = 0xff;
6279 }
6280 }
6281 else if (aBitmapFormat == BitmapFormat_RGBA)
6282 {
6283 /* [0] R, [1] G, [2] B, [3] A. */
6284 aData.resize(cbData);
6285 for (uint32_t i = 0; i < cbData; i += 4)
6286 {
6287 aData[i] = pu8Data[i + 2];
6288 aData[i + 1] = pu8Data[i + 1];
6289 aData[i + 2] = pu8Data[i];
6290 aData[i + 3] = 0xff;
6291 }
6292 }
6293 else if (aBitmapFormat == BitmapFormat_PNG)
6294 {
6295 uint8_t *pu8PNG = NULL;
6296 uint32_t cbPNG = 0;
6297 uint32_t cxPNG = 0;
6298 uint32_t cyPNG = 0;
6299
6300 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6301
6302 if (RT_SUCCESS(vrc))
6303 {
6304 aData.resize(cbPNG);
6305 if (cbPNG)
6306 memcpy(&aData.front(), pu8PNG, cbPNG);
6307 }
6308 else
6309 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6310 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6311 vrc);
6312
6313 RTMemFree(pu8PNG);
6314 }
6315 }
6316
6317 freeSavedDisplayScreenshot(pu8Data);
6318
6319 return hr;
6320}
6321
6322HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6323 ULONG *aWidth,
6324 ULONG *aHeight,
6325 std::vector<BitmapFormat_T> &aBitmapFormats)
6326{
6327 if (aScreenId != 0)
6328 return E_NOTIMPL;
6329
6330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6331
6332 uint8_t *pu8Data = NULL;
6333 uint32_t cbData = 0;
6334 uint32_t u32Width = 0;
6335 uint32_t u32Height = 0;
6336
6337 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6338
6339 if (RT_FAILURE(vrc))
6340 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6341 tr("Saved screenshot data is not available (%Rrc)"),
6342 vrc);
6343
6344 *aWidth = u32Width;
6345 *aHeight = u32Height;
6346 aBitmapFormats.resize(1);
6347 aBitmapFormats[0] = BitmapFormat_PNG;
6348
6349 freeSavedDisplayScreenshot(pu8Data);
6350
6351 return S_OK;
6352}
6353
6354HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6355 BitmapFormat_T aBitmapFormat,
6356 ULONG *aWidth,
6357 ULONG *aHeight,
6358 std::vector<BYTE> &aData)
6359{
6360 if (aScreenId != 0)
6361 return E_NOTIMPL;
6362
6363 if (aBitmapFormat != BitmapFormat_PNG)
6364 return E_NOTIMPL;
6365
6366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6367
6368 uint8_t *pu8Data = NULL;
6369 uint32_t cbData = 0;
6370 uint32_t u32Width = 0;
6371 uint32_t u32Height = 0;
6372
6373 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6374
6375 if (RT_FAILURE(vrc))
6376 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6377 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6378 vrc);
6379
6380 *aWidth = u32Width;
6381 *aHeight = u32Height;
6382
6383 aData.resize(cbData);
6384 if (cbData)
6385 memcpy(&aData.front(), pu8Data, cbData);
6386
6387 freeSavedDisplayScreenshot(pu8Data);
6388
6389 return S_OK;
6390}
6391
6392HRESULT Machine::hotPlugCPU(ULONG aCpu)
6393{
6394 HRESULT rc = S_OK;
6395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6396
6397 if (!mHWData->mCPUHotPlugEnabled)
6398 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6399
6400 if (aCpu >= mHWData->mCPUCount)
6401 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6402
6403 if (mHWData->mCPUAttached[aCpu])
6404 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6405
6406 alock.release();
6407 rc = i_onCPUChange(aCpu, false);
6408 alock.acquire();
6409 if (FAILED(rc)) return rc;
6410
6411 i_setModified(IsModified_MachineData);
6412 mHWData.backup();
6413 mHWData->mCPUAttached[aCpu] = true;
6414
6415 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6416 if (Global::IsOnline(mData->mMachineState))
6417 i_saveSettings(NULL, alock);
6418
6419 return S_OK;
6420}
6421
6422HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6423{
6424 HRESULT rc = S_OK;
6425
6426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 if (!mHWData->mCPUHotPlugEnabled)
6429 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6430
6431 if (aCpu >= SchemaDefs::MaxCPUCount)
6432 return setError(E_INVALIDARG,
6433 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6434 SchemaDefs::MaxCPUCount);
6435
6436 if (!mHWData->mCPUAttached[aCpu])
6437 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6438
6439 /* CPU 0 can't be detached */
6440 if (aCpu == 0)
6441 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6442
6443 alock.release();
6444 rc = i_onCPUChange(aCpu, true);
6445 alock.acquire();
6446 if (FAILED(rc)) return rc;
6447
6448 i_setModified(IsModified_MachineData);
6449 mHWData.backup();
6450 mHWData->mCPUAttached[aCpu] = false;
6451
6452 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6453 if (Global::IsOnline(mData->mMachineState))
6454 i_saveSettings(NULL, alock);
6455
6456 return S_OK;
6457}
6458
6459HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6460{
6461 *aAttached = false;
6462
6463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6464
6465 /* If hotplug is enabled the CPU is always enabled. */
6466 if (!mHWData->mCPUHotPlugEnabled)
6467 {
6468 if (aCpu < mHWData->mCPUCount)
6469 *aAttached = true;
6470 }
6471 else
6472 {
6473 if (aCpu < SchemaDefs::MaxCPUCount)
6474 *aAttached = mHWData->mCPUAttached[aCpu];
6475 }
6476
6477 return S_OK;
6478}
6479
6480HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6481{
6482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6483
6484 Utf8Str log = i_getLogFilename(aIdx);
6485 if (!RTFileExists(log.c_str()))
6486 log.setNull();
6487 aFilename = log;
6488
6489 return S_OK;
6490}
6491
6492HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6493{
6494 if (aSize < 0)
6495 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6496
6497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6498
6499 HRESULT rc = S_OK;
6500 Utf8Str log = i_getLogFilename(aIdx);
6501
6502 /* do not unnecessarily hold the lock while doing something which does
6503 * not need the lock and potentially takes a long time. */
6504 alock.release();
6505
6506 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6507 * keeps the SOAP reply size under 1M for the webservice (we're using
6508 * base64 encoded strings for binary data for years now, avoiding the
6509 * expansion of each byte array element to approx. 25 bytes of XML. */
6510 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6511 aData.resize(cbData);
6512
6513 RTFILE LogFile;
6514 int vrc = RTFileOpen(&LogFile, log.c_str(),
6515 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6516 if (RT_SUCCESS(vrc))
6517 {
6518 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6519 if (RT_SUCCESS(vrc))
6520 aData.resize(cbData);
6521 else
6522 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6523 tr("Could not read log file '%s' (%Rrc)"),
6524 log.c_str(), vrc);
6525 RTFileClose(LogFile);
6526 }
6527 else
6528 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6529 tr("Could not open log file '%s' (%Rrc)"),
6530 log.c_str(), vrc);
6531
6532 if (FAILED(rc))
6533 aData.resize(0);
6534
6535 return rc;
6536}
6537
6538
6539/**
6540 * Currently this method doesn't attach device to the running VM,
6541 * just makes sure it's plugged on next VM start.
6542 */
6543HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6544{
6545 // lock scope
6546 {
6547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6548
6549 HRESULT rc = i_checkStateDependency(MutableStateDep);
6550 if (FAILED(rc)) return rc;
6551
6552 ChipsetType_T aChipset = ChipsetType_PIIX3;
6553 COMGETTER(ChipsetType)(&aChipset);
6554
6555 if (aChipset != ChipsetType_ICH9)
6556 {
6557 return setError(E_INVALIDARG,
6558 tr("Host PCI attachment only supported with ICH9 chipset"));
6559 }
6560
6561 // check if device with this host PCI address already attached
6562 for (HWData::PCIDeviceAssignmentList::const_iterator
6563 it = mHWData->mPCIDeviceAssignments.begin();
6564 it != mHWData->mPCIDeviceAssignments.end();
6565 ++it)
6566 {
6567 LONG iHostAddress = -1;
6568 ComPtr<PCIDeviceAttachment> pAttach;
6569 pAttach = *it;
6570 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6571 if (iHostAddress == aHostAddress)
6572 return setError(E_INVALIDARG,
6573 tr("Device with host PCI address already attached to this VM"));
6574 }
6575
6576 ComObjPtr<PCIDeviceAttachment> pda;
6577 char name[32];
6578
6579 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6580 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6581 pda.createObject();
6582 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6583 i_setModified(IsModified_MachineData);
6584 mHWData.backup();
6585 mHWData->mPCIDeviceAssignments.push_back(pda);
6586 }
6587
6588 return S_OK;
6589}
6590
6591/**
6592 * Currently this method doesn't detach device from the running VM,
6593 * just makes sure it's not plugged on next VM start.
6594 */
6595HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6596{
6597 ComObjPtr<PCIDeviceAttachment> pAttach;
6598 bool fRemoved = false;
6599 HRESULT rc;
6600
6601 // lock scope
6602 {
6603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6604
6605 rc = i_checkStateDependency(MutableStateDep);
6606 if (FAILED(rc)) return rc;
6607
6608 for (HWData::PCIDeviceAssignmentList::const_iterator
6609 it = mHWData->mPCIDeviceAssignments.begin();
6610 it != mHWData->mPCIDeviceAssignments.end();
6611 ++it)
6612 {
6613 LONG iHostAddress = -1;
6614 pAttach = *it;
6615 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6616 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6617 {
6618 i_setModified(IsModified_MachineData);
6619 mHWData.backup();
6620 mHWData->mPCIDeviceAssignments.remove(pAttach);
6621 fRemoved = true;
6622 break;
6623 }
6624 }
6625 }
6626
6627
6628 /* Fire event outside of the lock */
6629 if (fRemoved)
6630 {
6631 Assert(!pAttach.isNull());
6632 ComPtr<IEventSource> es;
6633 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6634 Assert(SUCCEEDED(rc));
6635 Bstr mid;
6636 rc = this->COMGETTER(Id)(mid.asOutParam());
6637 Assert(SUCCEEDED(rc));
6638 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6639 }
6640
6641 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6642 tr("No host PCI device %08x attached"),
6643 aHostAddress
6644 );
6645}
6646
6647HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6648{
6649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6650
6651 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6652 size_t i = 0;
6653 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6654 it = mHWData->mPCIDeviceAssignments.begin();
6655 it != mHWData->mPCIDeviceAssignments.end();
6656 ++it, ++i)
6657 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6658
6659 return S_OK;
6660}
6661
6662HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6663{
6664 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6665
6666 return S_OK;
6667}
6668
6669HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6670{
6671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6672
6673 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6674
6675 return S_OK;
6676}
6677
6678HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6679{
6680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6681 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6682 if (SUCCEEDED(hrc))
6683 {
6684 hrc = mHWData.backupEx();
6685 if (SUCCEEDED(hrc))
6686 {
6687 i_setModified(IsModified_MachineData);
6688 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6689 }
6690 }
6691 return hrc;
6692}
6693
6694HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6695{
6696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6697 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6698 return S_OK;
6699}
6700
6701HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6702{
6703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6704 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6705 if (SUCCEEDED(hrc))
6706 {
6707 hrc = mHWData.backupEx();
6708 if (SUCCEEDED(hrc))
6709 {
6710 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6711 if (SUCCEEDED(hrc))
6712 i_setModified(IsModified_MachineData);
6713 }
6714 }
6715 return hrc;
6716}
6717
6718HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6719{
6720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6721
6722 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6723
6724 return S_OK;
6725}
6726
6727HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6728{
6729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6730 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6731 if (SUCCEEDED(hrc))
6732 {
6733 hrc = mHWData.backupEx();
6734 if (SUCCEEDED(hrc))
6735 {
6736 i_setModified(IsModified_MachineData);
6737 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6738 }
6739 }
6740 return hrc;
6741}
6742
6743HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6744{
6745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6746
6747 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6748
6749 return S_OK;
6750}
6751
6752HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6753{
6754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6755
6756 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6757 if ( SUCCEEDED(hrc)
6758 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6759 {
6760 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6761 int vrc;
6762
6763 if (aAutostartEnabled)
6764 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6765 else
6766 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6767
6768 if (RT_SUCCESS(vrc))
6769 {
6770 hrc = mHWData.backupEx();
6771 if (SUCCEEDED(hrc))
6772 {
6773 i_setModified(IsModified_MachineData);
6774 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6775 }
6776 }
6777 else if (vrc == VERR_NOT_SUPPORTED)
6778 hrc = setError(VBOX_E_NOT_SUPPORTED,
6779 tr("The VM autostart feature is not supported on this platform"));
6780 else if (vrc == VERR_PATH_NOT_FOUND)
6781 hrc = setError(E_FAIL,
6782 tr("The path to the autostart database is not set"));
6783 else
6784 hrc = setError(E_UNEXPECTED,
6785 aAutostartEnabled ?
6786 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6787 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6788 mUserData->s.strName.c_str(), vrc);
6789 }
6790 return hrc;
6791}
6792
6793HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6794{
6795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6796
6797 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6798
6799 return S_OK;
6800}
6801
6802HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6803{
6804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6805 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6806 if (SUCCEEDED(hrc))
6807 {
6808 hrc = mHWData.backupEx();
6809 if (SUCCEEDED(hrc))
6810 {
6811 i_setModified(IsModified_MachineData);
6812 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6813 }
6814 }
6815 return hrc;
6816}
6817
6818HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6819{
6820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6821
6822 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6823
6824 return S_OK;
6825}
6826
6827HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6828{
6829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6830 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6831 if ( SUCCEEDED(hrc)
6832 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6833 {
6834 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6835 int vrc;
6836
6837 if (aAutostopType != AutostopType_Disabled)
6838 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6839 else
6840 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6841
6842 if (RT_SUCCESS(vrc))
6843 {
6844 hrc = mHWData.backupEx();
6845 if (SUCCEEDED(hrc))
6846 {
6847 i_setModified(IsModified_MachineData);
6848 mHWData->mAutostart.enmAutostopType = aAutostopType;
6849 }
6850 }
6851 else if (vrc == VERR_NOT_SUPPORTED)
6852 hrc = setError(VBOX_E_NOT_SUPPORTED,
6853 tr("The VM autostop feature is not supported on this platform"));
6854 else if (vrc == VERR_PATH_NOT_FOUND)
6855 hrc = setError(E_FAIL,
6856 tr("The path to the autostart database is not set"));
6857 else
6858 hrc = setError(E_UNEXPECTED,
6859 aAutostopType != AutostopType_Disabled ?
6860 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6861 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6862 mUserData->s.strName.c_str(), vrc);
6863 }
6864 return hrc;
6865}
6866
6867HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6868{
6869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6870
6871 aDefaultFrontend = mHWData->mDefaultFrontend;
6872
6873 return S_OK;
6874}
6875
6876HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6877{
6878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6879 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6880 if (SUCCEEDED(hrc))
6881 {
6882 hrc = mHWData.backupEx();
6883 if (SUCCEEDED(hrc))
6884 {
6885 i_setModified(IsModified_MachineData);
6886 mHWData->mDefaultFrontend = aDefaultFrontend;
6887 }
6888 }
6889 return hrc;
6890}
6891
6892HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6893{
6894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6895 size_t cbIcon = mUserData->s.ovIcon.size();
6896 aIcon.resize(cbIcon);
6897 if (cbIcon)
6898 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6899 return S_OK;
6900}
6901
6902HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6903{
6904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6905 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6906 if (SUCCEEDED(hrc))
6907 {
6908 i_setModified(IsModified_MachineData);
6909 mUserData.backup();
6910 size_t cbIcon = aIcon.size();
6911 mUserData->s.ovIcon.resize(cbIcon);
6912 if (cbIcon)
6913 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6914 }
6915 return hrc;
6916}
6917
6918HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6919{
6920#ifdef VBOX_WITH_USB
6921 *aUSBProxyAvailable = true;
6922#else
6923 *aUSBProxyAvailable = false;
6924#endif
6925 return S_OK;
6926}
6927
6928HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6929{
6930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6931
6932 *aVMProcessPriority = mUserData->s.enmVMPriority;
6933
6934 return S_OK;
6935}
6936
6937HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6938{
6939 RT_NOREF(aVMProcessPriority);
6940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6941 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6942 if (SUCCEEDED(hrc))
6943 {
6944 hrc = mUserData.backupEx();
6945 if (SUCCEEDED(hrc))
6946 {
6947 i_setModified(IsModified_MachineData);
6948 mUserData->s.enmVMPriority = aVMProcessPriority;
6949 }
6950 }
6951 alock.release();
6952 if (SUCCEEDED(hrc))
6953 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6954 return hrc;
6955}
6956
6957HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6958 ComPtr<IProgress> &aProgress)
6959{
6960 ComObjPtr<Progress> pP;
6961 Progress *ppP = pP;
6962 IProgress *iP = static_cast<IProgress *>(ppP);
6963 IProgress **pProgress = &iP;
6964
6965 IMachine *pTarget = aTarget;
6966
6967 /* Convert the options. */
6968 RTCList<CloneOptions_T> optList;
6969 if (aOptions.size())
6970 for (size_t i = 0; i < aOptions.size(); ++i)
6971 optList.append(aOptions[i]);
6972
6973 if (optList.contains(CloneOptions_Link))
6974 {
6975 if (!i_isSnapshotMachine())
6976 return setError(E_INVALIDARG,
6977 tr("Linked clone can only be created from a snapshot"));
6978 if (aMode != CloneMode_MachineState)
6979 return setError(E_INVALIDARG,
6980 tr("Linked clone can only be created for a single machine state"));
6981 }
6982 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6983
6984 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6985
6986 HRESULT rc = pWorker->start(pProgress);
6987
6988 pP = static_cast<Progress *>(*pProgress);
6989 pP.queryInterfaceTo(aProgress.asOutParam());
6990
6991 return rc;
6992
6993}
6994
6995HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6996 const com::Utf8Str &aType,
6997 ComPtr<IProgress> &aProgress)
6998{
6999 LogFlowThisFuncEnter();
7000
7001 ComObjPtr<Progress> ptrProgress;
7002 HRESULT hrc = ptrProgress.createObject();
7003 if (SUCCEEDED(hrc))
7004 {
7005 com::Utf8Str strDefaultPath;
7006 if (aTargetPath.isEmpty())
7007 i_calculateFullPath(".", strDefaultPath);
7008
7009 /* Initialize our worker task */
7010 MachineMoveVM *pTask = NULL;
7011 try
7012 {
7013 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7014 }
7015 catch (std::bad_alloc &)
7016 {
7017 return E_OUTOFMEMORY;
7018 }
7019
7020 hrc = pTask->init();//no exceptions are thrown
7021
7022 if (SUCCEEDED(hrc))
7023 {
7024 hrc = pTask->createThread();
7025 pTask = NULL; /* Consumed by createThread(). */
7026 if (SUCCEEDED(hrc))
7027 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7028 else
7029 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7030 }
7031 else
7032 delete pTask;
7033 }
7034
7035 LogFlowThisFuncLeave();
7036 return hrc;
7037
7038}
7039
7040HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7041{
7042 NOREF(aProgress);
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044
7045 // This check should always fail.
7046 HRESULT rc = i_checkStateDependency(MutableStateDep);
7047 if (FAILED(rc)) return rc;
7048
7049 AssertFailedReturn(E_NOTIMPL);
7050}
7051
7052HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7053{
7054 NOREF(aSavedStateFile);
7055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7056
7057 // This check should always fail.
7058 HRESULT rc = i_checkStateDependency(MutableStateDep);
7059 if (FAILED(rc)) return rc;
7060
7061 AssertFailedReturn(E_NOTIMPL);
7062}
7063
7064HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7065{
7066 NOREF(aFRemoveFile);
7067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7068
7069 // This check should always fail.
7070 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7071 if (FAILED(rc)) return rc;
7072
7073 AssertFailedReturn(E_NOTIMPL);
7074}
7075
7076// public methods for internal purposes
7077/////////////////////////////////////////////////////////////////////////////
7078
7079/**
7080 * Adds the given IsModified_* flag to the dirty flags of the machine.
7081 * This must be called either during i_loadSettings or under the machine write lock.
7082 * @param fl Flag
7083 * @param fAllowStateModification If state modifications are allowed.
7084 */
7085void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7086{
7087 mData->flModifications |= fl;
7088 if (fAllowStateModification && i_isStateModificationAllowed())
7089 mData->mCurrentStateModified = true;
7090}
7091
7092/**
7093 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7094 * care of the write locking.
7095 *
7096 * @param fModification The flag to add.
7097 * @param fAllowStateModification If state modifications are allowed.
7098 */
7099void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7100{
7101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7102 i_setModified(fModification, fAllowStateModification);
7103}
7104
7105/**
7106 * Saves the registry entry of this machine to the given configuration node.
7107 *
7108 * @param data Machine registry data.
7109 *
7110 * @note locks this object for reading.
7111 */
7112HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7113{
7114 AutoLimitedCaller autoCaller(this);
7115 AssertComRCReturnRC(autoCaller.rc());
7116
7117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7118
7119 data.uuid = mData->mUuid;
7120 data.strSettingsFile = mData->m_strConfigFile;
7121
7122 return S_OK;
7123}
7124
7125/**
7126 * Calculates the absolute path of the given path taking the directory of the
7127 * machine settings file as the current directory.
7128 *
7129 * @param strPath Path to calculate the absolute path for.
7130 * @param aResult Where to put the result (used only on success, can be the
7131 * same Utf8Str instance as passed in @a aPath).
7132 * @return IPRT result.
7133 *
7134 * @note Locks this object for reading.
7135 */
7136int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7137{
7138 AutoCaller autoCaller(this);
7139 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7140
7141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7142
7143 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7144
7145 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7146
7147 strSettingsDir.stripFilename();
7148 char szFolder[RTPATH_MAX];
7149 size_t cbFolder = sizeof(szFolder);
7150 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7151 if (RT_SUCCESS(vrc))
7152 aResult = szFolder;
7153
7154 return vrc;
7155}
7156
7157/**
7158 * Copies strSource to strTarget, making it relative to the machine folder
7159 * if it is a subdirectory thereof, or simply copying it otherwise.
7160 *
7161 * @param strSource Path to evaluate and copy.
7162 * @param strTarget Buffer to receive target path.
7163 *
7164 * @note Locks this object for reading.
7165 */
7166void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7167 Utf8Str &strTarget)
7168{
7169 AutoCaller autoCaller(this);
7170 AssertComRCReturn(autoCaller.rc(), (void)0);
7171
7172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7173
7174 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7175 // use strTarget as a temporary buffer to hold the machine settings dir
7176 strTarget = mData->m_strConfigFileFull;
7177 strTarget.stripFilename();
7178 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7179 {
7180 // is relative: then append what's left
7181 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7182 // for empty paths (only possible for subdirs) use "." to avoid
7183 // triggering default settings for not present config attributes.
7184 if (strTarget.isEmpty())
7185 strTarget = ".";
7186 }
7187 else
7188 // is not relative: then overwrite
7189 strTarget = strSource;
7190}
7191
7192/**
7193 * Returns the full path to the machine's log folder in the
7194 * \a aLogFolder argument.
7195 */
7196void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7197{
7198 AutoCaller autoCaller(this);
7199 AssertComRCReturnVoid(autoCaller.rc());
7200
7201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 char szTmp[RTPATH_MAX];
7204 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7205 if (RT_SUCCESS(vrc))
7206 {
7207 if (szTmp[0] && !mUserData.isNull())
7208 {
7209 char szTmp2[RTPATH_MAX];
7210 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7211 if (RT_SUCCESS(vrc))
7212 aLogFolder.printf("%s%c%s",
7213 szTmp2,
7214 RTPATH_DELIMITER,
7215 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7216 }
7217 else
7218 vrc = VERR_PATH_IS_RELATIVE;
7219 }
7220
7221 if (RT_FAILURE(vrc))
7222 {
7223 // fallback if VBOX_USER_LOGHOME is not set or invalid
7224 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7225 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7226 aLogFolder.append(RTPATH_DELIMITER);
7227 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7228 }
7229}
7230
7231/**
7232 * Returns the full path to the machine's log file for an given index.
7233 */
7234Utf8Str Machine::i_getLogFilename(ULONG idx)
7235{
7236 Utf8Str logFolder;
7237 getLogFolder(logFolder);
7238 Assert(logFolder.length());
7239
7240 Utf8Str log;
7241 if (idx == 0)
7242 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7243#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7244 else if (idx == 1)
7245 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7246 else
7247 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7248#else
7249 else
7250 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7251#endif
7252 return log;
7253}
7254
7255/**
7256 * Returns the full path to the machine's hardened log file.
7257 */
7258Utf8Str Machine::i_getHardeningLogFilename(void)
7259{
7260 Utf8Str strFilename;
7261 getLogFolder(strFilename);
7262 Assert(strFilename.length());
7263 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7264 return strFilename;
7265}
7266
7267/**
7268 * Returns the default NVRAM filename based on the location of the VM config.
7269 * Note that this is a relative path.
7270 */
7271Utf8Str Machine::i_getDefaultNVRAMFilename()
7272{
7273 AutoCaller autoCaller(this);
7274 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7275
7276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7277
7278 if (i_isSnapshotMachine())
7279 return Utf8Str::Empty;
7280
7281 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7282 strNVRAMFilePath.stripPath();
7283 strNVRAMFilePath.stripSuffix();
7284 strNVRAMFilePath += ".nvram";
7285
7286 return strNVRAMFilePath;
7287}
7288
7289/**
7290 * Returns the NVRAM filename for a new snapshot. This intentionally works
7291 * similarly to the saved state file naming. Note that this is usually
7292 * a relative path, unless the snapshot folder is absolute.
7293 */
7294Utf8Str Machine::i_getSnapshotNVRAMFilename()
7295{
7296 AutoCaller autoCaller(this);
7297 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7298
7299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7300
7301 RTTIMESPEC ts;
7302 RTTimeNow(&ts);
7303 RTTIME time;
7304 RTTimeExplode(&time, &ts);
7305
7306 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7307 strNVRAMFilePath += RTPATH_DELIMITER;
7308 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7309 time.i32Year, time.u8Month, time.u8MonthDay,
7310 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7311
7312 return strNVRAMFilePath;
7313}
7314
7315/**
7316 * Returns the version of the settings file.
7317 */
7318SettingsVersion_T Machine::i_getSettingsVersion(void)
7319{
7320 AutoCaller autoCaller(this);
7321 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7322
7323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7324
7325 return mData->pMachineConfigFile->getSettingsVersion();
7326}
7327
7328/**
7329 * Composes a unique saved state filename based on the current system time. The filename is
7330 * granular to the second so this will work so long as no more than one snapshot is taken on
7331 * a machine per second.
7332 *
7333 * Before version 4.1, we used this formula for saved state files:
7334 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7335 * which no longer works because saved state files can now be shared between the saved state of the
7336 * "saved" machine and an online snapshot, and the following would cause problems:
7337 * 1) save machine
7338 * 2) create online snapshot from that machine state --> reusing saved state file
7339 * 3) save machine again --> filename would be reused, breaking the online snapshot
7340 *
7341 * So instead we now use a timestamp.
7342 *
7343 * @param strStateFilePath
7344 */
7345
7346void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7347{
7348 AutoCaller autoCaller(this);
7349 AssertComRCReturnVoid(autoCaller.rc());
7350
7351 {
7352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7353 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7354 }
7355
7356 RTTIMESPEC ts;
7357 RTTimeNow(&ts);
7358 RTTIME time;
7359 RTTimeExplode(&time, &ts);
7360
7361 strStateFilePath += RTPATH_DELIMITER;
7362 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7363 time.i32Year, time.u8Month, time.u8MonthDay,
7364 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7365}
7366
7367/**
7368 * Returns whether at least one USB controller is present for the VM.
7369 */
7370bool Machine::i_isUSBControllerPresent()
7371{
7372 AutoCaller autoCaller(this);
7373 AssertComRCReturn(autoCaller.rc(), false);
7374
7375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7376
7377 return (mUSBControllers->size() > 0);
7378}
7379
7380
7381/**
7382 * @note Locks this object for writing, calls the client process
7383 * (inside the lock).
7384 */
7385HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7386 const Utf8Str &strFrontend,
7387 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7388 ProgressProxy *aProgress)
7389{
7390 LogFlowThisFuncEnter();
7391
7392 AssertReturn(aControl, E_FAIL);
7393 AssertReturn(aProgress, E_FAIL);
7394 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7395
7396 AutoCaller autoCaller(this);
7397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7398
7399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7400
7401 if (!mData->mRegistered)
7402 return setError(E_UNEXPECTED,
7403 tr("The machine '%s' is not registered"),
7404 mUserData->s.strName.c_str());
7405
7406 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7407
7408 /* The process started when launching a VM with separate UI/VM processes is always
7409 * the UI process, i.e. needs special handling as it won't claim the session. */
7410 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7411
7412 if (fSeparate)
7413 {
7414 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7415 return setError(VBOX_E_INVALID_OBJECT_STATE,
7416 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7417 mUserData->s.strName.c_str());
7418 }
7419 else
7420 {
7421 if ( mData->mSession.mState == SessionState_Locked
7422 || mData->mSession.mState == SessionState_Spawning
7423 || mData->mSession.mState == SessionState_Unlocking)
7424 return setError(VBOX_E_INVALID_OBJECT_STATE,
7425 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7426 mUserData->s.strName.c_str());
7427
7428 /* may not be busy */
7429 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7430 }
7431
7432 /* Hardening logging */
7433#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7434 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7435 {
7436 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7437 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7438 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7439 {
7440 Utf8Str strStartupLogDir = strHardeningLogFile;
7441 strStartupLogDir.stripFilename();
7442 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7443 file without stripping the file. */
7444 }
7445 strSupHardeningLogArg.append(strHardeningLogFile);
7446
7447 /* Remove legacy log filename to avoid confusion. */
7448 Utf8Str strOldStartupLogFile;
7449 getLogFolder(strOldStartupLogFile);
7450 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7451 RTFileDelete(strOldStartupLogFile.c_str());
7452 }
7453#else
7454 Utf8Str strSupHardeningLogArg;
7455#endif
7456
7457 Utf8Str strAppOverride;
7458#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7459 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7460#endif
7461
7462 bool fUseVBoxSDS = false;
7463 Utf8Str strCanonicalName;
7464 if (false)
7465 { }
7466#ifdef VBOX_WITH_QTGUI
7467 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7468 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7469 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7470 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7471 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7472 {
7473 strCanonicalName = "GUI/Qt";
7474 fUseVBoxSDS = true;
7475 }
7476#endif
7477#ifdef VBOX_WITH_VBOXSDL
7478 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7479 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7480 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7481 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7482 {
7483 strCanonicalName = "GUI/SDL";
7484 fUseVBoxSDS = true;
7485 }
7486#endif
7487#ifdef VBOX_WITH_HEADLESS
7488 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7489 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7490 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7491 {
7492 strCanonicalName = "headless";
7493 }
7494#endif
7495 else
7496 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7497
7498 Utf8Str idStr = mData->mUuid.toString();
7499 Utf8Str const &strMachineName = mUserData->s.strName;
7500 RTPROCESS pid = NIL_RTPROCESS;
7501
7502#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7503 RT_NOREF(fUseVBoxSDS);
7504#else
7505 DWORD idCallerSession = ~(DWORD)0;
7506 if (fUseVBoxSDS)
7507 {
7508 /*
7509 * The VBoxSDS should be used for process launching the VM with
7510 * GUI only if the caller and the VBoxSDS are in different Windows
7511 * sessions and the caller in the interactive one.
7512 */
7513 fUseVBoxSDS = false;
7514
7515 /* Get windows session of the current process. The process token used
7516 due to several reasons:
7517 1. The token is absent for the current thread except someone set it
7518 for us.
7519 2. Needs to get the id of the session where the process is started.
7520 We only need to do this once, though. */
7521 static DWORD s_idCurrentSession = ~(DWORD)0;
7522 DWORD idCurrentSession = s_idCurrentSession;
7523 if (idCurrentSession == ~(DWORD)0)
7524 {
7525 HANDLE hCurrentProcessToken = NULL;
7526 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7527 {
7528 DWORD cbIgn = 0;
7529 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7530 s_idCurrentSession = idCurrentSession;
7531 else
7532 {
7533 idCurrentSession = ~(DWORD)0;
7534 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7535 }
7536 CloseHandle(hCurrentProcessToken);
7537 }
7538 else
7539 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7540 }
7541
7542 /* get the caller's session */
7543 HRESULT hrc = CoImpersonateClient();
7544 if (SUCCEEDED(hrc))
7545 {
7546 HANDLE hCallerThreadToken;
7547 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7548 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7549 &hCallerThreadToken))
7550 {
7551 SetLastError(NO_ERROR);
7552 DWORD cbIgn = 0;
7553 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7554 {
7555 /* Only need to use SDS if the session ID differs: */
7556 if (idCurrentSession != idCallerSession)
7557 {
7558 fUseVBoxSDS = false;
7559
7560 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7561 DWORD cbTokenGroups = 0;
7562 PTOKEN_GROUPS pTokenGroups = NULL;
7563 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7564 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7565 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7566 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7567 {
7568 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7569 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7570 PSID pInteractiveSid = NULL;
7571 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7572 {
7573 /* Iterate over the groups looking for the interactive SID: */
7574 fUseVBoxSDS = false;
7575 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7576 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7577 {
7578 fUseVBoxSDS = true;
7579 break;
7580 }
7581 FreeSid(pInteractiveSid);
7582 }
7583 }
7584 else
7585 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7586 RTMemTmpFree(pTokenGroups);
7587 }
7588 }
7589 else
7590 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7591 CloseHandle(hCallerThreadToken);
7592 }
7593 else
7594 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7595 CoRevertToSelf();
7596 }
7597 else
7598 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7599 }
7600 if (fUseVBoxSDS)
7601 {
7602 /* connect to VBoxSDS */
7603 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7604 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7605 if (FAILED(rc))
7606 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7607 strMachineName.c_str());
7608
7609 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7610 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7611 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7612 service to access the files. */
7613 rc = CoSetProxyBlanket(pVBoxSDS,
7614 RPC_C_AUTHN_DEFAULT,
7615 RPC_C_AUTHZ_DEFAULT,
7616 COLE_DEFAULT_PRINCIPAL,
7617 RPC_C_AUTHN_LEVEL_DEFAULT,
7618 RPC_C_IMP_LEVEL_IMPERSONATE,
7619 NULL,
7620 EOAC_DEFAULT);
7621 if (FAILED(rc))
7622 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7623
7624 size_t const cEnvVars = aEnvironmentChanges.size();
7625 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7626 for (size_t i = 0; i < cEnvVars; i++)
7627 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7628
7629 ULONG uPid = 0;
7630 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7631 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7632 idCallerSession, &uPid);
7633 if (FAILED(rc))
7634 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7635 pid = (RTPROCESS)uPid;
7636 }
7637 else
7638#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7639 {
7640 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7641 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7642 if (RT_FAILURE(vrc))
7643 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7644 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7645 }
7646
7647 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7648 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7649
7650 if (!fSeparate)
7651 {
7652 /*
7653 * Note that we don't release the lock here before calling the client,
7654 * because it doesn't need to call us back if called with a NULL argument.
7655 * Releasing the lock here is dangerous because we didn't prepare the
7656 * launch data yet, but the client we've just started may happen to be
7657 * too fast and call LockMachine() that will fail (because of PID, etc.),
7658 * so that the Machine will never get out of the Spawning session state.
7659 */
7660
7661 /* inform the session that it will be a remote one */
7662 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7663#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7664 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7665#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7666 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7667#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7668 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7669
7670 if (FAILED(rc))
7671 {
7672 /* restore the session state */
7673 mData->mSession.mState = SessionState_Unlocked;
7674 alock.release();
7675 mParent->i_addProcessToReap(pid);
7676 /* The failure may occur w/o any error info (from RPC), so provide one */
7677 return setError(VBOX_E_VM_ERROR,
7678 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7679 }
7680
7681 /* attach launch data to the machine */
7682 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7683 mData->mSession.mRemoteControls.push_back(aControl);
7684 mData->mSession.mProgress = aProgress;
7685 mData->mSession.mPID = pid;
7686 mData->mSession.mState = SessionState_Spawning;
7687 Assert(strCanonicalName.isNotEmpty());
7688 mData->mSession.mName = strCanonicalName;
7689 }
7690 else
7691 {
7692 /* For separate UI process we declare the launch as completed instantly, as the
7693 * actual headless VM start may or may not come. No point in remembering anything
7694 * yet, as what matters for us is when the headless VM gets started. */
7695 aProgress->i_notifyComplete(S_OK);
7696 }
7697
7698 alock.release();
7699 mParent->i_addProcessToReap(pid);
7700
7701 LogFlowThisFuncLeave();
7702 return S_OK;
7703}
7704
7705/**
7706 * Returns @c true if the given session machine instance has an open direct
7707 * session (and optionally also for direct sessions which are closing) and
7708 * returns the session control machine instance if so.
7709 *
7710 * Note that when the method returns @c false, the arguments remain unchanged.
7711 *
7712 * @param aMachine Session machine object.
7713 * @param aControl Direct session control object (optional).
7714 * @param aRequireVM If true then only allow VM sessions.
7715 * @param aAllowClosing If true then additionally a session which is currently
7716 * being closed will also be allowed.
7717 *
7718 * @note locks this object for reading.
7719 */
7720bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7721 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7722 bool aRequireVM /*= false*/,
7723 bool aAllowClosing /*= false*/)
7724{
7725 AutoLimitedCaller autoCaller(this);
7726 AssertComRCReturn(autoCaller.rc(), false);
7727
7728 /* just return false for inaccessible machines */
7729 if (getObjectState().getState() != ObjectState::Ready)
7730 return false;
7731
7732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7733
7734 if ( ( mData->mSession.mState == SessionState_Locked
7735 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7736 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7737 )
7738 {
7739 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7740
7741 aMachine = mData->mSession.mMachine;
7742
7743 if (aControl != NULL)
7744 *aControl = mData->mSession.mDirectControl;
7745
7746 return true;
7747 }
7748
7749 return false;
7750}
7751
7752/**
7753 * Returns @c true if the given machine has an spawning direct session.
7754 *
7755 * @note locks this object for reading.
7756 */
7757bool Machine::i_isSessionSpawning()
7758{
7759 AutoLimitedCaller autoCaller(this);
7760 AssertComRCReturn(autoCaller.rc(), false);
7761
7762 /* just return false for inaccessible machines */
7763 if (getObjectState().getState() != ObjectState::Ready)
7764 return false;
7765
7766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7767
7768 if (mData->mSession.mState == SessionState_Spawning)
7769 return true;
7770
7771 return false;
7772}
7773
7774/**
7775 * Called from the client watcher thread to check for unexpected client process
7776 * death during Session_Spawning state (e.g. before it successfully opened a
7777 * direct session).
7778 *
7779 * On Win32 and on OS/2, this method is called only when we've got the
7780 * direct client's process termination notification, so it always returns @c
7781 * true.
7782 *
7783 * On other platforms, this method returns @c true if the client process is
7784 * terminated and @c false if it's still alive.
7785 *
7786 * @note Locks this object for writing.
7787 */
7788bool Machine::i_checkForSpawnFailure()
7789{
7790 AutoCaller autoCaller(this);
7791 if (!autoCaller.isOk())
7792 {
7793 /* nothing to do */
7794 LogFlowThisFunc(("Already uninitialized!\n"));
7795 return true;
7796 }
7797
7798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7799
7800 if (mData->mSession.mState != SessionState_Spawning)
7801 {
7802 /* nothing to do */
7803 LogFlowThisFunc(("Not spawning any more!\n"));
7804 return true;
7805 }
7806
7807 HRESULT rc = S_OK;
7808
7809 /* PID not yet initialized, skip check. */
7810 if (mData->mSession.mPID == NIL_RTPROCESS)
7811 return false;
7812
7813 RTPROCSTATUS status;
7814 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7815
7816 if (vrc != VERR_PROCESS_RUNNING)
7817 {
7818 Utf8Str strExtraInfo;
7819
7820#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7821 /* If the startup logfile exists and is of non-zero length, tell the
7822 user to look there for more details to encourage them to attach it
7823 when reporting startup issues. */
7824 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7825 uint64_t cbStartupLogFile = 0;
7826 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7827 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7828 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7829#endif
7830
7831 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7832 rc = setError(E_FAIL,
7833 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7834 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7835 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7836 rc = setError(E_FAIL,
7837 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7838 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7839 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7840 rc = setError(E_FAIL,
7841 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7842 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7843 else
7844 rc = setErrorBoth(E_FAIL, vrc,
7845 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7846 i_getName().c_str(), vrc, strExtraInfo.c_str());
7847 }
7848
7849 if (FAILED(rc))
7850 {
7851 /* Close the remote session, remove the remote control from the list
7852 * and reset session state to Closed (@note keep the code in sync with
7853 * the relevant part in LockMachine()). */
7854
7855 Assert(mData->mSession.mRemoteControls.size() == 1);
7856 if (mData->mSession.mRemoteControls.size() == 1)
7857 {
7858 ErrorInfoKeeper eik;
7859 mData->mSession.mRemoteControls.front()->Uninitialize();
7860 }
7861
7862 mData->mSession.mRemoteControls.clear();
7863 mData->mSession.mState = SessionState_Unlocked;
7864
7865 /* finalize the progress after setting the state */
7866 if (!mData->mSession.mProgress.isNull())
7867 {
7868 mData->mSession.mProgress->notifyComplete(rc);
7869 mData->mSession.mProgress.setNull();
7870 }
7871
7872 mData->mSession.mPID = NIL_RTPROCESS;
7873
7874 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7875 return true;
7876 }
7877
7878 return false;
7879}
7880
7881/**
7882 * Checks whether the machine can be registered. If so, commits and saves
7883 * all settings.
7884 *
7885 * @note Must be called from mParent's write lock. Locks this object and
7886 * children for writing.
7887 */
7888HRESULT Machine::i_prepareRegister()
7889{
7890 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7891
7892 AutoLimitedCaller autoCaller(this);
7893 AssertComRCReturnRC(autoCaller.rc());
7894
7895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7896
7897 /* wait for state dependents to drop to zero */
7898 i_ensureNoStateDependencies(alock);
7899
7900 if (!mData->mAccessible)
7901 return setError(VBOX_E_INVALID_OBJECT_STATE,
7902 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7903 mUserData->s.strName.c_str(),
7904 mData->mUuid.toString().c_str());
7905
7906 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7907
7908 if (mData->mRegistered)
7909 return setError(VBOX_E_INVALID_OBJECT_STATE,
7910 tr("The machine '%s' with UUID {%s} is already registered"),
7911 mUserData->s.strName.c_str(),
7912 mData->mUuid.toString().c_str());
7913
7914 HRESULT rc = S_OK;
7915
7916 // Ensure the settings are saved. If we are going to be registered and
7917 // no config file exists yet, create it by calling i_saveSettings() too.
7918 if ( (mData->flModifications)
7919 || (!mData->pMachineConfigFile->fileExists())
7920 )
7921 {
7922 rc = i_saveSettings(NULL, alock);
7923 // no need to check whether VirtualBox.xml needs saving too since
7924 // we can't have a machine XML file rename pending
7925 if (FAILED(rc)) return rc;
7926 }
7927
7928 /* more config checking goes here */
7929
7930 if (SUCCEEDED(rc))
7931 {
7932 /* we may have had implicit modifications we want to fix on success */
7933 i_commit();
7934
7935 mData->mRegistered = true;
7936 }
7937 else
7938 {
7939 /* we may have had implicit modifications we want to cancel on failure*/
7940 i_rollback(false /* aNotify */);
7941 }
7942
7943 return rc;
7944}
7945
7946/**
7947 * Increases the number of objects dependent on the machine state or on the
7948 * registered state. Guarantees that these two states will not change at least
7949 * until #i_releaseStateDependency() is called.
7950 *
7951 * Depending on the @a aDepType value, additional state checks may be made.
7952 * These checks will set extended error info on failure. See
7953 * #i_checkStateDependency() for more info.
7954 *
7955 * If this method returns a failure, the dependency is not added and the caller
7956 * is not allowed to rely on any particular machine state or registration state
7957 * value and may return the failed result code to the upper level.
7958 *
7959 * @param aDepType Dependency type to add.
7960 * @param aState Current machine state (NULL if not interested).
7961 * @param aRegistered Current registered state (NULL if not interested).
7962 *
7963 * @note Locks this object for writing.
7964 */
7965HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7966 MachineState_T *aState /* = NULL */,
7967 BOOL *aRegistered /* = NULL */)
7968{
7969 AutoCaller autoCaller(this);
7970 AssertComRCReturnRC(autoCaller.rc());
7971
7972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7973
7974 HRESULT rc = i_checkStateDependency(aDepType);
7975 if (FAILED(rc)) return rc;
7976
7977 {
7978 if (mData->mMachineStateChangePending != 0)
7979 {
7980 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7981 * drop to zero so don't add more. It may make sense to wait a bit
7982 * and retry before reporting an error (since the pending state
7983 * transition should be really quick) but let's just assert for
7984 * now to see if it ever happens on practice. */
7985
7986 AssertFailed();
7987
7988 return setError(E_ACCESSDENIED,
7989 tr("Machine state change is in progress. Please retry the operation later."));
7990 }
7991
7992 ++mData->mMachineStateDeps;
7993 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7994 }
7995
7996 if (aState)
7997 *aState = mData->mMachineState;
7998 if (aRegistered)
7999 *aRegistered = mData->mRegistered;
8000
8001 return S_OK;
8002}
8003
8004/**
8005 * Decreases the number of objects dependent on the machine state.
8006 * Must always complete the #i_addStateDependency() call after the state
8007 * dependency is no more necessary.
8008 */
8009void Machine::i_releaseStateDependency()
8010{
8011 AutoCaller autoCaller(this);
8012 AssertComRCReturnVoid(autoCaller.rc());
8013
8014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8015
8016 /* releaseStateDependency() w/o addStateDependency()? */
8017 AssertReturnVoid(mData->mMachineStateDeps != 0);
8018 -- mData->mMachineStateDeps;
8019
8020 if (mData->mMachineStateDeps == 0)
8021 {
8022 /* inform i_ensureNoStateDependencies() that there are no more deps */
8023 if (mData->mMachineStateChangePending != 0)
8024 {
8025 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8026 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8027 }
8028 }
8029}
8030
8031Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8032{
8033 /* start with nothing found */
8034 Utf8Str strResult("");
8035
8036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8037
8038 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8039 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8040 // found:
8041 strResult = it->second; // source is a Utf8Str
8042
8043 return strResult;
8044}
8045
8046// protected methods
8047/////////////////////////////////////////////////////////////////////////////
8048
8049/**
8050 * Performs machine state checks based on the @a aDepType value. If a check
8051 * fails, this method will set extended error info, otherwise it will return
8052 * S_OK. It is supposed, that on failure, the caller will immediately return
8053 * the return value of this method to the upper level.
8054 *
8055 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8056 *
8057 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8058 * current state of this machine object allows to change settings of the
8059 * machine (i.e. the machine is not registered, or registered but not running
8060 * and not saved). It is useful to call this method from Machine setters
8061 * before performing any change.
8062 *
8063 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8064 * as for MutableStateDep except that if the machine is saved, S_OK is also
8065 * returned. This is useful in setters which allow changing machine
8066 * properties when it is in the saved state.
8067 *
8068 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8069 * if the current state of this machine object allows to change runtime
8070 * changeable settings of the machine (i.e. the machine is not registered, or
8071 * registered but either running or not running and not saved). It is useful
8072 * to call this method from Machine setters before performing any changes to
8073 * runtime changeable settings.
8074 *
8075 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8076 * the same as for MutableOrRunningStateDep except that if the machine is
8077 * saved, S_OK is also returned. This is useful in setters which allow
8078 * changing runtime and saved state changeable machine properties.
8079 *
8080 * @param aDepType Dependency type to check.
8081 *
8082 * @note Non Machine based classes should use #i_addStateDependency() and
8083 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8084 * template.
8085 *
8086 * @note This method must be called from under this object's read or write
8087 * lock.
8088 */
8089HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8090{
8091 switch (aDepType)
8092 {
8093 case AnyStateDep:
8094 {
8095 break;
8096 }
8097 case MutableStateDep:
8098 {
8099 if ( mData->mRegistered
8100 && ( !i_isSessionMachine()
8101 || ( mData->mMachineState != MachineState_Aborted
8102 && mData->mMachineState != MachineState_Teleported
8103 && mData->mMachineState != MachineState_PoweredOff
8104 )
8105 )
8106 )
8107 return setError(VBOX_E_INVALID_VM_STATE,
8108 tr("The machine is not mutable (state is %s)"),
8109 Global::stringifyMachineState(mData->mMachineState));
8110 break;
8111 }
8112 case MutableOrSavedStateDep:
8113 {
8114 if ( mData->mRegistered
8115 && ( !i_isSessionMachine()
8116 || ( mData->mMachineState != MachineState_Aborted
8117 && mData->mMachineState != MachineState_Teleported
8118 && mData->mMachineState != MachineState_Saved
8119 && mData->mMachineState != MachineState_AbortedSaved
8120 && mData->mMachineState != MachineState_PoweredOff
8121 )
8122 )
8123 )
8124 return setError(VBOX_E_INVALID_VM_STATE,
8125 tr("The machine is not mutable or saved (state is %s)"),
8126 Global::stringifyMachineState(mData->mMachineState));
8127 break;
8128 }
8129 case MutableOrRunningStateDep:
8130 {
8131 if ( mData->mRegistered
8132 && ( !i_isSessionMachine()
8133 || ( mData->mMachineState != MachineState_Aborted
8134 && mData->mMachineState != MachineState_Teleported
8135 && mData->mMachineState != MachineState_PoweredOff
8136 && !Global::IsOnline(mData->mMachineState)
8137 )
8138 )
8139 )
8140 return setError(VBOX_E_INVALID_VM_STATE,
8141 tr("The machine is not mutable or running (state is %s)"),
8142 Global::stringifyMachineState(mData->mMachineState));
8143 break;
8144 }
8145 case MutableOrSavedOrRunningStateDep:
8146 {
8147 if ( mData->mRegistered
8148 && ( !i_isSessionMachine()
8149 || ( mData->mMachineState != MachineState_Aborted
8150 && mData->mMachineState != MachineState_Teleported
8151 && mData->mMachineState != MachineState_Saved
8152 && mData->mMachineState != MachineState_AbortedSaved
8153 && mData->mMachineState != MachineState_PoweredOff
8154 && !Global::IsOnline(mData->mMachineState)
8155 )
8156 )
8157 )
8158 return setError(VBOX_E_INVALID_VM_STATE,
8159 tr("The machine is not mutable, saved or running (state is %s)"),
8160 Global::stringifyMachineState(mData->mMachineState));
8161 break;
8162 }
8163 }
8164
8165 return S_OK;
8166}
8167
8168/**
8169 * Helper to initialize all associated child objects and allocate data
8170 * structures.
8171 *
8172 * This method must be called as a part of the object's initialization procedure
8173 * (usually done in the #init() method).
8174 *
8175 * @note Must be called only from #init() or from #i_registeredInit().
8176 */
8177HRESULT Machine::initDataAndChildObjects()
8178{
8179 AutoCaller autoCaller(this);
8180 AssertComRCReturnRC(autoCaller.rc());
8181 AssertReturn( getObjectState().getState() == ObjectState::InInit
8182 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8183
8184 AssertReturn(!mData->mAccessible, E_FAIL);
8185
8186 /* allocate data structures */
8187 mSSData.allocate();
8188 mUserData.allocate();
8189 mHWData.allocate();
8190 mMediumAttachments.allocate();
8191 mStorageControllers.allocate();
8192 mUSBControllers.allocate();
8193
8194 /* initialize mOSTypeId */
8195 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8196
8197/** @todo r=bird: init() methods never fails, right? Why don't we make them
8198 * return void then! */
8199
8200 /* create associated BIOS settings object */
8201 unconst(mBIOSSettings).createObject();
8202 mBIOSSettings->init(this);
8203
8204 /* create associated trusted platform module object */
8205 unconst(mTrustedPlatformModule).createObject();
8206 mTrustedPlatformModule->init(this);
8207
8208 /* create associated NVRAM store object */
8209 unconst(mNvramStore).createObject();
8210 mNvramStore->init(this);
8211
8212 /* create associated record settings object */
8213 unconst(mRecordingSettings).createObject();
8214 mRecordingSettings->init(this);
8215
8216 /* create the graphics adapter object (always present) */
8217 unconst(mGraphicsAdapter).createObject();
8218 mGraphicsAdapter->init(this);
8219
8220 /* create an associated VRDE object (default is disabled) */
8221 unconst(mVRDEServer).createObject();
8222 mVRDEServer->init(this);
8223
8224 /* create associated serial port objects */
8225 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8226 {
8227 unconst(mSerialPorts[slot]).createObject();
8228 mSerialPorts[slot]->init(this, slot);
8229 }
8230
8231 /* create associated parallel port objects */
8232 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8233 {
8234 unconst(mParallelPorts[slot]).createObject();
8235 mParallelPorts[slot]->init(this, slot);
8236 }
8237
8238 /* create the audio adapter object (always present, default is disabled) */
8239 unconst(mAudioAdapter).createObject();
8240 mAudioAdapter->init(this);
8241
8242 /* create the USB device filters object (always present) */
8243 unconst(mUSBDeviceFilters).createObject();
8244 mUSBDeviceFilters->init(this);
8245
8246 /* create associated network adapter objects */
8247 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8248 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8249 {
8250 unconst(mNetworkAdapters[slot]).createObject();
8251 mNetworkAdapters[slot]->init(this, slot);
8252 }
8253
8254 /* create the bandwidth control */
8255 unconst(mBandwidthControl).createObject();
8256 mBandwidthControl->init(this);
8257
8258 return S_OK;
8259}
8260
8261/**
8262 * Helper to uninitialize all associated child objects and to free all data
8263 * structures.
8264 *
8265 * This method must be called as a part of the object's uninitialization
8266 * procedure (usually done in the #uninit() method).
8267 *
8268 * @note Must be called only from #uninit() or from #i_registeredInit().
8269 */
8270void Machine::uninitDataAndChildObjects()
8271{
8272 AutoCaller autoCaller(this);
8273 AssertComRCReturnVoid(autoCaller.rc());
8274 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8275 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8276 || getObjectState().getState() == ObjectState::InUninit
8277 || getObjectState().getState() == ObjectState::Limited);
8278
8279 /* tell all our other child objects we've been uninitialized */
8280 if (mBandwidthControl)
8281 {
8282 mBandwidthControl->uninit();
8283 unconst(mBandwidthControl).setNull();
8284 }
8285
8286 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8287 {
8288 if (mNetworkAdapters[slot])
8289 {
8290 mNetworkAdapters[slot]->uninit();
8291 unconst(mNetworkAdapters[slot]).setNull();
8292 }
8293 }
8294
8295 if (mUSBDeviceFilters)
8296 {
8297 mUSBDeviceFilters->uninit();
8298 unconst(mUSBDeviceFilters).setNull();
8299 }
8300
8301 if (mAudioAdapter)
8302 {
8303 mAudioAdapter->uninit();
8304 unconst(mAudioAdapter).setNull();
8305 }
8306
8307 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8308 {
8309 if (mParallelPorts[slot])
8310 {
8311 mParallelPorts[slot]->uninit();
8312 unconst(mParallelPorts[slot]).setNull();
8313 }
8314 }
8315
8316 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8317 {
8318 if (mSerialPorts[slot])
8319 {
8320 mSerialPorts[slot]->uninit();
8321 unconst(mSerialPorts[slot]).setNull();
8322 }
8323 }
8324
8325 if (mVRDEServer)
8326 {
8327 mVRDEServer->uninit();
8328 unconst(mVRDEServer).setNull();
8329 }
8330
8331 if (mGraphicsAdapter)
8332 {
8333 mGraphicsAdapter->uninit();
8334 unconst(mGraphicsAdapter).setNull();
8335 }
8336
8337 if (mBIOSSettings)
8338 {
8339 mBIOSSettings->uninit();
8340 unconst(mBIOSSettings).setNull();
8341 }
8342
8343 if (mTrustedPlatformModule)
8344 {
8345 mTrustedPlatformModule->uninit();
8346 unconst(mTrustedPlatformModule).setNull();
8347 }
8348
8349 if (mNvramStore)
8350 {
8351 mNvramStore->uninit();
8352 unconst(mNvramStore).setNull();
8353 }
8354
8355 if (mRecordingSettings)
8356 {
8357 mRecordingSettings->uninit();
8358 unconst(mRecordingSettings).setNull();
8359 }
8360
8361 /* Deassociate media (only when a real Machine or a SnapshotMachine
8362 * instance is uninitialized; SessionMachine instances refer to real
8363 * Machine media). This is necessary for a clean re-initialization of
8364 * the VM after successfully re-checking the accessibility state. Note
8365 * that in case of normal Machine or SnapshotMachine uninitialization (as
8366 * a result of unregistering or deleting the snapshot), outdated media
8367 * attachments will already be uninitialized and deleted, so this
8368 * code will not affect them. */
8369 if ( !mMediumAttachments.isNull()
8370 && !i_isSessionMachine()
8371 )
8372 {
8373 for (MediumAttachmentList::const_iterator
8374 it = mMediumAttachments->begin();
8375 it != mMediumAttachments->end();
8376 ++it)
8377 {
8378 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8379 if (pMedium.isNull())
8380 continue;
8381 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8382 AssertComRC(rc);
8383 }
8384 }
8385
8386 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8387 {
8388 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8389 if (mData->mFirstSnapshot)
8390 {
8391 // snapshots tree is protected by machine write lock; strictly
8392 // this isn't necessary here since we're deleting the entire
8393 // machine, but otherwise we assert in Snapshot::uninit()
8394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8395 mData->mFirstSnapshot->uninit();
8396 mData->mFirstSnapshot.setNull();
8397 }
8398
8399 mData->mCurrentSnapshot.setNull();
8400 }
8401
8402 /* free data structures (the essential mData structure is not freed here
8403 * since it may be still in use) */
8404 mMediumAttachments.free();
8405 mStorageControllers.free();
8406 mUSBControllers.free();
8407 mHWData.free();
8408 mUserData.free();
8409 mSSData.free();
8410}
8411
8412/**
8413 * Returns a pointer to the Machine object for this machine that acts like a
8414 * parent for complex machine data objects such as shared folders, etc.
8415 *
8416 * For primary Machine objects and for SnapshotMachine objects, returns this
8417 * object's pointer itself. For SessionMachine objects, returns the peer
8418 * (primary) machine pointer.
8419 */
8420Machine *Machine::i_getMachine()
8421{
8422 if (i_isSessionMachine())
8423 return (Machine*)mPeer;
8424 return this;
8425}
8426
8427/**
8428 * Makes sure that there are no machine state dependents. If necessary, waits
8429 * for the number of dependents to drop to zero.
8430 *
8431 * Make sure this method is called from under this object's write lock to
8432 * guarantee that no new dependents may be added when this method returns
8433 * control to the caller.
8434 *
8435 * @note Receives a lock to this object for writing. The lock will be released
8436 * while waiting (if necessary).
8437 *
8438 * @warning To be used only in methods that change the machine state!
8439 */
8440void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8441{
8442 AssertReturnVoid(isWriteLockOnCurrentThread());
8443
8444 /* Wait for all state dependents if necessary */
8445 if (mData->mMachineStateDeps != 0)
8446 {
8447 /* lazy semaphore creation */
8448 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8449 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8450
8451 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8452 mData->mMachineStateDeps));
8453
8454 ++mData->mMachineStateChangePending;
8455
8456 /* reset the semaphore before waiting, the last dependent will signal
8457 * it */
8458 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8459
8460 alock.release();
8461
8462 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8463
8464 alock.acquire();
8465
8466 -- mData->mMachineStateChangePending;
8467 }
8468}
8469
8470/**
8471 * Changes the machine state and informs callbacks.
8472 *
8473 * This method is not intended to fail so it either returns S_OK or asserts (and
8474 * returns a failure).
8475 *
8476 * @note Locks this object for writing.
8477 */
8478HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8479{
8480 LogFlowThisFuncEnter();
8481 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8482 Assert(aMachineState != MachineState_Null);
8483
8484 AutoCaller autoCaller(this);
8485 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8486
8487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8488
8489 /* wait for state dependents to drop to zero */
8490 i_ensureNoStateDependencies(alock);
8491
8492 MachineState_T const enmOldState = mData->mMachineState;
8493 if (enmOldState != aMachineState)
8494 {
8495 mData->mMachineState = aMachineState;
8496 RTTimeNow(&mData->mLastStateChange);
8497
8498#ifdef VBOX_WITH_DTRACE_R3_MAIN
8499 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8500#endif
8501 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8502 }
8503
8504 LogFlowThisFuncLeave();
8505 return S_OK;
8506}
8507
8508/**
8509 * Searches for a shared folder with the given logical name
8510 * in the collection of shared folders.
8511 *
8512 * @param aName logical name of the shared folder
8513 * @param aSharedFolder where to return the found object
8514 * @param aSetError whether to set the error info if the folder is
8515 * not found
8516 * @return
8517 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8518 *
8519 * @note
8520 * must be called from under the object's lock!
8521 */
8522HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8523 ComObjPtr<SharedFolder> &aSharedFolder,
8524 bool aSetError /* = false */)
8525{
8526 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8527 for (HWData::SharedFolderList::const_iterator
8528 it = mHWData->mSharedFolders.begin();
8529 it != mHWData->mSharedFolders.end();
8530 ++it)
8531 {
8532 SharedFolder *pSF = *it;
8533 AutoCaller autoCaller(pSF);
8534 if (pSF->i_getName() == aName)
8535 {
8536 aSharedFolder = pSF;
8537 rc = S_OK;
8538 break;
8539 }
8540 }
8541
8542 if (aSetError && FAILED(rc))
8543 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8544
8545 return rc;
8546}
8547
8548/**
8549 * Initializes all machine instance data from the given settings structures
8550 * from XML. The exception is the machine UUID which needs special handling
8551 * depending on the caller's use case, so the caller needs to set that herself.
8552 *
8553 * This gets called in several contexts during machine initialization:
8554 *
8555 * -- When machine XML exists on disk already and needs to be loaded into memory,
8556 * for example, from #i_registeredInit() to load all registered machines on
8557 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8558 * attached to the machine should be part of some media registry already.
8559 *
8560 * -- During OVF import, when a machine config has been constructed from an
8561 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8562 * ensure that the media listed as attachments in the config (which have
8563 * been imported from the OVF) receive the correct registry ID.
8564 *
8565 * -- During VM cloning.
8566 *
8567 * @param config Machine settings from XML.
8568 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8569 * for each attached medium in the config.
8570 * @return
8571 */
8572HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8573 const Guid *puuidRegistry)
8574{
8575 // copy name, description, OS type, teleporter, UTC etc.
8576 mUserData->s = config.machineUserData;
8577
8578 // look up the object by Id to check it is valid
8579 ComObjPtr<GuestOSType> pGuestOSType;
8580 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8581 if (!pGuestOSType.isNull())
8582 mUserData->s.strOsType = pGuestOSType->i_id();
8583
8584 // stateFile (optional)
8585 if (config.strStateFile.isEmpty())
8586 mSSData->strStateFilePath.setNull();
8587 else
8588 {
8589 Utf8Str stateFilePathFull(config.strStateFile);
8590 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8591 if (RT_FAILURE(vrc))
8592 return setErrorBoth(E_FAIL, vrc,
8593 tr("Invalid saved state file path '%s' (%Rrc)"),
8594 config.strStateFile.c_str(),
8595 vrc);
8596 mSSData->strStateFilePath = stateFilePathFull;
8597 }
8598
8599 // snapshot folder needs special processing so set it again
8600 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8601 if (FAILED(rc)) return rc;
8602
8603 /* Copy the extra data items (config may or may not be the same as
8604 * mData->pMachineConfigFile) if necessary. When loading the XML files
8605 * from disk they are the same, but not for OVF import. */
8606 if (mData->pMachineConfigFile != &config)
8607 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8608
8609 /* currentStateModified (optional, default is true) */
8610 mData->mCurrentStateModified = config.fCurrentStateModified;
8611
8612 mData->mLastStateChange = config.timeLastStateChange;
8613
8614 /*
8615 * note: all mUserData members must be assigned prior this point because
8616 * we need to commit changes in order to let mUserData be shared by all
8617 * snapshot machine instances.
8618 */
8619 mUserData.commitCopy();
8620
8621 // machine registry, if present (must be loaded before snapshots)
8622 if (config.canHaveOwnMediaRegistry())
8623 {
8624 // determine machine folder
8625 Utf8Str strMachineFolder = i_getSettingsFileFull();
8626 strMachineFolder.stripFilename();
8627 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8628 config.mediaRegistry,
8629 strMachineFolder);
8630 if (FAILED(rc)) return rc;
8631 }
8632
8633 /* Snapshot node (optional) */
8634 size_t cRootSnapshots;
8635 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8636 {
8637 // there must be only one root snapshot
8638 Assert(cRootSnapshots == 1);
8639
8640 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8641
8642 rc = i_loadSnapshot(snap,
8643 config.uuidCurrentSnapshot,
8644 NULL); // no parent == first snapshot
8645 if (FAILED(rc)) return rc;
8646 }
8647
8648 // hardware data
8649 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8650 if (FAILED(rc)) return rc;
8651
8652 /*
8653 * NOTE: the assignment below must be the last thing to do,
8654 * otherwise it will be not possible to change the settings
8655 * somewhere in the code above because all setters will be
8656 * blocked by i_checkStateDependency(MutableStateDep).
8657 */
8658
8659 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8660 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8661 {
8662 /* no need to use i_setMachineState() during init() */
8663 mData->mMachineState = MachineState_AbortedSaved;
8664 }
8665 else if (config.fAborted)
8666 {
8667 mSSData->strStateFilePath.setNull();
8668
8669 /* no need to use i_setMachineState() during init() */
8670 mData->mMachineState = MachineState_Aborted;
8671 }
8672 else if (!mSSData->strStateFilePath.isEmpty())
8673 {
8674 /* no need to use i_setMachineState() during init() */
8675 mData->mMachineState = MachineState_Saved;
8676 }
8677
8678 // after loading settings, we are no longer different from the XML on disk
8679 mData->flModifications = 0;
8680
8681 return S_OK;
8682}
8683
8684/**
8685 * Recursively loads all snapshots starting from the given.
8686 *
8687 * @param data snapshot settings.
8688 * @param aCurSnapshotId Current snapshot ID from the settings file.
8689 * @param aParentSnapshot Parent snapshot.
8690 */
8691HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8692 const Guid &aCurSnapshotId,
8693 Snapshot *aParentSnapshot)
8694{
8695 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8696 AssertReturn(!i_isSessionMachine(), E_FAIL);
8697
8698 HRESULT rc = S_OK;
8699
8700 Utf8Str strStateFile;
8701 if (!data.strStateFile.isEmpty())
8702 {
8703 /* optional */
8704 strStateFile = data.strStateFile;
8705 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8706 if (RT_FAILURE(vrc))
8707 return setErrorBoth(E_FAIL, vrc,
8708 tr("Invalid saved state file path '%s' (%Rrc)"),
8709 strStateFile.c_str(),
8710 vrc);
8711 }
8712
8713 /* create a snapshot machine object */
8714 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8715 pSnapshotMachine.createObject();
8716 rc = pSnapshotMachine->initFromSettings(this,
8717 data.hardware,
8718 &data.debugging,
8719 &data.autostart,
8720 data.uuid.ref(),
8721 strStateFile);
8722 if (FAILED(rc)) return rc;
8723
8724 /* create a snapshot object */
8725 ComObjPtr<Snapshot> pSnapshot;
8726 pSnapshot.createObject();
8727 /* initialize the snapshot */
8728 rc = pSnapshot->init(mParent, // VirtualBox object
8729 data.uuid,
8730 data.strName,
8731 data.strDescription,
8732 data.timestamp,
8733 pSnapshotMachine,
8734 aParentSnapshot);
8735 if (FAILED(rc)) return rc;
8736
8737 /* memorize the first snapshot if necessary */
8738 if (!mData->mFirstSnapshot)
8739 mData->mFirstSnapshot = pSnapshot;
8740
8741 /* memorize the current snapshot when appropriate */
8742 if ( !mData->mCurrentSnapshot
8743 && pSnapshot->i_getId() == aCurSnapshotId
8744 )
8745 mData->mCurrentSnapshot = pSnapshot;
8746
8747 // now create the children
8748 for (settings::SnapshotsList::const_iterator
8749 it = data.llChildSnapshots.begin();
8750 it != data.llChildSnapshots.end();
8751 ++it)
8752 {
8753 const settings::Snapshot &childData = *it;
8754 // recurse
8755 rc = i_loadSnapshot(childData,
8756 aCurSnapshotId,
8757 pSnapshot); // parent = the one we created above
8758 if (FAILED(rc)) return rc;
8759 }
8760
8761 return rc;
8762}
8763
8764/**
8765 * Loads settings into mHWData.
8766 *
8767 * @param puuidRegistry Registry ID.
8768 * @param puuidSnapshot Snapshot ID
8769 * @param data Reference to the hardware settings.
8770 * @param pDbg Pointer to the debugging settings.
8771 * @param pAutostart Pointer to the autostart settings.
8772 */
8773HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8774 const Guid *puuidSnapshot,
8775 const settings::Hardware &data,
8776 const settings::Debugging *pDbg,
8777 const settings::Autostart *pAutostart)
8778{
8779 AssertReturn(!i_isSessionMachine(), E_FAIL);
8780
8781 HRESULT rc = S_OK;
8782
8783 try
8784 {
8785 ComObjPtr<GuestOSType> pGuestOSType;
8786 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8787
8788 /* The hardware version attribute (optional). */
8789 mHWData->mHWVersion = data.strVersion;
8790 mHWData->mHardwareUUID = data.uuid;
8791
8792 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8793 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8794 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8795 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8796 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8797 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8798 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8799 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8800 mHWData->mPAEEnabled = data.fPAE;
8801 mHWData->mLongMode = data.enmLongMode;
8802 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8803 mHWData->mAPIC = data.fAPIC;
8804 mHWData->mX2APIC = data.fX2APIC;
8805 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8806 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8807 mHWData->mSpecCtrl = data.fSpecCtrl;
8808 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8809 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8810 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8811 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8812 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8813 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8814 mHWData->mCPUCount = data.cCPUs;
8815 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8816 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8817 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8818 mHWData->mCpuProfile = data.strCpuProfile;
8819
8820 // cpu
8821 if (mHWData->mCPUHotPlugEnabled)
8822 {
8823 for (settings::CpuList::const_iterator
8824 it = data.llCpus.begin();
8825 it != data.llCpus.end();
8826 ++it)
8827 {
8828 const settings::Cpu &cpu = *it;
8829
8830 mHWData->mCPUAttached[cpu.ulId] = true;
8831 }
8832 }
8833
8834 // cpuid leafs
8835 for (settings::CpuIdLeafsList::const_iterator
8836 it = data.llCpuIdLeafs.begin();
8837 it != data.llCpuIdLeafs.end();
8838 ++it)
8839 {
8840 const settings::CpuIdLeaf &rLeaf= *it;
8841 if ( rLeaf.idx < UINT32_C(0x20)
8842 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8843 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8844 mHWData->mCpuIdLeafList.push_back(rLeaf);
8845 /* else: just ignore */
8846 }
8847
8848 mHWData->mMemorySize = data.ulMemorySizeMB;
8849 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8850
8851 // boot order
8852 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8853 {
8854 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8855 if (it == data.mapBootOrder.end())
8856 mHWData->mBootOrder[i] = DeviceType_Null;
8857 else
8858 mHWData->mBootOrder[i] = it->second;
8859 }
8860
8861 mHWData->mFirmwareType = data.firmwareType;
8862 mHWData->mPointingHIDType = data.pointingHIDType;
8863 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8864 mHWData->mChipsetType = data.chipsetType;
8865 mHWData->mIommuType = data.iommuType;
8866 mHWData->mParavirtProvider = data.paravirtProvider;
8867 mHWData->mParavirtDebug = data.strParavirtDebug;
8868 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8869 mHWData->mHPETEnabled = data.fHPETEnabled;
8870
8871 /* GraphicsAdapter */
8872 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8873 if (FAILED(rc)) return rc;
8874
8875 /* VRDEServer */
8876 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8877 if (FAILED(rc)) return rc;
8878
8879 /* BIOS */
8880 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8881 if (FAILED(rc)) return rc;
8882
8883 /* Trusted Platform Module */
8884 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8885 if (FAILED(rc)) return rc;
8886
8887 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8888 if (FAILED(rc)) return rc;
8889
8890 /* Recording settings */
8891 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8892 if (FAILED(rc)) return rc;
8893
8894 // Bandwidth control (must come before network adapters)
8895 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8896 if (FAILED(rc)) return rc;
8897
8898 /* USB controllers */
8899 for (settings::USBControllerList::const_iterator
8900 it = data.usbSettings.llUSBControllers.begin();
8901 it != data.usbSettings.llUSBControllers.end();
8902 ++it)
8903 {
8904 const settings::USBController &settingsCtrl = *it;
8905 ComObjPtr<USBController> newCtrl;
8906
8907 newCtrl.createObject();
8908 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8909 mUSBControllers->push_back(newCtrl);
8910 }
8911
8912 /* USB device filters */
8913 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8914 if (FAILED(rc)) return rc;
8915
8916 // network adapters (establish array size first and apply defaults, to
8917 // ensure reading the same settings as we saved, since the list skips
8918 // adapters having defaults)
8919 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8920 size_t oldCount = mNetworkAdapters.size();
8921 if (newCount > oldCount)
8922 {
8923 mNetworkAdapters.resize(newCount);
8924 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8925 {
8926 unconst(mNetworkAdapters[slot]).createObject();
8927 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8928 }
8929 }
8930 else if (newCount < oldCount)
8931 mNetworkAdapters.resize(newCount);
8932 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8933 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8934 for (settings::NetworkAdaptersList::const_iterator
8935 it = data.llNetworkAdapters.begin();
8936 it != data.llNetworkAdapters.end();
8937 ++it)
8938 {
8939 const settings::NetworkAdapter &nic = *it;
8940
8941 /* slot uniqueness is guaranteed by XML Schema */
8942 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8943 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8944 if (FAILED(rc)) return rc;
8945 }
8946
8947 // serial ports (establish defaults first, to ensure reading the same
8948 // settings as we saved, since the list skips ports having defaults)
8949 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8950 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8951 for (settings::SerialPortsList::const_iterator
8952 it = data.llSerialPorts.begin();
8953 it != data.llSerialPorts.end();
8954 ++it)
8955 {
8956 const settings::SerialPort &s = *it;
8957
8958 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8959 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8960 if (FAILED(rc)) return rc;
8961 }
8962
8963 // parallel ports (establish defaults first, to ensure reading the same
8964 // settings as we saved, since the list skips ports having defaults)
8965 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8966 mParallelPorts[i]->i_applyDefaults();
8967 for (settings::ParallelPortsList::const_iterator
8968 it = data.llParallelPorts.begin();
8969 it != data.llParallelPorts.end();
8970 ++it)
8971 {
8972 const settings::ParallelPort &p = *it;
8973
8974 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8975 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8976 if (FAILED(rc)) return rc;
8977 }
8978
8979 /* AudioAdapter */
8980 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8981 if (FAILED(rc)) return rc;
8982
8983 /* storage controllers */
8984 rc = i_loadStorageControllers(data.storage,
8985 puuidRegistry,
8986 puuidSnapshot);
8987 if (FAILED(rc)) return rc;
8988
8989 /* Shared folders */
8990 for (settings::SharedFoldersList::const_iterator
8991 it = data.llSharedFolders.begin();
8992 it != data.llSharedFolders.end();
8993 ++it)
8994 {
8995 const settings::SharedFolder &sf = *it;
8996
8997 ComObjPtr<SharedFolder> sharedFolder;
8998 /* Check for double entries. Not allowed! */
8999 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9000 if (SUCCEEDED(rc))
9001 return setError(VBOX_E_OBJECT_IN_USE,
9002 tr("Shared folder named '%s' already exists"),
9003 sf.strName.c_str());
9004
9005 /* Create the new shared folder. Don't break on error. This will be
9006 * reported when the machine starts. */
9007 sharedFolder.createObject();
9008 rc = sharedFolder->init(i_getMachine(),
9009 sf.strName,
9010 sf.strHostPath,
9011 RT_BOOL(sf.fWritable),
9012 RT_BOOL(sf.fAutoMount),
9013 sf.strAutoMountPoint,
9014 false /* fFailOnError */);
9015 if (FAILED(rc)) return rc;
9016 mHWData->mSharedFolders.push_back(sharedFolder);
9017 }
9018
9019 // Clipboard
9020 mHWData->mClipboardMode = data.clipboardMode;
9021 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9022
9023 // drag'n'drop
9024 mHWData->mDnDMode = data.dndMode;
9025
9026 // guest settings
9027 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9028
9029 // IO settings
9030 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9031 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9032
9033 // Host PCI devices
9034 for (settings::HostPCIDeviceAttachmentList::const_iterator
9035 it = data.pciAttachments.begin();
9036 it != data.pciAttachments.end();
9037 ++it)
9038 {
9039 const settings::HostPCIDeviceAttachment &hpda = *it;
9040 ComObjPtr<PCIDeviceAttachment> pda;
9041
9042 pda.createObject();
9043 pda->i_loadSettings(this, hpda);
9044 mHWData->mPCIDeviceAssignments.push_back(pda);
9045 }
9046
9047 /*
9048 * (The following isn't really real hardware, but it lives in HWData
9049 * for reasons of convenience.)
9050 */
9051
9052#ifdef VBOX_WITH_GUEST_PROPS
9053 /* Guest properties (optional) */
9054
9055 /* Only load transient guest properties for configs which have saved
9056 * state, because there shouldn't be any for powered off VMs. The same
9057 * logic applies for snapshots, as offline snapshots shouldn't have
9058 * any such properties. They confuse the code in various places.
9059 * Note: can't rely on the machine state, as it isn't set yet. */
9060 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9061 /* apologies for the hacky unconst() usage, but this needs hacking
9062 * actually inconsistent settings into consistency, otherwise there
9063 * will be some corner cases where the inconsistency survives
9064 * surprisingly long without getting fixed, especially for snapshots
9065 * as there are no config changes. */
9066 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9067 for (settings::GuestPropertiesList::iterator
9068 it = llGuestProperties.begin();
9069 it != llGuestProperties.end();
9070 /*nothing*/)
9071 {
9072 const settings::GuestProperty &prop = *it;
9073 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9074 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9075 if ( fSkipTransientGuestProperties
9076 && ( fFlags & GUEST_PROP_F_TRANSIENT
9077 || fFlags & GUEST_PROP_F_TRANSRESET))
9078 {
9079 it = llGuestProperties.erase(it);
9080 continue;
9081 }
9082 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9083 mHWData->mGuestProperties[prop.strName] = property;
9084 ++it;
9085 }
9086#endif /* VBOX_WITH_GUEST_PROPS defined */
9087
9088 rc = i_loadDebugging(pDbg);
9089 if (FAILED(rc))
9090 return rc;
9091
9092 mHWData->mAutostart = *pAutostart;
9093
9094 /* default frontend */
9095 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9096 }
9097 catch (std::bad_alloc &)
9098 {
9099 return E_OUTOFMEMORY;
9100 }
9101
9102 AssertComRC(rc);
9103 return rc;
9104}
9105
9106/**
9107 * Called from i_loadHardware() to load the debugging settings of the
9108 * machine.
9109 *
9110 * @param pDbg Pointer to the settings.
9111 */
9112HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9113{
9114 mHWData->mDebugging = *pDbg;
9115 /* no more processing currently required, this will probably change. */
9116 return S_OK;
9117}
9118
9119/**
9120 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9121 *
9122 * @param data storage settings.
9123 * @param puuidRegistry media registry ID to set media to or NULL;
9124 * see Machine::i_loadMachineDataFromSettings()
9125 * @param puuidSnapshot snapshot ID
9126 * @return
9127 */
9128HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9129 const Guid *puuidRegistry,
9130 const Guid *puuidSnapshot)
9131{
9132 AssertReturn(!i_isSessionMachine(), E_FAIL);
9133
9134 HRESULT rc = S_OK;
9135
9136 for (settings::StorageControllersList::const_iterator
9137 it = data.llStorageControllers.begin();
9138 it != data.llStorageControllers.end();
9139 ++it)
9140 {
9141 const settings::StorageController &ctlData = *it;
9142
9143 ComObjPtr<StorageController> pCtl;
9144 /* Try to find one with the name first. */
9145 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9146 if (SUCCEEDED(rc))
9147 return setError(VBOX_E_OBJECT_IN_USE,
9148 tr("Storage controller named '%s' already exists"),
9149 ctlData.strName.c_str());
9150
9151 pCtl.createObject();
9152 rc = pCtl->init(this,
9153 ctlData.strName,
9154 ctlData.storageBus,
9155 ctlData.ulInstance,
9156 ctlData.fBootable);
9157 if (FAILED(rc)) return rc;
9158
9159 mStorageControllers->push_back(pCtl);
9160
9161 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9162 if (FAILED(rc)) return rc;
9163
9164 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9165 if (FAILED(rc)) return rc;
9166
9167 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9168 if (FAILED(rc)) return rc;
9169
9170 /* Load the attached devices now. */
9171 rc = i_loadStorageDevices(pCtl,
9172 ctlData,
9173 puuidRegistry,
9174 puuidSnapshot);
9175 if (FAILED(rc)) return rc;
9176 }
9177
9178 return S_OK;
9179}
9180
9181/**
9182 * Called from i_loadStorageControllers for a controller's devices.
9183 *
9184 * @param aStorageController
9185 * @param data
9186 * @param puuidRegistry media registry ID to set media to or NULL; see
9187 * Machine::i_loadMachineDataFromSettings()
9188 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9189 * @return
9190 */
9191HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9192 const settings::StorageController &data,
9193 const Guid *puuidRegistry,
9194 const Guid *puuidSnapshot)
9195{
9196 HRESULT rc = S_OK;
9197
9198 /* paranoia: detect duplicate attachments */
9199 for (settings::AttachedDevicesList::const_iterator
9200 it = data.llAttachedDevices.begin();
9201 it != data.llAttachedDevices.end();
9202 ++it)
9203 {
9204 const settings::AttachedDevice &ad = *it;
9205
9206 for (settings::AttachedDevicesList::const_iterator it2 = it;
9207 it2 != data.llAttachedDevices.end();
9208 ++it2)
9209 {
9210 if (it == it2)
9211 continue;
9212
9213 const settings::AttachedDevice &ad2 = *it2;
9214
9215 if ( ad.lPort == ad2.lPort
9216 && ad.lDevice == ad2.lDevice)
9217 {
9218 return setError(E_FAIL,
9219 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9220 aStorageController->i_getName().c_str(),
9221 ad.lPort,
9222 ad.lDevice,
9223 mUserData->s.strName.c_str());
9224 }
9225 }
9226 }
9227
9228 for (settings::AttachedDevicesList::const_iterator
9229 it = data.llAttachedDevices.begin();
9230 it != data.llAttachedDevices.end();
9231 ++it)
9232 {
9233 const settings::AttachedDevice &dev = *it;
9234 ComObjPtr<Medium> medium;
9235
9236 switch (dev.deviceType)
9237 {
9238 case DeviceType_Floppy:
9239 case DeviceType_DVD:
9240 if (dev.strHostDriveSrc.isNotEmpty())
9241 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9242 false /* fRefresh */, medium);
9243 else
9244 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9245 dev.uuid,
9246 false /* fRefresh */,
9247 false /* aSetError */,
9248 medium);
9249 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9250 // This is not an error. The host drive or UUID might have vanished, so just go
9251 // ahead without this removeable medium attachment
9252 rc = S_OK;
9253 break;
9254
9255 case DeviceType_HardDisk:
9256 {
9257 /* find a hard disk by UUID */
9258 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9259 if (FAILED(rc))
9260 {
9261 if (i_isSnapshotMachine())
9262 {
9263 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9264 // so the user knows that the bad disk is in a snapshot somewhere
9265 com::ErrorInfo info;
9266 return setError(E_FAIL,
9267 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9268 puuidSnapshot->raw(),
9269 info.getText().raw());
9270 }
9271 else
9272 return rc;
9273 }
9274
9275 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9276
9277 if (medium->i_getType() == MediumType_Immutable)
9278 {
9279 if (i_isSnapshotMachine())
9280 return setError(E_FAIL,
9281 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9282 "of the virtual machine '%s' ('%s')"),
9283 medium->i_getLocationFull().c_str(),
9284 dev.uuid.raw(),
9285 puuidSnapshot->raw(),
9286 mUserData->s.strName.c_str(),
9287 mData->m_strConfigFileFull.c_str());
9288
9289 return setError(E_FAIL,
9290 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9291 medium->i_getLocationFull().c_str(),
9292 dev.uuid.raw(),
9293 mUserData->s.strName.c_str(),
9294 mData->m_strConfigFileFull.c_str());
9295 }
9296
9297 if (medium->i_getType() == MediumType_MultiAttach)
9298 {
9299 if (i_isSnapshotMachine())
9300 return setError(E_FAIL,
9301 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9302 "of the virtual machine '%s' ('%s')"),
9303 medium->i_getLocationFull().c_str(),
9304 dev.uuid.raw(),
9305 puuidSnapshot->raw(),
9306 mUserData->s.strName.c_str(),
9307 mData->m_strConfigFileFull.c_str());
9308
9309 return setError(E_FAIL,
9310 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9311 medium->i_getLocationFull().c_str(),
9312 dev.uuid.raw(),
9313 mUserData->s.strName.c_str(),
9314 mData->m_strConfigFileFull.c_str());
9315 }
9316
9317 if ( !i_isSnapshotMachine()
9318 && medium->i_getChildren().size() != 0
9319 )
9320 return setError(E_FAIL,
9321 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9322 "because it has %d differencing child hard disks"),
9323 medium->i_getLocationFull().c_str(),
9324 dev.uuid.raw(),
9325 mUserData->s.strName.c_str(),
9326 mData->m_strConfigFileFull.c_str(),
9327 medium->i_getChildren().size());
9328
9329 if (i_findAttachment(*mMediumAttachments.data(),
9330 medium))
9331 return setError(E_FAIL,
9332 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9333 medium->i_getLocationFull().c_str(),
9334 dev.uuid.raw(),
9335 mUserData->s.strName.c_str(),
9336 mData->m_strConfigFileFull.c_str());
9337
9338 break;
9339 }
9340
9341 default:
9342 return setError(E_FAIL,
9343 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9344 medium->i_getLocationFull().c_str(),
9345 mUserData->s.strName.c_str(),
9346 mData->m_strConfigFileFull.c_str());
9347 }
9348
9349 if (FAILED(rc))
9350 break;
9351
9352 /* Bandwidth groups are loaded at this point. */
9353 ComObjPtr<BandwidthGroup> pBwGroup;
9354
9355 if (!dev.strBwGroup.isEmpty())
9356 {
9357 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9358 if (FAILED(rc))
9359 return setError(E_FAIL,
9360 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9361 medium->i_getLocationFull().c_str(),
9362 dev.strBwGroup.c_str(),
9363 mUserData->s.strName.c_str(),
9364 mData->m_strConfigFileFull.c_str());
9365 pBwGroup->i_reference();
9366 }
9367
9368 const Utf8Str controllerName = aStorageController->i_getName();
9369 ComObjPtr<MediumAttachment> pAttachment;
9370 pAttachment.createObject();
9371 rc = pAttachment->init(this,
9372 medium,
9373 controllerName,
9374 dev.lPort,
9375 dev.lDevice,
9376 dev.deviceType,
9377 false,
9378 dev.fPassThrough,
9379 dev.fTempEject,
9380 dev.fNonRotational,
9381 dev.fDiscard,
9382 dev.fHotPluggable,
9383 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9384 if (FAILED(rc)) break;
9385
9386 /* associate the medium with this machine and snapshot */
9387 if (!medium.isNull())
9388 {
9389 AutoCaller medCaller(medium);
9390 if (FAILED(medCaller.rc())) return medCaller.rc();
9391 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9392
9393 if (i_isSnapshotMachine())
9394 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9395 else
9396 rc = medium->i_addBackReference(mData->mUuid);
9397 /* If the medium->addBackReference fails it sets an appropriate
9398 * error message, so no need to do any guesswork here. */
9399
9400 if (puuidRegistry)
9401 // caller wants registry ID to be set on all attached media (OVF import case)
9402 medium->i_addRegistry(*puuidRegistry);
9403 }
9404
9405 if (FAILED(rc))
9406 break;
9407
9408 /* back up mMediumAttachments to let registeredInit() properly rollback
9409 * on failure (= limited accessibility) */
9410 i_setModified(IsModified_Storage);
9411 mMediumAttachments.backup();
9412 mMediumAttachments->push_back(pAttachment);
9413 }
9414
9415 return rc;
9416}
9417
9418/**
9419 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9420 *
9421 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9422 * @param aSnapshot where to return the found snapshot
9423 * @param aSetError true to set extended error info on failure
9424 */
9425HRESULT Machine::i_findSnapshotById(const Guid &aId,
9426 ComObjPtr<Snapshot> &aSnapshot,
9427 bool aSetError /* = false */)
9428{
9429 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9430
9431 if (!mData->mFirstSnapshot)
9432 {
9433 if (aSetError)
9434 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9435 return E_FAIL;
9436 }
9437
9438 if (aId.isZero())
9439 aSnapshot = mData->mFirstSnapshot;
9440 else
9441 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9442
9443 if (!aSnapshot)
9444 {
9445 if (aSetError)
9446 return setError(E_FAIL,
9447 tr("Could not find a snapshot with UUID {%s}"),
9448 aId.toString().c_str());
9449 return E_FAIL;
9450 }
9451
9452 return S_OK;
9453}
9454
9455/**
9456 * Returns the snapshot with the given name or fails of no such snapshot.
9457 *
9458 * @param strName snapshot name to find
9459 * @param aSnapshot where to return the found snapshot
9460 * @param aSetError true to set extended error info on failure
9461 */
9462HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9463 ComObjPtr<Snapshot> &aSnapshot,
9464 bool aSetError /* = false */)
9465{
9466 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9467
9468 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9469
9470 if (!mData->mFirstSnapshot)
9471 {
9472 if (aSetError)
9473 return setError(VBOX_E_OBJECT_NOT_FOUND,
9474 tr("This machine does not have any snapshots"));
9475 return VBOX_E_OBJECT_NOT_FOUND;
9476 }
9477
9478 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9479
9480 if (!aSnapshot)
9481 {
9482 if (aSetError)
9483 return setError(VBOX_E_OBJECT_NOT_FOUND,
9484 tr("Could not find a snapshot named '%s'"), strName.c_str());
9485 return VBOX_E_OBJECT_NOT_FOUND;
9486 }
9487
9488 return S_OK;
9489}
9490
9491/**
9492 * Returns a storage controller object with the given name.
9493 *
9494 * @param aName storage controller name to find
9495 * @param aStorageController where to return the found storage controller
9496 * @param aSetError true to set extended error info on failure
9497 */
9498HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9499 ComObjPtr<StorageController> &aStorageController,
9500 bool aSetError /* = false */)
9501{
9502 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9503
9504 for (StorageControllerList::const_iterator
9505 it = mStorageControllers->begin();
9506 it != mStorageControllers->end();
9507 ++it)
9508 {
9509 if ((*it)->i_getName() == aName)
9510 {
9511 aStorageController = (*it);
9512 return S_OK;
9513 }
9514 }
9515
9516 if (aSetError)
9517 return setError(VBOX_E_OBJECT_NOT_FOUND,
9518 tr("Could not find a storage controller named '%s'"),
9519 aName.c_str());
9520 return VBOX_E_OBJECT_NOT_FOUND;
9521}
9522
9523/**
9524 * Returns a USB controller object with the given name.
9525 *
9526 * @param aName USB controller name to find
9527 * @param aUSBController where to return the found USB controller
9528 * @param aSetError true to set extended error info on failure
9529 */
9530HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9531 ComObjPtr<USBController> &aUSBController,
9532 bool aSetError /* = false */)
9533{
9534 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9535
9536 for (USBControllerList::const_iterator
9537 it = mUSBControllers->begin();
9538 it != mUSBControllers->end();
9539 ++it)
9540 {
9541 if ((*it)->i_getName() == aName)
9542 {
9543 aUSBController = (*it);
9544 return S_OK;
9545 }
9546 }
9547
9548 if (aSetError)
9549 return setError(VBOX_E_OBJECT_NOT_FOUND,
9550 tr("Could not find a storage controller named '%s'"),
9551 aName.c_str());
9552 return VBOX_E_OBJECT_NOT_FOUND;
9553}
9554
9555/**
9556 * Returns the number of USB controller instance of the given type.
9557 *
9558 * @param enmType USB controller type.
9559 */
9560ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9561{
9562 ULONG cCtrls = 0;
9563
9564 for (USBControllerList::const_iterator
9565 it = mUSBControllers->begin();
9566 it != mUSBControllers->end();
9567 ++it)
9568 {
9569 if ((*it)->i_getControllerType() == enmType)
9570 cCtrls++;
9571 }
9572
9573 return cCtrls;
9574}
9575
9576HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9577 MediumAttachmentList &atts)
9578{
9579 AutoCaller autoCaller(this);
9580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9581
9582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9583
9584 for (MediumAttachmentList::const_iterator
9585 it = mMediumAttachments->begin();
9586 it != mMediumAttachments->end();
9587 ++it)
9588 {
9589 const ComObjPtr<MediumAttachment> &pAtt = *it;
9590 // should never happen, but deal with NULL pointers in the list.
9591 AssertContinue(!pAtt.isNull());
9592
9593 // getControllerName() needs caller+read lock
9594 AutoCaller autoAttCaller(pAtt);
9595 if (FAILED(autoAttCaller.rc()))
9596 {
9597 atts.clear();
9598 return autoAttCaller.rc();
9599 }
9600 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9601
9602 if (pAtt->i_getControllerName() == aName)
9603 atts.push_back(pAtt);
9604 }
9605
9606 return S_OK;
9607}
9608
9609
9610/**
9611 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9612 * file if the machine name was changed and about creating a new settings file
9613 * if this is a new machine.
9614 *
9615 * @note Must be never called directly but only from #saveSettings().
9616 */
9617HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9618 bool *pfSettingsFileIsNew)
9619{
9620 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9621
9622 HRESULT rc = S_OK;
9623
9624 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9625 /// @todo need to handle primary group change, too
9626
9627 /* attempt to rename the settings file if machine name is changed */
9628 if ( mUserData->s.fNameSync
9629 && mUserData.isBackedUp()
9630 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9631 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9632 )
9633 {
9634 bool dirRenamed = false;
9635 bool fileRenamed = false;
9636
9637 Utf8Str configFile, newConfigFile;
9638 Utf8Str configFilePrev, newConfigFilePrev;
9639 Utf8Str NVRAMFile, newNVRAMFile;
9640 Utf8Str configDir, newConfigDir;
9641
9642 do
9643 {
9644 int vrc = VINF_SUCCESS;
9645
9646 Utf8Str name = mUserData.backedUpData()->s.strName;
9647 Utf8Str newName = mUserData->s.strName;
9648 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9649 if (group == "/")
9650 group.setNull();
9651 Utf8Str newGroup = mUserData->s.llGroups.front();
9652 if (newGroup == "/")
9653 newGroup.setNull();
9654
9655 configFile = mData->m_strConfigFileFull;
9656
9657 /* first, rename the directory if it matches the group and machine name */
9658 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9659 /** @todo hack, make somehow use of ComposeMachineFilename */
9660 if (mUserData->s.fDirectoryIncludesUUID)
9661 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9662 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9663 /** @todo hack, make somehow use of ComposeMachineFilename */
9664 if (mUserData->s.fDirectoryIncludesUUID)
9665 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9666 configDir = configFile;
9667 configDir.stripFilename();
9668 newConfigDir = configDir;
9669 if ( configDir.length() >= groupPlusName.length()
9670 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9671 groupPlusName.c_str()))
9672 {
9673 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9674 Utf8Str newConfigBaseDir(newConfigDir);
9675 newConfigDir.append(newGroupPlusName);
9676 /* consistency: use \ if appropriate on the platform */
9677 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9678 /* new dir and old dir cannot be equal here because of 'if'
9679 * above and because name != newName */
9680 Assert(configDir != newConfigDir);
9681 if (!fSettingsFileIsNew)
9682 {
9683 /* perform real rename only if the machine is not new */
9684 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9685 if ( vrc == VERR_FILE_NOT_FOUND
9686 || vrc == VERR_PATH_NOT_FOUND)
9687 {
9688 /* create the parent directory, then retry renaming */
9689 Utf8Str parent(newConfigDir);
9690 parent.stripFilename();
9691 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9692 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9693 }
9694 if (RT_FAILURE(vrc))
9695 {
9696 rc = setErrorBoth(E_FAIL, vrc,
9697 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9698 configDir.c_str(),
9699 newConfigDir.c_str(),
9700 vrc);
9701 break;
9702 }
9703 /* delete subdirectories which are no longer needed */
9704 Utf8Str dir(configDir);
9705 dir.stripFilename();
9706 while (dir != newConfigBaseDir && dir != ".")
9707 {
9708 vrc = RTDirRemove(dir.c_str());
9709 if (RT_FAILURE(vrc))
9710 break;
9711 dir.stripFilename();
9712 }
9713 dirRenamed = true;
9714 }
9715 }
9716
9717 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9718
9719 /* then try to rename the settings file itself */
9720 if (newConfigFile != configFile)
9721 {
9722 /* get the path to old settings file in renamed directory */
9723 Assert(mData->m_strConfigFileFull == configFile);
9724 configFile.printf("%s%c%s",
9725 newConfigDir.c_str(),
9726 RTPATH_DELIMITER,
9727 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9728 if (!fSettingsFileIsNew)
9729 {
9730 /* perform real rename only if the machine is not new */
9731 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9732 if (RT_FAILURE(vrc))
9733 {
9734 rc = setErrorBoth(E_FAIL, vrc,
9735 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9736 configFile.c_str(),
9737 newConfigFile.c_str(),
9738 vrc);
9739 break;
9740 }
9741 fileRenamed = true;
9742 configFilePrev = configFile;
9743 configFilePrev += "-prev";
9744 newConfigFilePrev = newConfigFile;
9745 newConfigFilePrev += "-prev";
9746 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9747 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9748 if (NVRAMFile.isNotEmpty())
9749 {
9750 // in the NVRAM file path, replace the old directory with the new directory
9751 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9752 {
9753 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9754 NVRAMFile = newConfigDir + strNVRAMFile;
9755 }
9756 newNVRAMFile = newConfigFile;
9757 newNVRAMFile.stripSuffix();
9758 newNVRAMFile += ".nvram";
9759 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9760 }
9761 }
9762 }
9763
9764 // update m_strConfigFileFull amd mConfigFile
9765 mData->m_strConfigFileFull = newConfigFile;
9766 // compute the relative path too
9767 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9768
9769 // store the old and new so that VirtualBox::i_saveSettings() can update
9770 // the media registry
9771 if ( mData->mRegistered
9772 && (configDir != newConfigDir || configFile != newConfigFile))
9773 {
9774 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9775
9776 if (pfNeedsGlobalSaveSettings)
9777 *pfNeedsGlobalSaveSettings = true;
9778 }
9779
9780 // in the saved state file path, replace the old directory with the new directory
9781 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9782 {
9783 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9784 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9785 }
9786 if (newNVRAMFile.isNotEmpty())
9787 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9788
9789 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9790 if (mData->mFirstSnapshot)
9791 {
9792 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9793 newConfigDir.c_str());
9794 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9795 newConfigDir.c_str());
9796 }
9797 }
9798 while (0);
9799
9800 if (FAILED(rc))
9801 {
9802 /* silently try to rename everything back */
9803 if (fileRenamed)
9804 {
9805 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9806 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9807 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9808 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9809 }
9810 if (dirRenamed)
9811 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9812 }
9813
9814 if (FAILED(rc)) return rc;
9815 }
9816
9817 if (fSettingsFileIsNew)
9818 {
9819 /* create a virgin config file */
9820 int vrc = VINF_SUCCESS;
9821
9822 /* ensure the settings directory exists */
9823 Utf8Str path(mData->m_strConfigFileFull);
9824 path.stripFilename();
9825 if (!RTDirExists(path.c_str()))
9826 {
9827 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9828 if (RT_FAILURE(vrc))
9829 {
9830 return setErrorBoth(E_FAIL, vrc,
9831 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9832 path.c_str(),
9833 vrc);
9834 }
9835 }
9836
9837 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9838 path = mData->m_strConfigFileFull;
9839 RTFILE f = NIL_RTFILE;
9840 vrc = RTFileOpen(&f, path.c_str(),
9841 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9842 if (RT_FAILURE(vrc))
9843 return setErrorBoth(E_FAIL, vrc,
9844 tr("Could not create the settings file '%s' (%Rrc)"),
9845 path.c_str(),
9846 vrc);
9847 RTFileClose(f);
9848 }
9849 if (pfSettingsFileIsNew)
9850 *pfSettingsFileIsNew = fSettingsFileIsNew;
9851
9852 return rc;
9853}
9854
9855/**
9856 * Saves and commits machine data, user data and hardware data.
9857 *
9858 * Note that on failure, the data remains uncommitted.
9859 *
9860 * @a aFlags may combine the following flags:
9861 *
9862 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9863 * Used when saving settings after an operation that makes them 100%
9864 * correspond to the settings from the current snapshot.
9865 * - SaveS_Force: settings will be saved without doing a deep compare of the
9866 * settings structures. This is used when this is called because snapshots
9867 * have changed to avoid the overhead of the deep compare.
9868 *
9869 * @note Must be called from under this object's write lock. Locks children for
9870 * writing.
9871 *
9872 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9873 * initialized to false and that will be set to true by this function if
9874 * the caller must invoke VirtualBox::i_saveSettings() because the global
9875 * settings have changed. This will happen if a machine rename has been
9876 * saved and the global machine and media registries will therefore need
9877 * updating.
9878 * @param alock Reference to the lock for this machine object.
9879 * @param aFlags Flags.
9880 */
9881HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9882 AutoWriteLock &alock,
9883 int aFlags /*= 0*/)
9884{
9885 LogFlowThisFuncEnter();
9886
9887 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9888
9889 /* make sure child objects are unable to modify the settings while we are
9890 * saving them */
9891 i_ensureNoStateDependencies(alock);
9892
9893 AssertReturn(!i_isSnapshotMachine(),
9894 E_FAIL);
9895
9896 if (!mData->mAccessible)
9897 return setError(VBOX_E_INVALID_VM_STATE,
9898 tr("The machine is not accessible, so cannot save settings"));
9899
9900 HRESULT rc = S_OK;
9901 bool fNeedsWrite = false;
9902 bool fSettingsFileIsNew = false;
9903
9904 /* First, prepare to save settings. It will care about renaming the
9905 * settings directory and file if the machine name was changed and about
9906 * creating a new settings file if this is a new machine. */
9907 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
9908 &fSettingsFileIsNew);
9909 if (FAILED(rc)) return rc;
9910
9911 // keep a pointer to the current settings structures
9912 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9913 settings::MachineConfigFile *pNewConfig = NULL;
9914
9915 try
9916 {
9917 // make a fresh one to have everyone write stuff into
9918 pNewConfig = new settings::MachineConfigFile(NULL);
9919 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9920
9921 // now go and copy all the settings data from COM to the settings structures
9922 // (this calls i_saveSettings() on all the COM objects in the machine)
9923 i_copyMachineDataToSettings(*pNewConfig);
9924
9925 if (aFlags & SaveS_ResetCurStateModified)
9926 {
9927 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9928 mData->mCurrentStateModified = FALSE;
9929 fNeedsWrite = true; // always, no need to compare
9930 }
9931 else if (aFlags & SaveS_Force)
9932 {
9933 fNeedsWrite = true; // always, no need to compare
9934 }
9935 else
9936 {
9937 if (!mData->mCurrentStateModified)
9938 {
9939 // do a deep compare of the settings that we just saved with the settings
9940 // previously stored in the config file; this invokes MachineConfigFile::operator==
9941 // which does a deep compare of all the settings, which is expensive but less expensive
9942 // than writing out XML in vain
9943 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9944
9945 // could still be modified if any settings changed
9946 mData->mCurrentStateModified = fAnySettingsChanged;
9947
9948 fNeedsWrite = fAnySettingsChanged;
9949 }
9950 else
9951 fNeedsWrite = true;
9952 }
9953
9954 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9955
9956 if (fNeedsWrite)
9957 // now spit it all out!
9958 pNewConfig->write(mData->m_strConfigFileFull);
9959
9960 mData->pMachineConfigFile = pNewConfig;
9961 delete pOldConfig;
9962 i_commit();
9963
9964 // after saving settings, we are no longer different from the XML on disk
9965 mData->flModifications = 0;
9966 }
9967 catch (HRESULT err)
9968 {
9969 // we assume that error info is set by the thrower
9970 rc = err;
9971
9972 // delete any newly created settings file
9973 if (fSettingsFileIsNew)
9974 RTFileDelete(mData->m_strConfigFileFull.c_str());
9975
9976 // restore old config
9977 delete pNewConfig;
9978 mData->pMachineConfigFile = pOldConfig;
9979 }
9980 catch (...)
9981 {
9982 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9983 }
9984
9985 if (fNeedsWrite)
9986 {
9987 /* Fire the data change event, even on failure (since we've already
9988 * committed all data). This is done only for SessionMachines because
9989 * mutable Machine instances are always not registered (i.e. private
9990 * to the client process that creates them) and thus don't need to
9991 * inform callbacks. */
9992 if (i_isSessionMachine())
9993 mParent->i_onMachineDataChanged(mData->mUuid);
9994 }
9995
9996 LogFlowThisFunc(("rc=%08X\n", rc));
9997 LogFlowThisFuncLeave();
9998 return rc;
9999}
10000
10001/**
10002 * Implementation for saving the machine settings into the given
10003 * settings::MachineConfigFile instance. This copies machine extradata
10004 * from the previous machine config file in the instance data, if any.
10005 *
10006 * This gets called from two locations:
10007 *
10008 * -- Machine::i_saveSettings(), during the regular XML writing;
10009 *
10010 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10011 * exported to OVF and we write the VirtualBox proprietary XML
10012 * into a <vbox:Machine> tag.
10013 *
10014 * This routine fills all the fields in there, including snapshots, *except*
10015 * for the following:
10016 *
10017 * -- fCurrentStateModified. There is some special logic associated with that.
10018 *
10019 * The caller can then call MachineConfigFile::write() or do something else
10020 * with it.
10021 *
10022 * Caller must hold the machine lock!
10023 *
10024 * This throws XML errors and HRESULT, so the caller must have a catch block!
10025 */
10026void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10027{
10028 // deep copy extradata, being extra careful with self assignment (the STL
10029 // map assignment on Mac OS X clang based Xcode isn't checking)
10030 if (&config != mData->pMachineConfigFile)
10031 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10032
10033 config.uuid = mData->mUuid;
10034
10035 // copy name, description, OS type, teleport, UTC etc.
10036 config.machineUserData = mUserData->s;
10037
10038 if ( mData->mMachineState == MachineState_Saved
10039 || mData->mMachineState == MachineState_AbortedSaved
10040 || mData->mMachineState == MachineState_Restoring
10041 // when doing certain snapshot operations we may or may not have
10042 // a saved state in the current state, so keep everything as is
10043 || ( ( mData->mMachineState == MachineState_Snapshotting
10044 || mData->mMachineState == MachineState_DeletingSnapshot
10045 || mData->mMachineState == MachineState_RestoringSnapshot)
10046 && (!mSSData->strStateFilePath.isEmpty())
10047 )
10048 )
10049 {
10050 Assert(!mSSData->strStateFilePath.isEmpty());
10051 /* try to make the file name relative to the settings file dir */
10052 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10053 }
10054 else
10055 {
10056 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10057 config.strStateFile.setNull();
10058 }
10059
10060 if (mData->mCurrentSnapshot)
10061 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10062 else
10063 config.uuidCurrentSnapshot.clear();
10064
10065 config.timeLastStateChange = mData->mLastStateChange;
10066 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10067 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10068
10069 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10070 if (FAILED(rc)) throw rc;
10071
10072 // save machine's media registry if this is VirtualBox 4.0 or later
10073 if (config.canHaveOwnMediaRegistry())
10074 {
10075 // determine machine folder
10076 Utf8Str strMachineFolder = i_getSettingsFileFull();
10077 strMachineFolder.stripFilename();
10078 mParent->i_saveMediaRegistry(config.mediaRegistry,
10079 i_getId(), // only media with registry ID == machine UUID
10080 strMachineFolder);
10081 // this throws HRESULT
10082 }
10083
10084 // save snapshots
10085 rc = i_saveAllSnapshots(config);
10086 if (FAILED(rc)) throw rc;
10087}
10088
10089/**
10090 * Saves all snapshots of the machine into the given machine config file. Called
10091 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10092 * @param config
10093 * @return
10094 */
10095HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10096{
10097 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10098
10099 HRESULT rc = S_OK;
10100
10101 try
10102 {
10103 config.llFirstSnapshot.clear();
10104
10105 if (mData->mFirstSnapshot)
10106 {
10107 // the settings use a list for "the first snapshot"
10108 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10109
10110 // get reference to the snapshot on the list and work on that
10111 // element straight in the list to avoid excessive copying later
10112 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10113 if (FAILED(rc)) throw rc;
10114 }
10115
10116// if (mType == IsSessionMachine)
10117// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10118
10119 }
10120 catch (HRESULT err)
10121 {
10122 /* we assume that error info is set by the thrower */
10123 rc = err;
10124 }
10125 catch (...)
10126 {
10127 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10128 }
10129
10130 return rc;
10131}
10132
10133/**
10134 * Saves the VM hardware configuration. It is assumed that the
10135 * given node is empty.
10136 *
10137 * @param data Reference to the settings object for the hardware config.
10138 * @param pDbg Pointer to the settings object for the debugging config
10139 * which happens to live in mHWData.
10140 * @param pAutostart Pointer to the settings object for the autostart config
10141 * which happens to live in mHWData.
10142 */
10143HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10144 settings::Autostart *pAutostart)
10145{
10146 HRESULT rc = S_OK;
10147
10148 try
10149 {
10150 /* The hardware version attribute (optional).
10151 Automatically upgrade from 1 to current default hardware version
10152 when there is no saved state. (ugly!) */
10153 if ( mHWData->mHWVersion == "1"
10154 && mSSData->strStateFilePath.isEmpty()
10155 )
10156 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10157
10158 data.strVersion = mHWData->mHWVersion;
10159 data.uuid = mHWData->mHardwareUUID;
10160
10161 // CPU
10162 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10163 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10164 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10165 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10166 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10167 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10168 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10169 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10170 data.fPAE = !!mHWData->mPAEEnabled;
10171 data.enmLongMode = mHWData->mLongMode;
10172 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10173 data.fAPIC = !!mHWData->mAPIC;
10174 data.fX2APIC = !!mHWData->mX2APIC;
10175 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10176 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10177 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10178 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10179 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10180 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10181 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10182 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10183 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10184 data.cCPUs = mHWData->mCPUCount;
10185 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10186 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10187 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10188 data.strCpuProfile = mHWData->mCpuProfile;
10189
10190 data.llCpus.clear();
10191 if (data.fCpuHotPlug)
10192 {
10193 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10194 {
10195 if (mHWData->mCPUAttached[idx])
10196 {
10197 settings::Cpu cpu;
10198 cpu.ulId = idx;
10199 data.llCpus.push_back(cpu);
10200 }
10201 }
10202 }
10203
10204 /* Standard and Extended CPUID leafs. */
10205 data.llCpuIdLeafs.clear();
10206 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10207
10208 // memory
10209 data.ulMemorySizeMB = mHWData->mMemorySize;
10210 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10211
10212 // firmware
10213 data.firmwareType = mHWData->mFirmwareType;
10214
10215 // HID
10216 data.pointingHIDType = mHWData->mPointingHIDType;
10217 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10218
10219 // chipset
10220 data.chipsetType = mHWData->mChipsetType;
10221
10222 // iommu
10223 data.iommuType = mHWData->mIommuType;
10224
10225 // paravirt
10226 data.paravirtProvider = mHWData->mParavirtProvider;
10227 data.strParavirtDebug = mHWData->mParavirtDebug;
10228
10229 // emulated USB card reader
10230 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10231
10232 // HPET
10233 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10234
10235 // boot order
10236 data.mapBootOrder.clear();
10237 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10238 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10239
10240 /* VRDEServer settings (optional) */
10241 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10242 if (FAILED(rc)) throw rc;
10243
10244 /* BIOS settings (required) */
10245 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10246 if (FAILED(rc)) throw rc;
10247
10248 /* Trusted Platform Module settings (required) */
10249 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10250 if (FAILED(rc)) throw rc;
10251
10252 /* NVRAM settings (required) */
10253 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10254 if (FAILED(rc)) throw rc;
10255
10256 /* Recording settings (required) */
10257 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10258 if (FAILED(rc)) throw rc;
10259
10260 /* GraphicsAdapter settings (required) */
10261 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10262 if (FAILED(rc)) throw rc;
10263
10264 /* USB Controller (required) */
10265 data.usbSettings.llUSBControllers.clear();
10266 for (USBControllerList::const_iterator
10267 it = mUSBControllers->begin();
10268 it != mUSBControllers->end();
10269 ++it)
10270 {
10271 ComObjPtr<USBController> ctrl = *it;
10272 settings::USBController settingsCtrl;
10273
10274 settingsCtrl.strName = ctrl->i_getName();
10275 settingsCtrl.enmType = ctrl->i_getControllerType();
10276
10277 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10278 }
10279
10280 /* USB device filters (required) */
10281 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10282 if (FAILED(rc)) throw rc;
10283
10284 /* Network adapters (required) */
10285 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10286 data.llNetworkAdapters.clear();
10287 /* Write out only the nominal number of network adapters for this
10288 * chipset type. Since Machine::commit() hasn't been called there
10289 * may be extra NIC settings in the vector. */
10290 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10291 {
10292 settings::NetworkAdapter nic;
10293 nic.ulSlot = (uint32_t)slot;
10294 /* paranoia check... must not be NULL, but must not crash either. */
10295 if (mNetworkAdapters[slot])
10296 {
10297 if (mNetworkAdapters[slot]->i_hasDefaults())
10298 continue;
10299
10300 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10301 if (FAILED(rc)) throw rc;
10302
10303 data.llNetworkAdapters.push_back(nic);
10304 }
10305 }
10306
10307 /* Serial ports */
10308 data.llSerialPorts.clear();
10309 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10310 {
10311 if (mSerialPorts[slot]->i_hasDefaults())
10312 continue;
10313
10314 settings::SerialPort s;
10315 s.ulSlot = slot;
10316 rc = mSerialPorts[slot]->i_saveSettings(s);
10317 if (FAILED(rc)) return rc;
10318
10319 data.llSerialPorts.push_back(s);
10320 }
10321
10322 /* Parallel ports */
10323 data.llParallelPorts.clear();
10324 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10325 {
10326 if (mParallelPorts[slot]->i_hasDefaults())
10327 continue;
10328
10329 settings::ParallelPort p;
10330 p.ulSlot = slot;
10331 rc = mParallelPorts[slot]->i_saveSettings(p);
10332 if (FAILED(rc)) return rc;
10333
10334 data.llParallelPorts.push_back(p);
10335 }
10336
10337 /* Audio adapter */
10338 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10339 if (FAILED(rc)) return rc;
10340
10341 rc = i_saveStorageControllers(data.storage);
10342 if (FAILED(rc)) return rc;
10343
10344 /* Shared folders */
10345 data.llSharedFolders.clear();
10346 for (HWData::SharedFolderList::const_iterator
10347 it = mHWData->mSharedFolders.begin();
10348 it != mHWData->mSharedFolders.end();
10349 ++it)
10350 {
10351 SharedFolder *pSF = *it;
10352 AutoCaller sfCaller(pSF);
10353 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10354 settings::SharedFolder sf;
10355 sf.strName = pSF->i_getName();
10356 sf.strHostPath = pSF->i_getHostPath();
10357 sf.fWritable = !!pSF->i_isWritable();
10358 sf.fAutoMount = !!pSF->i_isAutoMounted();
10359 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10360
10361 data.llSharedFolders.push_back(sf);
10362 }
10363
10364 // clipboard
10365 data.clipboardMode = mHWData->mClipboardMode;
10366 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10367
10368 // drag'n'drop
10369 data.dndMode = mHWData->mDnDMode;
10370
10371 /* Guest */
10372 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10373
10374 // IO settings
10375 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10376 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10377
10378 /* BandwidthControl (required) */
10379 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10380 if (FAILED(rc)) throw rc;
10381
10382 /* Host PCI devices */
10383 data.pciAttachments.clear();
10384 for (HWData::PCIDeviceAssignmentList::const_iterator
10385 it = mHWData->mPCIDeviceAssignments.begin();
10386 it != mHWData->mPCIDeviceAssignments.end();
10387 ++it)
10388 {
10389 ComObjPtr<PCIDeviceAttachment> pda = *it;
10390 settings::HostPCIDeviceAttachment hpda;
10391
10392 rc = pda->i_saveSettings(hpda);
10393 if (FAILED(rc)) throw rc;
10394
10395 data.pciAttachments.push_back(hpda);
10396 }
10397
10398 // guest properties
10399 data.llGuestProperties.clear();
10400#ifdef VBOX_WITH_GUEST_PROPS
10401 for (HWData::GuestPropertyMap::const_iterator
10402 it = mHWData->mGuestProperties.begin();
10403 it != mHWData->mGuestProperties.end();
10404 ++it)
10405 {
10406 HWData::GuestProperty property = it->second;
10407
10408 /* Remove transient guest properties at shutdown unless we
10409 * are saving state. Note that restoring snapshot intentionally
10410 * keeps them, they will be removed if appropriate once the final
10411 * machine state is set (as crashes etc. need to work). */
10412 if ( ( mData->mMachineState == MachineState_PoweredOff
10413 || mData->mMachineState == MachineState_Aborted
10414 || mData->mMachineState == MachineState_Teleported)
10415 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10416 continue;
10417 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10418 prop.strName = it->first;
10419 prop.strValue = property.strValue;
10420 prop.timestamp = (uint64_t)property.mTimestamp;
10421 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10422 GuestPropWriteFlags(property.mFlags, szFlags);
10423 prop.strFlags = szFlags;
10424
10425 data.llGuestProperties.push_back(prop);
10426 }
10427
10428 /* I presume this doesn't require a backup(). */
10429 mData->mGuestPropertiesModified = FALSE;
10430#endif /* VBOX_WITH_GUEST_PROPS defined */
10431
10432 *pDbg = mHWData->mDebugging;
10433 *pAutostart = mHWData->mAutostart;
10434
10435 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10436 }
10437 catch (std::bad_alloc &)
10438 {
10439 return E_OUTOFMEMORY;
10440 }
10441
10442 AssertComRC(rc);
10443 return rc;
10444}
10445
10446/**
10447 * Saves the storage controller configuration.
10448 *
10449 * @param data storage settings.
10450 */
10451HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10452{
10453 data.llStorageControllers.clear();
10454
10455 for (StorageControllerList::const_iterator
10456 it = mStorageControllers->begin();
10457 it != mStorageControllers->end();
10458 ++it)
10459 {
10460 HRESULT rc;
10461 ComObjPtr<StorageController> pCtl = *it;
10462
10463 settings::StorageController ctl;
10464 ctl.strName = pCtl->i_getName();
10465 ctl.controllerType = pCtl->i_getControllerType();
10466 ctl.storageBus = pCtl->i_getStorageBus();
10467 ctl.ulInstance = pCtl->i_getInstance();
10468 ctl.fBootable = pCtl->i_getBootable();
10469
10470 /* Save the port count. */
10471 ULONG portCount;
10472 rc = pCtl->COMGETTER(PortCount)(&portCount);
10473 ComAssertComRCRet(rc, rc);
10474 ctl.ulPortCount = portCount;
10475
10476 /* Save fUseHostIOCache */
10477 BOOL fUseHostIOCache;
10478 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10479 ComAssertComRCRet(rc, rc);
10480 ctl.fUseHostIOCache = !!fUseHostIOCache;
10481
10482 /* save the devices now. */
10483 rc = i_saveStorageDevices(pCtl, ctl);
10484 ComAssertComRCRet(rc, rc);
10485
10486 data.llStorageControllers.push_back(ctl);
10487 }
10488
10489 return S_OK;
10490}
10491
10492/**
10493 * Saves the hard disk configuration.
10494 */
10495HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10496 settings::StorageController &data)
10497{
10498 MediumAttachmentList atts;
10499
10500 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10501 if (FAILED(rc)) return rc;
10502
10503 data.llAttachedDevices.clear();
10504 for (MediumAttachmentList::const_iterator
10505 it = atts.begin();
10506 it != atts.end();
10507 ++it)
10508 {
10509 settings::AttachedDevice dev;
10510 IMediumAttachment *iA = *it;
10511 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10512 Medium *pMedium = pAttach->i_getMedium();
10513
10514 dev.deviceType = pAttach->i_getType();
10515 dev.lPort = pAttach->i_getPort();
10516 dev.lDevice = pAttach->i_getDevice();
10517 dev.fPassThrough = pAttach->i_getPassthrough();
10518 dev.fHotPluggable = pAttach->i_getHotPluggable();
10519 if (pMedium)
10520 {
10521 if (pMedium->i_isHostDrive())
10522 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10523 else
10524 dev.uuid = pMedium->i_getId();
10525 dev.fTempEject = pAttach->i_getTempEject();
10526 dev.fNonRotational = pAttach->i_getNonRotational();
10527 dev.fDiscard = pAttach->i_getDiscard();
10528 }
10529
10530 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10531
10532 data.llAttachedDevices.push_back(dev);
10533 }
10534
10535 return S_OK;
10536}
10537
10538/**
10539 * Saves machine state settings as defined by aFlags
10540 * (SaveSTS_* values).
10541 *
10542 * @param aFlags Combination of SaveSTS_* flags.
10543 *
10544 * @note Locks objects for writing.
10545 */
10546HRESULT Machine::i_saveStateSettings(int aFlags)
10547{
10548 if (aFlags == 0)
10549 return S_OK;
10550
10551 AutoCaller autoCaller(this);
10552 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10553
10554 /* This object's write lock is also necessary to serialize file access
10555 * (prevent concurrent reads and writes) */
10556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10557
10558 HRESULT rc = S_OK;
10559
10560 Assert(mData->pMachineConfigFile);
10561
10562 try
10563 {
10564 if (aFlags & SaveSTS_CurStateModified)
10565 mData->pMachineConfigFile->fCurrentStateModified = true;
10566
10567 if (aFlags & SaveSTS_StateFilePath)
10568 {
10569 if (!mSSData->strStateFilePath.isEmpty())
10570 /* try to make the file name relative to the settings file dir */
10571 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10572 else
10573 mData->pMachineConfigFile->strStateFile.setNull();
10574 }
10575
10576 if (aFlags & SaveSTS_StateTimeStamp)
10577 {
10578 Assert( mData->mMachineState != MachineState_Aborted
10579 || mSSData->strStateFilePath.isEmpty());
10580
10581 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10582
10583 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10584 || mData->mMachineState == MachineState_AbortedSaved);
10585/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10586 }
10587
10588 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10589 }
10590 catch (...)
10591 {
10592 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10593 }
10594
10595 return rc;
10596}
10597
10598/**
10599 * Ensures that the given medium is added to a media registry. If this machine
10600 * was created with 4.0 or later, then the machine registry is used. Otherwise
10601 * the global VirtualBox media registry is used.
10602 *
10603 * Caller must NOT hold machine lock, media tree or any medium locks!
10604 *
10605 * @param pMedium
10606 */
10607void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10608{
10609 /* Paranoia checks: do not hold machine or media tree locks. */
10610 AssertReturnVoid(!isWriteLockOnCurrentThread());
10611 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10612
10613 ComObjPtr<Medium> pBase;
10614 {
10615 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10616 pBase = pMedium->i_getBase();
10617 }
10618
10619 /* Paranoia checks: do not hold medium locks. */
10620 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10621 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10622
10623 // decide which medium registry to use now that the medium is attached:
10624 Guid uuid;
10625 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10626 if (fCanHaveOwnMediaRegistry)
10627 // machine XML is VirtualBox 4.0 or higher:
10628 uuid = i_getId(); // machine UUID
10629 else
10630 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10631
10632 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10633 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10634 if (pMedium->i_addRegistry(uuid))
10635 mParent->i_markRegistryModified(uuid);
10636
10637 /* For more complex hard disk structures it can happen that the base
10638 * medium isn't yet associated with any medium registry. Do that now. */
10639 if (pMedium != pBase)
10640 {
10641 /* Tree lock needed by Medium::addRegistry when recursing. */
10642 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10643 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10644 {
10645 treeLock.release();
10646 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10647 treeLock.acquire();
10648 }
10649 if (pBase->i_addRegistryRecursive(uuid))
10650 {
10651 treeLock.release();
10652 mParent->i_markRegistryModified(uuid);
10653 }
10654 }
10655}
10656
10657/**
10658 * Creates differencing hard disks for all normal hard disks attached to this
10659 * machine and a new set of attachments to refer to created disks.
10660 *
10661 * Used when taking a snapshot or when deleting the current state. Gets called
10662 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10663 *
10664 * This method assumes that mMediumAttachments contains the original hard disk
10665 * attachments it needs to create diffs for. On success, these attachments will
10666 * be replaced with the created diffs.
10667 *
10668 * Attachments with non-normal hard disks are left as is.
10669 *
10670 * If @a aOnline is @c false then the original hard disks that require implicit
10671 * diffs will be locked for reading. Otherwise it is assumed that they are
10672 * already locked for writing (when the VM was started). Note that in the latter
10673 * case it is responsibility of the caller to lock the newly created diffs for
10674 * writing if this method succeeds.
10675 *
10676 * @param aProgress Progress object to run (must contain at least as
10677 * many operations left as the number of hard disks
10678 * attached).
10679 * @param aWeight Weight of this operation.
10680 * @param aOnline Whether the VM was online prior to this operation.
10681 *
10682 * @note The progress object is not marked as completed, neither on success nor
10683 * on failure. This is a responsibility of the caller.
10684 *
10685 * @note Locks this object and the media tree for writing.
10686 */
10687HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10688 ULONG aWeight,
10689 bool aOnline)
10690{
10691 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10692
10693 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10694 AssertReturn(!!pProgressControl, E_INVALIDARG);
10695
10696 AutoCaller autoCaller(this);
10697 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10698
10699 AutoMultiWriteLock2 alock(this->lockHandle(),
10700 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10701
10702 /* must be in a protective state because we release the lock below */
10703 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10704 || mData->mMachineState == MachineState_OnlineSnapshotting
10705 || mData->mMachineState == MachineState_LiveSnapshotting
10706 || mData->mMachineState == MachineState_RestoringSnapshot
10707 || mData->mMachineState == MachineState_DeletingSnapshot
10708 , E_FAIL);
10709
10710 HRESULT rc = S_OK;
10711
10712 // use appropriate locked media map (online or offline)
10713 MediumLockListMap lockedMediaOffline;
10714 MediumLockListMap *lockedMediaMap;
10715 if (aOnline)
10716 lockedMediaMap = &mData->mSession.mLockedMedia;
10717 else
10718 lockedMediaMap = &lockedMediaOffline;
10719
10720 try
10721 {
10722 if (!aOnline)
10723 {
10724 /* lock all attached hard disks early to detect "in use"
10725 * situations before creating actual diffs */
10726 for (MediumAttachmentList::const_iterator
10727 it = mMediumAttachments->begin();
10728 it != mMediumAttachments->end();
10729 ++it)
10730 {
10731 MediumAttachment *pAtt = *it;
10732 if (pAtt->i_getType() == DeviceType_HardDisk)
10733 {
10734 Medium *pMedium = pAtt->i_getMedium();
10735 Assert(pMedium);
10736
10737 MediumLockList *pMediumLockList(new MediumLockList());
10738 alock.release();
10739 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10740 NULL /* pToLockWrite */,
10741 false /* fMediumLockWriteAll */,
10742 NULL,
10743 *pMediumLockList);
10744 alock.acquire();
10745 if (FAILED(rc))
10746 {
10747 delete pMediumLockList;
10748 throw rc;
10749 }
10750 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10751 if (FAILED(rc))
10752 {
10753 throw setError(rc,
10754 tr("Collecting locking information for all attached media failed"));
10755 }
10756 }
10757 }
10758
10759 /* Now lock all media. If this fails, nothing is locked. */
10760 alock.release();
10761 rc = lockedMediaMap->Lock();
10762 alock.acquire();
10763 if (FAILED(rc))
10764 {
10765 throw setError(rc,
10766 tr("Locking of attached media failed"));
10767 }
10768 }
10769
10770 /* remember the current list (note that we don't use backup() since
10771 * mMediumAttachments may be already backed up) */
10772 MediumAttachmentList atts = *mMediumAttachments.data();
10773
10774 /* start from scratch */
10775 mMediumAttachments->clear();
10776
10777 /* go through remembered attachments and create diffs for normal hard
10778 * disks and attach them */
10779 for (MediumAttachmentList::const_iterator
10780 it = atts.begin();
10781 it != atts.end();
10782 ++it)
10783 {
10784 MediumAttachment *pAtt = *it;
10785
10786 DeviceType_T devType = pAtt->i_getType();
10787 Medium *pMedium = pAtt->i_getMedium();
10788
10789 if ( devType != DeviceType_HardDisk
10790 || pMedium == NULL
10791 || pMedium->i_getType() != MediumType_Normal)
10792 {
10793 /* copy the attachment as is */
10794
10795 /** @todo the progress object created in SessionMachine::TakeSnaphot
10796 * only expects operations for hard disks. Later other
10797 * device types need to show up in the progress as well. */
10798 if (devType == DeviceType_HardDisk)
10799 {
10800 if (pMedium == NULL)
10801 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10802 aWeight); // weight
10803 else
10804 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10805 pMedium->i_getBase()->i_getName().c_str()).raw(),
10806 aWeight); // weight
10807 }
10808
10809 mMediumAttachments->push_back(pAtt);
10810 continue;
10811 }
10812
10813 /* need a diff */
10814 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10815 pMedium->i_getBase()->i_getName().c_str()).raw(),
10816 aWeight); // weight
10817
10818 Utf8Str strFullSnapshotFolder;
10819 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10820
10821 ComObjPtr<Medium> diff;
10822 diff.createObject();
10823 // store the diff in the same registry as the parent
10824 // (this cannot fail here because we can't create implicit diffs for
10825 // unregistered images)
10826 Guid uuidRegistryParent;
10827 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10828 Assert(fInRegistry); NOREF(fInRegistry);
10829 rc = diff->init(mParent,
10830 pMedium->i_getPreferredDiffFormat(),
10831 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10832 uuidRegistryParent,
10833 DeviceType_HardDisk);
10834 if (FAILED(rc)) throw rc;
10835
10836 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10837 * the push_back? Looks like we're going to release medium with the
10838 * wrong kind of lock (general issue with if we fail anywhere at all)
10839 * and an orphaned VDI in the snapshots folder. */
10840
10841 /* update the appropriate lock list */
10842 MediumLockList *pMediumLockList;
10843 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10844 AssertComRCThrowRC(rc);
10845 if (aOnline)
10846 {
10847 alock.release();
10848 /* The currently attached medium will be read-only, change
10849 * the lock type to read. */
10850 rc = pMediumLockList->Update(pMedium, false);
10851 alock.acquire();
10852 AssertComRCThrowRC(rc);
10853 }
10854
10855 /* release the locks before the potentially lengthy operation */
10856 alock.release();
10857 rc = pMedium->i_createDiffStorage(diff,
10858 pMedium->i_getPreferredDiffVariant(),
10859 pMediumLockList,
10860 NULL /* aProgress */,
10861 true /* aWait */,
10862 false /* aNotify */);
10863 alock.acquire();
10864 if (FAILED(rc)) throw rc;
10865
10866 /* actual lock list update is done in Machine::i_commitMedia */
10867
10868 rc = diff->i_addBackReference(mData->mUuid);
10869 AssertComRCThrowRC(rc);
10870
10871 /* add a new attachment */
10872 ComObjPtr<MediumAttachment> attachment;
10873 attachment.createObject();
10874 rc = attachment->init(this,
10875 diff,
10876 pAtt->i_getControllerName(),
10877 pAtt->i_getPort(),
10878 pAtt->i_getDevice(),
10879 DeviceType_HardDisk,
10880 true /* aImplicit */,
10881 false /* aPassthrough */,
10882 false /* aTempEject */,
10883 pAtt->i_getNonRotational(),
10884 pAtt->i_getDiscard(),
10885 pAtt->i_getHotPluggable(),
10886 pAtt->i_getBandwidthGroup());
10887 if (FAILED(rc)) throw rc;
10888
10889 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10890 AssertComRCThrowRC(rc);
10891 mMediumAttachments->push_back(attachment);
10892 }
10893 }
10894 catch (HRESULT aRC) { rc = aRC; }
10895
10896 /* unlock all hard disks we locked when there is no VM */
10897 if (!aOnline)
10898 {
10899 ErrorInfoKeeper eik;
10900
10901 HRESULT rc1 = lockedMediaMap->Clear();
10902 AssertComRC(rc1);
10903 }
10904
10905 return rc;
10906}
10907
10908/**
10909 * Deletes implicit differencing hard disks created either by
10910 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10911 * mMediumAttachments.
10912 *
10913 * Note that to delete hard disks created by #attachDevice() this method is
10914 * called from #i_rollbackMedia() when the changes are rolled back.
10915 *
10916 * @note Locks this object and the media tree for writing.
10917 */
10918HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10919{
10920 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10921
10922 AutoCaller autoCaller(this);
10923 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10924
10925 AutoMultiWriteLock2 alock(this->lockHandle(),
10926 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10927
10928 /* We absolutely must have backed up state. */
10929 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10930
10931 /* Check if there are any implicitly created diff images. */
10932 bool fImplicitDiffs = false;
10933 for (MediumAttachmentList::const_iterator
10934 it = mMediumAttachments->begin();
10935 it != mMediumAttachments->end();
10936 ++it)
10937 {
10938 const ComObjPtr<MediumAttachment> &pAtt = *it;
10939 if (pAtt->i_isImplicit())
10940 {
10941 fImplicitDiffs = true;
10942 break;
10943 }
10944 }
10945 /* If there is nothing to do, leave early. This saves lots of image locking
10946 * effort. It also avoids a MachineStateChanged event without real reason.
10947 * This is important e.g. when loading a VM config, because there should be
10948 * no events. Otherwise API clients can become thoroughly confused for
10949 * inaccessible VMs (the code for loading VM configs uses this method for
10950 * cleanup if the config makes no sense), as they take such events as an
10951 * indication that the VM is alive, and they would force the VM config to
10952 * be reread, leading to an endless loop. */
10953 if (!fImplicitDiffs)
10954 return S_OK;
10955
10956 HRESULT rc = S_OK;
10957 MachineState_T oldState = mData->mMachineState;
10958
10959 /* will release the lock before the potentially lengthy operation,
10960 * so protect with the special state (unless already protected) */
10961 if ( oldState != MachineState_Snapshotting
10962 && oldState != MachineState_OnlineSnapshotting
10963 && oldState != MachineState_LiveSnapshotting
10964 && oldState != MachineState_RestoringSnapshot
10965 && oldState != MachineState_DeletingSnapshot
10966 && oldState != MachineState_DeletingSnapshotOnline
10967 && oldState != MachineState_DeletingSnapshotPaused
10968 )
10969 i_setMachineState(MachineState_SettingUp);
10970
10971 // use appropriate locked media map (online or offline)
10972 MediumLockListMap lockedMediaOffline;
10973 MediumLockListMap *lockedMediaMap;
10974 if (aOnline)
10975 lockedMediaMap = &mData->mSession.mLockedMedia;
10976 else
10977 lockedMediaMap = &lockedMediaOffline;
10978
10979 try
10980 {
10981 if (!aOnline)
10982 {
10983 /* lock all attached hard disks early to detect "in use"
10984 * situations before deleting actual diffs */
10985 for (MediumAttachmentList::const_iterator
10986 it = mMediumAttachments->begin();
10987 it != mMediumAttachments->end();
10988 ++it)
10989 {
10990 MediumAttachment *pAtt = *it;
10991 if (pAtt->i_getType() == DeviceType_HardDisk)
10992 {
10993 Medium *pMedium = pAtt->i_getMedium();
10994 Assert(pMedium);
10995
10996 MediumLockList *pMediumLockList(new MediumLockList());
10997 alock.release();
10998 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10999 NULL /* pToLockWrite */,
11000 false /* fMediumLockWriteAll */,
11001 NULL,
11002 *pMediumLockList);
11003 alock.acquire();
11004
11005 if (FAILED(rc))
11006 {
11007 delete pMediumLockList;
11008 throw rc;
11009 }
11010
11011 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11012 if (FAILED(rc))
11013 throw rc;
11014 }
11015 }
11016
11017 if (FAILED(rc))
11018 throw rc;
11019 } // end of offline
11020
11021 /* Lock lists are now up to date and include implicitly created media */
11022
11023 /* Go through remembered attachments and delete all implicitly created
11024 * diffs and fix up the attachment information */
11025 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11026 MediumAttachmentList implicitAtts;
11027 for (MediumAttachmentList::const_iterator
11028 it = mMediumAttachments->begin();
11029 it != mMediumAttachments->end();
11030 ++it)
11031 {
11032 ComObjPtr<MediumAttachment> pAtt = *it;
11033 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11034 if (pMedium.isNull())
11035 continue;
11036
11037 // Implicit attachments go on the list for deletion and back references are removed.
11038 if (pAtt->i_isImplicit())
11039 {
11040 /* Deassociate and mark for deletion */
11041 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11042 rc = pMedium->i_removeBackReference(mData->mUuid);
11043 if (FAILED(rc))
11044 throw rc;
11045 implicitAtts.push_back(pAtt);
11046 continue;
11047 }
11048
11049 /* Was this medium attached before? */
11050 if (!i_findAttachment(oldAtts, pMedium))
11051 {
11052 /* no: de-associate */
11053 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11054 rc = pMedium->i_removeBackReference(mData->mUuid);
11055 if (FAILED(rc))
11056 throw rc;
11057 continue;
11058 }
11059 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11060 }
11061
11062 /* If there are implicit attachments to delete, throw away the lock
11063 * map contents (which will unlock all media) since the medium
11064 * attachments will be rolled back. Below we need to completely
11065 * recreate the lock map anyway since it is infinitely complex to
11066 * do this incrementally (would need reconstructing each attachment
11067 * change, which would be extremely hairy). */
11068 if (implicitAtts.size() != 0)
11069 {
11070 ErrorInfoKeeper eik;
11071
11072 HRESULT rc1 = lockedMediaMap->Clear();
11073 AssertComRC(rc1);
11074 }
11075
11076 /* rollback hard disk changes */
11077 mMediumAttachments.rollback();
11078
11079 MultiResult mrc(S_OK);
11080
11081 // Delete unused implicit diffs.
11082 if (implicitAtts.size() != 0)
11083 {
11084 alock.release();
11085
11086 for (MediumAttachmentList::const_iterator
11087 it = implicitAtts.begin();
11088 it != implicitAtts.end();
11089 ++it)
11090 {
11091 // Remove medium associated with this attachment.
11092 ComObjPtr<MediumAttachment> pAtt = *it;
11093 Assert(pAtt);
11094 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11095 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11096 Assert(pMedium);
11097
11098 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11099 // continue on delete failure, just collect error messages
11100 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11101 pMedium->i_getLocationFull().c_str() ));
11102 mrc = rc;
11103 }
11104 // Clear the list of deleted implicit attachments now, while not
11105 // holding the lock, as it will ultimately trigger Medium::uninit()
11106 // calls which assume that the media tree lock isn't held.
11107 implicitAtts.clear();
11108
11109 alock.acquire();
11110
11111 /* if there is a VM recreate media lock map as mentioned above,
11112 * otherwise it is a waste of time and we leave things unlocked */
11113 if (aOnline)
11114 {
11115 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11116 /* must never be NULL, but better safe than sorry */
11117 if (!pMachine.isNull())
11118 {
11119 alock.release();
11120 rc = mData->mSession.mMachine->i_lockMedia();
11121 alock.acquire();
11122 if (FAILED(rc))
11123 throw rc;
11124 }
11125 }
11126 }
11127 }
11128 catch (HRESULT aRC) {rc = aRC;}
11129
11130 if (mData->mMachineState == MachineState_SettingUp)
11131 i_setMachineState(oldState);
11132
11133 /* unlock all hard disks we locked when there is no VM */
11134 if (!aOnline)
11135 {
11136 ErrorInfoKeeper eik;
11137
11138 HRESULT rc1 = lockedMediaMap->Clear();
11139 AssertComRC(rc1);
11140 }
11141
11142 return rc;
11143}
11144
11145
11146/**
11147 * Looks through the given list of media attachments for one with the given parameters
11148 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11149 * can be searched as well if needed.
11150 *
11151 * @param ll
11152 * @param aControllerName
11153 * @param aControllerPort
11154 * @param aDevice
11155 * @return
11156 */
11157MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11158 const Utf8Str &aControllerName,
11159 LONG aControllerPort,
11160 LONG aDevice)
11161{
11162 for (MediumAttachmentList::const_iterator
11163 it = ll.begin();
11164 it != ll.end();
11165 ++it)
11166 {
11167 MediumAttachment *pAttach = *it;
11168 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11169 return pAttach;
11170 }
11171
11172 return NULL;
11173}
11174
11175/**
11176 * Looks through the given list of media attachments for one with the given parameters
11177 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11178 * can be searched as well if needed.
11179 *
11180 * @param ll
11181 * @param pMedium
11182 * @return
11183 */
11184MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11185 ComObjPtr<Medium> pMedium)
11186{
11187 for (MediumAttachmentList::const_iterator
11188 it = ll.begin();
11189 it != ll.end();
11190 ++it)
11191 {
11192 MediumAttachment *pAttach = *it;
11193 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11194 if (pMediumThis == pMedium)
11195 return pAttach;
11196 }
11197
11198 return NULL;
11199}
11200
11201/**
11202 * Looks through the given list of media attachments for one with the given parameters
11203 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11204 * can be searched as well if needed.
11205 *
11206 * @param ll
11207 * @param id
11208 * @return
11209 */
11210MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11211 Guid &id)
11212{
11213 for (MediumAttachmentList::const_iterator
11214 it = ll.begin();
11215 it != ll.end();
11216 ++it)
11217 {
11218 MediumAttachment *pAttach = *it;
11219 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11220 if (pMediumThis->i_getId() == id)
11221 return pAttach;
11222 }
11223
11224 return NULL;
11225}
11226
11227/**
11228 * Main implementation for Machine::DetachDevice. This also gets called
11229 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11230 *
11231 * @param pAttach Medium attachment to detach.
11232 * @param writeLock Machine write lock which the caller must have locked once.
11233 * This may be released temporarily in here.
11234 * @param pSnapshot If NULL, then the detachment is for the current machine.
11235 * Otherwise this is for a SnapshotMachine, and this must be
11236 * its snapshot.
11237 * @return
11238 */
11239HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11240 AutoWriteLock &writeLock,
11241 Snapshot *pSnapshot)
11242{
11243 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11244 DeviceType_T mediumType = pAttach->i_getType();
11245
11246 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11247
11248 if (pAttach->i_isImplicit())
11249 {
11250 /* attempt to implicitly delete the implicitly created diff */
11251
11252 /// @todo move the implicit flag from MediumAttachment to Medium
11253 /// and forbid any hard disk operation when it is implicit. Or maybe
11254 /// a special media state for it to make it even more simple.
11255
11256 Assert(mMediumAttachments.isBackedUp());
11257
11258 /* will release the lock before the potentially lengthy operation, so
11259 * protect with the special state */
11260 MachineState_T oldState = mData->mMachineState;
11261 i_setMachineState(MachineState_SettingUp);
11262
11263 writeLock.release();
11264
11265 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11266 true /*aWait*/,
11267 false /*aNotify*/);
11268
11269 writeLock.acquire();
11270
11271 i_setMachineState(oldState);
11272
11273 if (FAILED(rc)) return rc;
11274 }
11275
11276 i_setModified(IsModified_Storage);
11277 mMediumAttachments.backup();
11278 mMediumAttachments->remove(pAttach);
11279
11280 if (!oldmedium.isNull())
11281 {
11282 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11283 if (pSnapshot)
11284 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11285 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11286 else if (mediumType != DeviceType_HardDisk)
11287 oldmedium->i_removeBackReference(mData->mUuid);
11288 }
11289
11290 return S_OK;
11291}
11292
11293/**
11294 * Goes thru all media of the given list and
11295 *
11296 * 1) calls i_detachDevice() on each of them for this machine and
11297 * 2) adds all Medium objects found in the process to the given list,
11298 * depending on cleanupMode.
11299 *
11300 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11301 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11302 * media to the list.
11303 *
11304 * This gets called from Machine::Unregister, both for the actual Machine and
11305 * the SnapshotMachine objects that might be found in the snapshots.
11306 *
11307 * Requires caller and locking. The machine lock must be passed in because it
11308 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11309 *
11310 * @param writeLock Machine lock from top-level caller; this gets passed to
11311 * i_detachDevice.
11312 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11313 * object if called for a SnapshotMachine.
11314 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11315 * added to llMedia; if Full, then all media get added;
11316 * otherwise no media get added.
11317 * @param llMedia Caller's list to receive Medium objects which got detached so
11318 * caller can close() them, depending on cleanupMode.
11319 * @return
11320 */
11321HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11322 Snapshot *pSnapshot,
11323 CleanupMode_T cleanupMode,
11324 MediaList &llMedia)
11325{
11326 Assert(isWriteLockOnCurrentThread());
11327
11328 HRESULT rc;
11329
11330 // make a temporary list because i_detachDevice invalidates iterators into
11331 // mMediumAttachments
11332 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11333
11334 for (MediumAttachmentList::iterator
11335 it = llAttachments2.begin();
11336 it != llAttachments2.end();
11337 ++it)
11338 {
11339 ComObjPtr<MediumAttachment> &pAttach = *it;
11340 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11341
11342 if (!pMedium.isNull())
11343 {
11344 AutoCaller mac(pMedium);
11345 if (FAILED(mac.rc())) return mac.rc();
11346 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11347 DeviceType_T devType = pMedium->i_getDeviceType();
11348 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11349 && devType == DeviceType_HardDisk)
11350 || (cleanupMode == CleanupMode_Full)
11351 )
11352 {
11353 llMedia.push_back(pMedium);
11354 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11355 /* Not allowed to keep this lock as below we need the parent
11356 * medium lock, and the lock order is parent to child. */
11357 lock.release();
11358 /*
11359 * Search for medias which are not attached to any machine, but
11360 * in the chain to an attached disk. Mediums are only consided
11361 * if they are:
11362 * - have only one child
11363 * - no references to any machines
11364 * - are of normal medium type
11365 */
11366 while (!pParent.isNull())
11367 {
11368 AutoCaller mac1(pParent);
11369 if (FAILED(mac1.rc())) return mac1.rc();
11370 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11371 if (pParent->i_getChildren().size() == 1)
11372 {
11373 if ( pParent->i_getMachineBackRefCount() == 0
11374 && pParent->i_getType() == MediumType_Normal
11375 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11376 llMedia.push_back(pParent);
11377 }
11378 else
11379 break;
11380 pParent = pParent->i_getParent();
11381 }
11382 }
11383 }
11384
11385 // real machine: then we need to use the proper method
11386 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11387
11388 if (FAILED(rc))
11389 return rc;
11390 }
11391
11392 return S_OK;
11393}
11394
11395/**
11396 * Perform deferred hard disk detachments.
11397 *
11398 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11399 * changed (not backed up).
11400 *
11401 * If @a aOnline is @c true then this method will also unlock the old hard
11402 * disks for which the new implicit diffs were created and will lock these new
11403 * diffs for writing.
11404 *
11405 * @param aOnline Whether the VM was online prior to this operation.
11406 *
11407 * @note Locks this object for writing!
11408 */
11409void Machine::i_commitMedia(bool aOnline /*= false*/)
11410{
11411 AutoCaller autoCaller(this);
11412 AssertComRCReturnVoid(autoCaller.rc());
11413
11414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11415
11416 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11417
11418 HRESULT rc = S_OK;
11419
11420 /* no attach/detach operations -- nothing to do */
11421 if (!mMediumAttachments.isBackedUp())
11422 return;
11423
11424 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11425 bool fMediaNeedsLocking = false;
11426
11427 /* enumerate new attachments */
11428 for (MediumAttachmentList::const_iterator
11429 it = mMediumAttachments->begin();
11430 it != mMediumAttachments->end();
11431 ++it)
11432 {
11433 MediumAttachment *pAttach = *it;
11434
11435 pAttach->i_commit();
11436
11437 Medium *pMedium = pAttach->i_getMedium();
11438 bool fImplicit = pAttach->i_isImplicit();
11439
11440 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11441 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11442 fImplicit));
11443
11444 /** @todo convert all this Machine-based voodoo to MediumAttachment
11445 * based commit logic. */
11446 if (fImplicit)
11447 {
11448 /* convert implicit attachment to normal */
11449 pAttach->i_setImplicit(false);
11450
11451 if ( aOnline
11452 && pMedium
11453 && pAttach->i_getType() == DeviceType_HardDisk
11454 )
11455 {
11456 /* update the appropriate lock list */
11457 MediumLockList *pMediumLockList;
11458 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11459 AssertComRC(rc);
11460 if (pMediumLockList)
11461 {
11462 /* unlock if there's a need to change the locking */
11463 if (!fMediaNeedsLocking)
11464 {
11465 rc = mData->mSession.mLockedMedia.Unlock();
11466 AssertComRC(rc);
11467 fMediaNeedsLocking = true;
11468 }
11469 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11470 AssertComRC(rc);
11471 rc = pMediumLockList->Append(pMedium, true);
11472 AssertComRC(rc);
11473 }
11474 }
11475
11476 continue;
11477 }
11478
11479 if (pMedium)
11480 {
11481 /* was this medium attached before? */
11482 for (MediumAttachmentList::iterator
11483 oldIt = oldAtts.begin();
11484 oldIt != oldAtts.end();
11485 ++oldIt)
11486 {
11487 MediumAttachment *pOldAttach = *oldIt;
11488 if (pOldAttach->i_getMedium() == pMedium)
11489 {
11490 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11491
11492 /* yes: remove from old to avoid de-association */
11493 oldAtts.erase(oldIt);
11494 break;
11495 }
11496 }
11497 }
11498 }
11499
11500 /* enumerate remaining old attachments and de-associate from the
11501 * current machine state */
11502 for (MediumAttachmentList::const_iterator
11503 it = oldAtts.begin();
11504 it != oldAtts.end();
11505 ++it)
11506 {
11507 MediumAttachment *pAttach = *it;
11508 Medium *pMedium = pAttach->i_getMedium();
11509
11510 /* Detach only hard disks, since DVD/floppy media is detached
11511 * instantly in MountMedium. */
11512 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11513 {
11514 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11515
11516 /* now de-associate from the current machine state */
11517 rc = pMedium->i_removeBackReference(mData->mUuid);
11518 AssertComRC(rc);
11519
11520 if (aOnline)
11521 {
11522 /* unlock since medium is not used anymore */
11523 MediumLockList *pMediumLockList;
11524 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11525 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11526 {
11527 /* this happens for online snapshots, there the attachment
11528 * is changing, but only to a diff image created under
11529 * the old one, so there is no separate lock list */
11530 Assert(!pMediumLockList);
11531 }
11532 else
11533 {
11534 AssertComRC(rc);
11535 if (pMediumLockList)
11536 {
11537 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11538 AssertComRC(rc);
11539 }
11540 }
11541 }
11542 }
11543 }
11544
11545 /* take media locks again so that the locking state is consistent */
11546 if (fMediaNeedsLocking)
11547 {
11548 Assert(aOnline);
11549 rc = mData->mSession.mLockedMedia.Lock();
11550 AssertComRC(rc);
11551 }
11552
11553 /* commit the hard disk changes */
11554 mMediumAttachments.commit();
11555
11556 if (i_isSessionMachine())
11557 {
11558 /*
11559 * Update the parent machine to point to the new owner.
11560 * This is necessary because the stored parent will point to the
11561 * session machine otherwise and cause crashes or errors later
11562 * when the session machine gets invalid.
11563 */
11564 /** @todo Change the MediumAttachment class to behave like any other
11565 * class in this regard by creating peer MediumAttachment
11566 * objects for session machines and share the data with the peer
11567 * machine.
11568 */
11569 for (MediumAttachmentList::const_iterator
11570 it = mMediumAttachments->begin();
11571 it != mMediumAttachments->end();
11572 ++it)
11573 (*it)->i_updateParentMachine(mPeer);
11574
11575 /* attach new data to the primary machine and reshare it */
11576 mPeer->mMediumAttachments.attach(mMediumAttachments);
11577 }
11578
11579 return;
11580}
11581
11582/**
11583 * Perform deferred deletion of implicitly created diffs.
11584 *
11585 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11586 * changed (not backed up).
11587 *
11588 * @note Locks this object for writing!
11589 */
11590void Machine::i_rollbackMedia()
11591{
11592 AutoCaller autoCaller(this);
11593 AssertComRCReturnVoid(autoCaller.rc());
11594
11595 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11596 LogFlowThisFunc(("Entering rollbackMedia\n"));
11597
11598 HRESULT rc = S_OK;
11599
11600 /* no attach/detach operations -- nothing to do */
11601 if (!mMediumAttachments.isBackedUp())
11602 return;
11603
11604 /* enumerate new attachments */
11605 for (MediumAttachmentList::const_iterator
11606 it = mMediumAttachments->begin();
11607 it != mMediumAttachments->end();
11608 ++it)
11609 {
11610 MediumAttachment *pAttach = *it;
11611 /* Fix up the backrefs for DVD/floppy media. */
11612 if (pAttach->i_getType() != DeviceType_HardDisk)
11613 {
11614 Medium *pMedium = pAttach->i_getMedium();
11615 if (pMedium)
11616 {
11617 rc = pMedium->i_removeBackReference(mData->mUuid);
11618 AssertComRC(rc);
11619 }
11620 }
11621
11622 (*it)->i_rollback();
11623
11624 pAttach = *it;
11625 /* Fix up the backrefs for DVD/floppy media. */
11626 if (pAttach->i_getType() != DeviceType_HardDisk)
11627 {
11628 Medium *pMedium = pAttach->i_getMedium();
11629 if (pMedium)
11630 {
11631 rc = pMedium->i_addBackReference(mData->mUuid);
11632 AssertComRC(rc);
11633 }
11634 }
11635 }
11636
11637 /** @todo convert all this Machine-based voodoo to MediumAttachment
11638 * based rollback logic. */
11639 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11640
11641 return;
11642}
11643
11644/**
11645 * Returns true if the settings file is located in the directory named exactly
11646 * as the machine; this means, among other things, that the machine directory
11647 * should be auto-renamed.
11648 *
11649 * @param aSettingsDir if not NULL, the full machine settings file directory
11650 * name will be assigned there.
11651 *
11652 * @note Doesn't lock anything.
11653 * @note Not thread safe (must be called from this object's lock).
11654 */
11655bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11656{
11657 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11658 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11659 if (aSettingsDir)
11660 *aSettingsDir = strMachineDirName;
11661 strMachineDirName.stripPath(); // vmname
11662 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11663 strConfigFileOnly.stripPath() // vmname.vbox
11664 .stripSuffix(); // vmname
11665 /** @todo hack, make somehow use of ComposeMachineFilename */
11666 if (mUserData->s.fDirectoryIncludesUUID)
11667 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11668
11669 AssertReturn(!strMachineDirName.isEmpty(), false);
11670 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11671
11672 return strMachineDirName == strConfigFileOnly;
11673}
11674
11675/**
11676 * Discards all changes to machine settings.
11677 *
11678 * @param aNotify Whether to notify the direct session about changes or not.
11679 *
11680 * @note Locks objects for writing!
11681 */
11682void Machine::i_rollback(bool aNotify)
11683{
11684 AutoCaller autoCaller(this);
11685 AssertComRCReturn(autoCaller.rc(), (void)0);
11686
11687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11688
11689 if (!mStorageControllers.isNull())
11690 {
11691 if (mStorageControllers.isBackedUp())
11692 {
11693 /* unitialize all new devices (absent in the backed up list). */
11694 StorageControllerList *backedList = mStorageControllers.backedUpData();
11695 for (StorageControllerList::const_iterator
11696 it = mStorageControllers->begin();
11697 it != mStorageControllers->end();
11698 ++it)
11699 {
11700 if ( std::find(backedList->begin(), backedList->end(), *it)
11701 == backedList->end()
11702 )
11703 {
11704 (*it)->uninit();
11705 }
11706 }
11707
11708 /* restore the list */
11709 mStorageControllers.rollback();
11710 }
11711
11712 /* rollback any changes to devices after restoring the list */
11713 if (mData->flModifications & IsModified_Storage)
11714 {
11715 for (StorageControllerList::const_iterator
11716 it = mStorageControllers->begin();
11717 it != mStorageControllers->end();
11718 ++it)
11719 {
11720 (*it)->i_rollback();
11721 }
11722 }
11723 }
11724
11725 if (!mUSBControllers.isNull())
11726 {
11727 if (mUSBControllers.isBackedUp())
11728 {
11729 /* unitialize all new devices (absent in the backed up list). */
11730 USBControllerList *backedList = mUSBControllers.backedUpData();
11731 for (USBControllerList::const_iterator
11732 it = mUSBControllers->begin();
11733 it != mUSBControllers->end();
11734 ++it)
11735 {
11736 if ( std::find(backedList->begin(), backedList->end(), *it)
11737 == backedList->end()
11738 )
11739 {
11740 (*it)->uninit();
11741 }
11742 }
11743
11744 /* restore the list */
11745 mUSBControllers.rollback();
11746 }
11747
11748 /* rollback any changes to devices after restoring the list */
11749 if (mData->flModifications & IsModified_USB)
11750 {
11751 for (USBControllerList::const_iterator
11752 it = mUSBControllers->begin();
11753 it != mUSBControllers->end();
11754 ++it)
11755 {
11756 (*it)->i_rollback();
11757 }
11758 }
11759 }
11760
11761 mUserData.rollback();
11762
11763 mHWData.rollback();
11764
11765 if (mData->flModifications & IsModified_Storage)
11766 i_rollbackMedia();
11767
11768 if (mBIOSSettings)
11769 mBIOSSettings->i_rollback();
11770
11771 if (mTrustedPlatformModule)
11772 mTrustedPlatformModule->i_rollback();
11773
11774 if (mNvramStore)
11775 mNvramStore->i_rollback();
11776
11777 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11778 mRecordingSettings->i_rollback();
11779
11780 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11781 mGraphicsAdapter->i_rollback();
11782
11783 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11784 mVRDEServer->i_rollback();
11785
11786 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11787 mAudioAdapter->i_rollback();
11788
11789 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11790 mUSBDeviceFilters->i_rollback();
11791
11792 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11793 mBandwidthControl->i_rollback();
11794
11795 if (!mHWData.isNull())
11796 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11797 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11798 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11799 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11800
11801 if (mData->flModifications & IsModified_NetworkAdapters)
11802 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11803 if ( mNetworkAdapters[slot]
11804 && mNetworkAdapters[slot]->i_isModified())
11805 {
11806 mNetworkAdapters[slot]->i_rollback();
11807 networkAdapters[slot] = mNetworkAdapters[slot];
11808 }
11809
11810 if (mData->flModifications & IsModified_SerialPorts)
11811 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11812 if ( mSerialPorts[slot]
11813 && mSerialPorts[slot]->i_isModified())
11814 {
11815 mSerialPorts[slot]->i_rollback();
11816 serialPorts[slot] = mSerialPorts[slot];
11817 }
11818
11819 if (mData->flModifications & IsModified_ParallelPorts)
11820 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11821 if ( mParallelPorts[slot]
11822 && mParallelPorts[slot]->i_isModified())
11823 {
11824 mParallelPorts[slot]->i_rollback();
11825 parallelPorts[slot] = mParallelPorts[slot];
11826 }
11827
11828 if (aNotify)
11829 {
11830 /* inform the direct session about changes */
11831
11832 ComObjPtr<Machine> that = this;
11833 uint32_t flModifications = mData->flModifications;
11834 alock.release();
11835
11836 if (flModifications & IsModified_SharedFolders)
11837 that->i_onSharedFolderChange();
11838
11839 if (flModifications & IsModified_VRDEServer)
11840 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11841 if (flModifications & IsModified_USB)
11842 that->i_onUSBControllerChange();
11843
11844 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11845 if (networkAdapters[slot])
11846 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11847 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11848 if (serialPorts[slot])
11849 that->i_onSerialPortChange(serialPorts[slot]);
11850 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11851 if (parallelPorts[slot])
11852 that->i_onParallelPortChange(parallelPorts[slot]);
11853
11854 if (flModifications & IsModified_Storage)
11855 {
11856 for (StorageControllerList::const_iterator
11857 it = mStorageControllers->begin();
11858 it != mStorageControllers->end();
11859 ++it)
11860 {
11861 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11862 }
11863 }
11864
11865
11866#if 0
11867 if (flModifications & IsModified_BandwidthControl)
11868 that->onBandwidthControlChange();
11869#endif
11870 }
11871}
11872
11873/**
11874 * Commits all the changes to machine settings.
11875 *
11876 * Note that this operation is supposed to never fail.
11877 *
11878 * @note Locks this object and children for writing.
11879 */
11880void Machine::i_commit()
11881{
11882 AutoCaller autoCaller(this);
11883 AssertComRCReturnVoid(autoCaller.rc());
11884
11885 AutoCaller peerCaller(mPeer);
11886 AssertComRCReturnVoid(peerCaller.rc());
11887
11888 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11889
11890 /*
11891 * use safe commit to ensure Snapshot machines (that share mUserData)
11892 * will still refer to a valid memory location
11893 */
11894 mUserData.commitCopy();
11895
11896 mHWData.commit();
11897
11898 if (mMediumAttachments.isBackedUp())
11899 i_commitMedia(Global::IsOnline(mData->mMachineState));
11900
11901 mBIOSSettings->i_commit();
11902 mTrustedPlatformModule->i_commit();
11903 mNvramStore->i_commit();
11904 mRecordingSettings->i_commit();
11905 mGraphicsAdapter->i_commit();
11906 mVRDEServer->i_commit();
11907 mAudioAdapter->i_commit();
11908 mUSBDeviceFilters->i_commit();
11909 mBandwidthControl->i_commit();
11910
11911 /* Since mNetworkAdapters is a list which might have been changed (resized)
11912 * without using the Backupable<> template we need to handle the copying
11913 * of the list entries manually, including the creation of peers for the
11914 * new objects. */
11915 bool commitNetworkAdapters = false;
11916 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11917 if (mPeer)
11918 {
11919 /* commit everything, even the ones which will go away */
11920 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11921 mNetworkAdapters[slot]->i_commit();
11922 /* copy over the new entries, creating a peer and uninit the original */
11923 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11924 for (size_t slot = 0; slot < newSize; slot++)
11925 {
11926 /* look if this adapter has a peer device */
11927 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11928 if (!peer)
11929 {
11930 /* no peer means the adapter is a newly created one;
11931 * create a peer owning data this data share it with */
11932 peer.createObject();
11933 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11934 }
11935 mPeer->mNetworkAdapters[slot] = peer;
11936 }
11937 /* uninit any no longer needed network adapters */
11938 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11939 mNetworkAdapters[slot]->uninit();
11940 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11941 {
11942 if (mPeer->mNetworkAdapters[slot])
11943 mPeer->mNetworkAdapters[slot]->uninit();
11944 }
11945 /* Keep the original network adapter count until this point, so that
11946 * discarding a chipset type change will not lose settings. */
11947 mNetworkAdapters.resize(newSize);
11948 mPeer->mNetworkAdapters.resize(newSize);
11949 }
11950 else
11951 {
11952 /* we have no peer (our parent is the newly created machine);
11953 * just commit changes to the network adapters */
11954 commitNetworkAdapters = true;
11955 }
11956 if (commitNetworkAdapters)
11957 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11958 mNetworkAdapters[slot]->i_commit();
11959
11960 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11961 mSerialPorts[slot]->i_commit();
11962 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11963 mParallelPorts[slot]->i_commit();
11964
11965 bool commitStorageControllers = false;
11966
11967 if (mStorageControllers.isBackedUp())
11968 {
11969 mStorageControllers.commit();
11970
11971 if (mPeer)
11972 {
11973 /* Commit all changes to new controllers (this will reshare data with
11974 * peers for those who have peers) */
11975 StorageControllerList *newList = new StorageControllerList();
11976 for (StorageControllerList::const_iterator
11977 it = mStorageControllers->begin();
11978 it != mStorageControllers->end();
11979 ++it)
11980 {
11981 (*it)->i_commit();
11982
11983 /* look if this controller has a peer device */
11984 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11985 if (!peer)
11986 {
11987 /* no peer means the device is a newly created one;
11988 * create a peer owning data this device share it with */
11989 peer.createObject();
11990 peer->init(mPeer, *it, true /* aReshare */);
11991 }
11992 else
11993 {
11994 /* remove peer from the old list */
11995 mPeer->mStorageControllers->remove(peer);
11996 }
11997 /* and add it to the new list */
11998 newList->push_back(peer);
11999 }
12000
12001 /* uninit old peer's controllers that are left */
12002 for (StorageControllerList::const_iterator
12003 it = mPeer->mStorageControllers->begin();
12004 it != mPeer->mStorageControllers->end();
12005 ++it)
12006 {
12007 (*it)->uninit();
12008 }
12009
12010 /* attach new list of controllers to our peer */
12011 mPeer->mStorageControllers.attach(newList);
12012 }
12013 else
12014 {
12015 /* we have no peer (our parent is the newly created machine);
12016 * just commit changes to devices */
12017 commitStorageControllers = true;
12018 }
12019 }
12020 else
12021 {
12022 /* the list of controllers itself is not changed,
12023 * just commit changes to controllers themselves */
12024 commitStorageControllers = true;
12025 }
12026
12027 if (commitStorageControllers)
12028 {
12029 for (StorageControllerList::const_iterator
12030 it = mStorageControllers->begin();
12031 it != mStorageControllers->end();
12032 ++it)
12033 {
12034 (*it)->i_commit();
12035 }
12036 }
12037
12038 bool commitUSBControllers = false;
12039
12040 if (mUSBControllers.isBackedUp())
12041 {
12042 mUSBControllers.commit();
12043
12044 if (mPeer)
12045 {
12046 /* Commit all changes to new controllers (this will reshare data with
12047 * peers for those who have peers) */
12048 USBControllerList *newList = new USBControllerList();
12049 for (USBControllerList::const_iterator
12050 it = mUSBControllers->begin();
12051 it != mUSBControllers->end();
12052 ++it)
12053 {
12054 (*it)->i_commit();
12055
12056 /* look if this controller has a peer device */
12057 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12058 if (!peer)
12059 {
12060 /* no peer means the device is a newly created one;
12061 * create a peer owning data this device share it with */
12062 peer.createObject();
12063 peer->init(mPeer, *it, true /* aReshare */);
12064 }
12065 else
12066 {
12067 /* remove peer from the old list */
12068 mPeer->mUSBControllers->remove(peer);
12069 }
12070 /* and add it to the new list */
12071 newList->push_back(peer);
12072 }
12073
12074 /* uninit old peer's controllers that are left */
12075 for (USBControllerList::const_iterator
12076 it = mPeer->mUSBControllers->begin();
12077 it != mPeer->mUSBControllers->end();
12078 ++it)
12079 {
12080 (*it)->uninit();
12081 }
12082
12083 /* attach new list of controllers to our peer */
12084 mPeer->mUSBControllers.attach(newList);
12085 }
12086 else
12087 {
12088 /* we have no peer (our parent is the newly created machine);
12089 * just commit changes to devices */
12090 commitUSBControllers = true;
12091 }
12092 }
12093 else
12094 {
12095 /* the list of controllers itself is not changed,
12096 * just commit changes to controllers themselves */
12097 commitUSBControllers = true;
12098 }
12099
12100 if (commitUSBControllers)
12101 {
12102 for (USBControllerList::const_iterator
12103 it = mUSBControllers->begin();
12104 it != mUSBControllers->end();
12105 ++it)
12106 {
12107 (*it)->i_commit();
12108 }
12109 }
12110
12111 if (i_isSessionMachine())
12112 {
12113 /* attach new data to the primary machine and reshare it */
12114 mPeer->mUserData.attach(mUserData);
12115 mPeer->mHWData.attach(mHWData);
12116 /* mmMediumAttachments is reshared by fixupMedia */
12117 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12118 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12119 }
12120}
12121
12122/**
12123 * Copies all the hardware data from the given machine.
12124 *
12125 * Currently, only called when the VM is being restored from a snapshot. In
12126 * particular, this implies that the VM is not running during this method's
12127 * call.
12128 *
12129 * @note This method must be called from under this object's lock.
12130 *
12131 * @note This method doesn't call #i_commit(), so all data remains backed up and
12132 * unsaved.
12133 */
12134void Machine::i_copyFrom(Machine *aThat)
12135{
12136 AssertReturnVoid(!i_isSnapshotMachine());
12137 AssertReturnVoid(aThat->i_isSnapshotMachine());
12138
12139 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12140
12141 mHWData.assignCopy(aThat->mHWData);
12142
12143 // create copies of all shared folders (mHWData after attaching a copy
12144 // contains just references to original objects)
12145 for (HWData::SharedFolderList::iterator
12146 it = mHWData->mSharedFolders.begin();
12147 it != mHWData->mSharedFolders.end();
12148 ++it)
12149 {
12150 ComObjPtr<SharedFolder> folder;
12151 folder.createObject();
12152 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12153 AssertComRC(rc);
12154 *it = folder;
12155 }
12156
12157 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12158 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12159 mNvramStore->i_copyFrom(aThat->mNvramStore);
12160 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12161 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12162 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12163 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12164 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12165 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12166
12167 /* create private copies of all controllers */
12168 mStorageControllers.backup();
12169 mStorageControllers->clear();
12170 for (StorageControllerList::const_iterator
12171 it = aThat->mStorageControllers->begin();
12172 it != aThat->mStorageControllers->end();
12173 ++it)
12174 {
12175 ComObjPtr<StorageController> ctrl;
12176 ctrl.createObject();
12177 ctrl->initCopy(this, *it);
12178 mStorageControllers->push_back(ctrl);
12179 }
12180
12181 /* create private copies of all USB controllers */
12182 mUSBControllers.backup();
12183 mUSBControllers->clear();
12184 for (USBControllerList::const_iterator
12185 it = aThat->mUSBControllers->begin();
12186 it != aThat->mUSBControllers->end();
12187 ++it)
12188 {
12189 ComObjPtr<USBController> ctrl;
12190 ctrl.createObject();
12191 ctrl->initCopy(this, *it);
12192 mUSBControllers->push_back(ctrl);
12193 }
12194
12195 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12196 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12197 {
12198 if (mNetworkAdapters[slot].isNotNull())
12199 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12200 else
12201 {
12202 unconst(mNetworkAdapters[slot]).createObject();
12203 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12204 }
12205 }
12206 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12207 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12208 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12209 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12210}
12211
12212/**
12213 * Returns whether the given storage controller is hotplug capable.
12214 *
12215 * @returns true if the controller supports hotplugging
12216 * false otherwise.
12217 * @param enmCtrlType The controller type to check for.
12218 */
12219bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12220{
12221 ComPtr<ISystemProperties> systemProperties;
12222 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12223 if (FAILED(rc))
12224 return false;
12225
12226 BOOL aHotplugCapable = FALSE;
12227 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12228
12229 return RT_BOOL(aHotplugCapable);
12230}
12231
12232#ifdef VBOX_WITH_RESOURCE_USAGE_API
12233
12234void Machine::i_getDiskList(MediaList &list)
12235{
12236 for (MediumAttachmentList::const_iterator
12237 it = mMediumAttachments->begin();
12238 it != mMediumAttachments->end();
12239 ++it)
12240 {
12241 MediumAttachment *pAttach = *it;
12242 /* just in case */
12243 AssertContinue(pAttach);
12244
12245 AutoCaller localAutoCallerA(pAttach);
12246 if (FAILED(localAutoCallerA.rc())) continue;
12247
12248 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12249
12250 if (pAttach->i_getType() == DeviceType_HardDisk)
12251 list.push_back(pAttach->i_getMedium());
12252 }
12253}
12254
12255void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12256{
12257 AssertReturnVoid(isWriteLockOnCurrentThread());
12258 AssertPtrReturnVoid(aCollector);
12259
12260 pm::CollectorHAL *hal = aCollector->getHAL();
12261 /* Create sub metrics */
12262 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12263 "Percentage of processor time spent in user mode by the VM process.");
12264 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12265 "Percentage of processor time spent in kernel mode by the VM process.");
12266 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12267 "Size of resident portion of VM process in memory.");
12268 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12269 "Actual size of all VM disks combined.");
12270 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12271 "Network receive rate.");
12272 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12273 "Network transmit rate.");
12274 /* Create and register base metrics */
12275 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12276 cpuLoadUser, cpuLoadKernel);
12277 aCollector->registerBaseMetric(cpuLoad);
12278 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12279 ramUsageUsed);
12280 aCollector->registerBaseMetric(ramUsage);
12281 MediaList disks;
12282 i_getDiskList(disks);
12283 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12284 diskUsageUsed);
12285 aCollector->registerBaseMetric(diskUsage);
12286
12287 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12288 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12289 new pm::AggregateAvg()));
12290 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12291 new pm::AggregateMin()));
12292 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12293 new pm::AggregateMax()));
12294 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12295 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12296 new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12298 new pm::AggregateMin()));
12299 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12300 new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12303 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12304 new pm::AggregateAvg()));
12305 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12306 new pm::AggregateMin()));
12307 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12308 new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12311 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12312 new pm::AggregateAvg()));
12313 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12314 new pm::AggregateMin()));
12315 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12316 new pm::AggregateMax()));
12317
12318
12319 /* Guest metrics collector */
12320 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12321 aCollector->registerGuest(mCollectorGuest);
12322 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12323
12324 /* Create sub metrics */
12325 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12326 "Percentage of processor time spent in user mode as seen by the guest.");
12327 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12328 "Percentage of processor time spent in kernel mode as seen by the guest.");
12329 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12330 "Percentage of processor time spent idling as seen by the guest.");
12331
12332 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12333 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12334 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12335 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12336 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12337 pm::SubMetric *guestMemCache = new pm::SubMetric(
12338 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12339
12340 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12341 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12342
12343 /* Create and register base metrics */
12344 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12345 machineNetRx, machineNetTx);
12346 aCollector->registerBaseMetric(machineNetRate);
12347
12348 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12349 guestLoadUser, guestLoadKernel, guestLoadIdle);
12350 aCollector->registerBaseMetric(guestCpuLoad);
12351
12352 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12353 guestMemTotal, guestMemFree,
12354 guestMemBalloon, guestMemShared,
12355 guestMemCache, guestPagedTotal);
12356 aCollector->registerBaseMetric(guestCpuMem);
12357
12358 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12359 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12360 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12361 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12362
12363 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12364 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12365 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12366 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12367
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12369 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12371 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12372
12373 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12374 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12376 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12377
12378 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12379 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12381 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12382
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12387
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12392
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12397
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12400 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12401 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12402
12403 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12404 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12405 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12406 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12407
12408 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12409 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12410 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12411 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12412}
12413
12414void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12415{
12416 AssertReturnVoid(isWriteLockOnCurrentThread());
12417
12418 if (aCollector)
12419 {
12420 aCollector->unregisterMetricsFor(aMachine);
12421 aCollector->unregisterBaseMetricsFor(aMachine);
12422 }
12423}
12424
12425#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12426
12427
12428////////////////////////////////////////////////////////////////////////////////
12429
12430DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12431
12432HRESULT SessionMachine::FinalConstruct()
12433{
12434 LogFlowThisFunc(("\n"));
12435
12436 mClientToken = NULL;
12437
12438 return BaseFinalConstruct();
12439}
12440
12441void SessionMachine::FinalRelease()
12442{
12443 LogFlowThisFunc(("\n"));
12444
12445 Assert(!mClientToken);
12446 /* paranoia, should not hang around any more */
12447 if (mClientToken)
12448 {
12449 delete mClientToken;
12450 mClientToken = NULL;
12451 }
12452
12453 uninit(Uninit::Unexpected);
12454
12455 BaseFinalRelease();
12456}
12457
12458/**
12459 * @note Must be called only by Machine::LockMachine() from its own write lock.
12460 */
12461HRESULT SessionMachine::init(Machine *aMachine)
12462{
12463 LogFlowThisFuncEnter();
12464 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12465
12466 AssertReturn(aMachine, E_INVALIDARG);
12467
12468 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12469
12470 /* Enclose the state transition NotReady->InInit->Ready */
12471 AutoInitSpan autoInitSpan(this);
12472 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12473
12474 HRESULT rc = S_OK;
12475
12476 RT_ZERO(mAuthLibCtx);
12477
12478 /* create the machine client token */
12479 try
12480 {
12481 mClientToken = new ClientToken(aMachine, this);
12482 if (!mClientToken->isReady())
12483 {
12484 delete mClientToken;
12485 mClientToken = NULL;
12486 rc = E_FAIL;
12487 }
12488 }
12489 catch (std::bad_alloc &)
12490 {
12491 rc = E_OUTOFMEMORY;
12492 }
12493 if (FAILED(rc))
12494 return rc;
12495
12496 /* memorize the peer Machine */
12497 unconst(mPeer) = aMachine;
12498 /* share the parent pointer */
12499 unconst(mParent) = aMachine->mParent;
12500
12501 /* take the pointers to data to share */
12502 mData.share(aMachine->mData);
12503 mSSData.share(aMachine->mSSData);
12504
12505 mUserData.share(aMachine->mUserData);
12506 mHWData.share(aMachine->mHWData);
12507 mMediumAttachments.share(aMachine->mMediumAttachments);
12508
12509 mStorageControllers.allocate();
12510 for (StorageControllerList::const_iterator
12511 it = aMachine->mStorageControllers->begin();
12512 it != aMachine->mStorageControllers->end();
12513 ++it)
12514 {
12515 ComObjPtr<StorageController> ctl;
12516 ctl.createObject();
12517 ctl->init(this, *it);
12518 mStorageControllers->push_back(ctl);
12519 }
12520
12521 mUSBControllers.allocate();
12522 for (USBControllerList::const_iterator
12523 it = aMachine->mUSBControllers->begin();
12524 it != aMachine->mUSBControllers->end();
12525 ++it)
12526 {
12527 ComObjPtr<USBController> ctl;
12528 ctl.createObject();
12529 ctl->init(this, *it);
12530 mUSBControllers->push_back(ctl);
12531 }
12532
12533 unconst(mBIOSSettings).createObject();
12534 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12535
12536 unconst(mTrustedPlatformModule).createObject();
12537 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12538
12539 unconst(mNvramStore).createObject();
12540 mNvramStore->init(this, aMachine->mNvramStore);
12541
12542 unconst(mRecordingSettings).createObject();
12543 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12544 /* create another GraphicsAdapter object that will be mutable */
12545 unconst(mGraphicsAdapter).createObject();
12546 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12547 /* create another VRDEServer object that will be mutable */
12548 unconst(mVRDEServer).createObject();
12549 mVRDEServer->init(this, aMachine->mVRDEServer);
12550 /* create another audio adapter object that will be mutable */
12551 unconst(mAudioAdapter).createObject();
12552 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12553 /* create a list of serial ports that will be mutable */
12554 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12555 {
12556 unconst(mSerialPorts[slot]).createObject();
12557 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12558 }
12559 /* create a list of parallel ports that will be mutable */
12560 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12561 {
12562 unconst(mParallelPorts[slot]).createObject();
12563 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12564 }
12565
12566 /* create another USB device filters object that will be mutable */
12567 unconst(mUSBDeviceFilters).createObject();
12568 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12569
12570 /* create a list of network adapters that will be mutable */
12571 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12572 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12573 {
12574 unconst(mNetworkAdapters[slot]).createObject();
12575 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12576 }
12577
12578 /* create another bandwidth control object that will be mutable */
12579 unconst(mBandwidthControl).createObject();
12580 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12581
12582 /* default is to delete saved state on Saved -> PoweredOff transition */
12583 mRemoveSavedState = true;
12584
12585 /* Confirm a successful initialization when it's the case */
12586 autoInitSpan.setSucceeded();
12587
12588 miNATNetworksStarted = 0;
12589
12590 LogFlowThisFuncLeave();
12591 return rc;
12592}
12593
12594/**
12595 * Uninitializes this session object. If the reason is other than
12596 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12597 * or the client watcher code.
12598 *
12599 * @param aReason uninitialization reason
12600 *
12601 * @note Locks mParent + this object for writing.
12602 */
12603void SessionMachine::uninit(Uninit::Reason aReason)
12604{
12605 LogFlowThisFuncEnter();
12606 LogFlowThisFunc(("reason=%d\n", aReason));
12607
12608 /*
12609 * Strongly reference ourselves to prevent this object deletion after
12610 * mData->mSession.mMachine.setNull() below (which can release the last
12611 * reference and call the destructor). Important: this must be done before
12612 * accessing any members (and before AutoUninitSpan that does it as well).
12613 * This self reference will be released as the very last step on return.
12614 */
12615 ComObjPtr<SessionMachine> selfRef;
12616 if (aReason != Uninit::Unexpected)
12617 selfRef = this;
12618
12619 /* Enclose the state transition Ready->InUninit->NotReady */
12620 AutoUninitSpan autoUninitSpan(this);
12621 if (autoUninitSpan.uninitDone())
12622 {
12623 LogFlowThisFunc(("Already uninitialized\n"));
12624 LogFlowThisFuncLeave();
12625 return;
12626 }
12627
12628 if (autoUninitSpan.initFailed())
12629 {
12630 /* We've been called by init() because it's failed. It's not really
12631 * necessary (nor it's safe) to perform the regular uninit sequence
12632 * below, the following is enough.
12633 */
12634 LogFlowThisFunc(("Initialization failed.\n"));
12635 /* destroy the machine client token */
12636 if (mClientToken)
12637 {
12638 delete mClientToken;
12639 mClientToken = NULL;
12640 }
12641 uninitDataAndChildObjects();
12642 mData.free();
12643 unconst(mParent) = NULL;
12644 unconst(mPeer) = NULL;
12645 LogFlowThisFuncLeave();
12646 return;
12647 }
12648
12649 MachineState_T lastState;
12650 {
12651 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12652 lastState = mData->mMachineState;
12653 }
12654 NOREF(lastState);
12655
12656#ifdef VBOX_WITH_USB
12657 // release all captured USB devices, but do this before requesting the locks below
12658 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12659 {
12660 /* Console::captureUSBDevices() is called in the VM process only after
12661 * setting the machine state to Starting or Restoring.
12662 * Console::detachAllUSBDevices() will be called upon successful
12663 * termination. So, we need to release USB devices only if there was
12664 * an abnormal termination of a running VM.
12665 *
12666 * This is identical to SessionMachine::DetachAllUSBDevices except
12667 * for the aAbnormal argument. */
12668 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12669 AssertComRC(rc);
12670 NOREF(rc);
12671
12672 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12673 if (service)
12674 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12675 }
12676#endif /* VBOX_WITH_USB */
12677
12678 // we need to lock this object in uninit() because the lock is shared
12679 // with mPeer (as well as data we modify below). mParent lock is needed
12680 // by several calls to it.
12681 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12682
12683#ifdef VBOX_WITH_RESOURCE_USAGE_API
12684 /*
12685 * It is safe to call Machine::i_unregisterMetrics() here because
12686 * PerformanceCollector::samplerCallback no longer accesses guest methods
12687 * holding the lock.
12688 */
12689 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12690 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12691 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12692 if (mCollectorGuest)
12693 {
12694 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12695 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12696 mCollectorGuest = NULL;
12697 }
12698#endif
12699
12700 if (aReason == Uninit::Abnormal)
12701 {
12702 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12703
12704 /*
12705 * Move the VM to the 'Aborted' machine state unless we are restoring a
12706 * VM that was in the 'Saved' machine state. In that case, if the VM
12707 * fails before reaching either the 'Restoring' machine state or the
12708 * 'Running' machine state then we set the machine state to
12709 * 'AbortedSaved' in order to preserve the saved state file so that the
12710 * VM can be restored in the future.
12711 */
12712 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12713 i_setMachineState(MachineState_AbortedSaved);
12714 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12715 i_setMachineState(MachineState_Aborted);
12716 }
12717
12718 // any machine settings modified?
12719 if (mData->flModifications)
12720 {
12721 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12722 i_rollback(false /* aNotify */);
12723 }
12724
12725 mData->mSession.mPID = NIL_RTPROCESS;
12726
12727 if (aReason == Uninit::Unexpected)
12728 {
12729 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12730 * client watcher thread to update the set of machines that have open
12731 * sessions. */
12732 mParent->i_updateClientWatcher();
12733 }
12734
12735 /* uninitialize all remote controls */
12736 if (mData->mSession.mRemoteControls.size())
12737 {
12738 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12739 mData->mSession.mRemoteControls.size()));
12740
12741 /* Always restart a the beginning, since the iterator is invalidated
12742 * by using erase(). */
12743 for (Data::Session::RemoteControlList::iterator
12744 it = mData->mSession.mRemoteControls.begin();
12745 it != mData->mSession.mRemoteControls.end();
12746 it = mData->mSession.mRemoteControls.begin())
12747 {
12748 ComPtr<IInternalSessionControl> pControl = *it;
12749 mData->mSession.mRemoteControls.erase(it);
12750 multilock.release();
12751 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12752 HRESULT rc = pControl->Uninitialize();
12753 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12754 if (FAILED(rc))
12755 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12756 multilock.acquire();
12757 }
12758 mData->mSession.mRemoteControls.clear();
12759 }
12760
12761 /* Remove all references to the NAT network service. The service will stop
12762 * if all references (also from other VMs) are removed. */
12763 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12764 {
12765 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12766 {
12767 BOOL enabled;
12768 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12769 if ( FAILED(hrc)
12770 || !enabled)
12771 continue;
12772
12773 NetworkAttachmentType_T type;
12774 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12775 if ( SUCCEEDED(hrc)
12776 && type == NetworkAttachmentType_NATNetwork)
12777 {
12778 Bstr name;
12779 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12780 if (SUCCEEDED(hrc))
12781 {
12782 multilock.release();
12783 Utf8Str strName(name);
12784 LogRel(("VM '%s' stops using NAT network '%s'\n",
12785 mUserData->s.strName.c_str(), strName.c_str()));
12786 mParent->i_natNetworkRefDec(strName);
12787 multilock.acquire();
12788 }
12789 }
12790 }
12791 }
12792
12793 /*
12794 * An expected uninitialization can come only from #i_checkForDeath().
12795 * Otherwise it means that something's gone really wrong (for example,
12796 * the Session implementation has released the VirtualBox reference
12797 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12798 * etc). However, it's also possible, that the client releases the IPC
12799 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12800 * but the VirtualBox release event comes first to the server process.
12801 * This case is practically possible, so we should not assert on an
12802 * unexpected uninit, just log a warning.
12803 */
12804
12805 if (aReason == Uninit::Unexpected)
12806 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12807
12808 if (aReason != Uninit::Normal)
12809 {
12810 mData->mSession.mDirectControl.setNull();
12811 }
12812 else
12813 {
12814 /* this must be null here (see #OnSessionEnd()) */
12815 Assert(mData->mSession.mDirectControl.isNull());
12816 Assert(mData->mSession.mState == SessionState_Unlocking);
12817 Assert(!mData->mSession.mProgress.isNull());
12818 }
12819 if (mData->mSession.mProgress)
12820 {
12821 if (aReason == Uninit::Normal)
12822 mData->mSession.mProgress->i_notifyComplete(S_OK);
12823 else
12824 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12825 COM_IIDOF(ISession),
12826 getComponentName(),
12827 tr("The VM session was aborted"));
12828 mData->mSession.mProgress.setNull();
12829 }
12830
12831 if (mConsoleTaskData.mProgress)
12832 {
12833 Assert(aReason == Uninit::Abnormal);
12834 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12835 COM_IIDOF(ISession),
12836 getComponentName(),
12837 tr("The VM session was aborted"));
12838 mConsoleTaskData.mProgress.setNull();
12839 }
12840
12841 /* remove the association between the peer machine and this session machine */
12842 Assert( (SessionMachine*)mData->mSession.mMachine == this
12843 || aReason == Uninit::Unexpected);
12844
12845 /* reset the rest of session data */
12846 mData->mSession.mLockType = LockType_Null;
12847 mData->mSession.mMachine.setNull();
12848 mData->mSession.mState = SessionState_Unlocked;
12849 mData->mSession.mName.setNull();
12850
12851 /* destroy the machine client token before leaving the exclusive lock */
12852 if (mClientToken)
12853 {
12854 delete mClientToken;
12855 mClientToken = NULL;
12856 }
12857
12858 /* fire an event */
12859 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12860
12861 uninitDataAndChildObjects();
12862
12863 /* free the essential data structure last */
12864 mData.free();
12865
12866 /* release the exclusive lock before setting the below two to NULL */
12867 multilock.release();
12868
12869 unconst(mParent) = NULL;
12870 unconst(mPeer) = NULL;
12871
12872 AuthLibUnload(&mAuthLibCtx);
12873
12874 LogFlowThisFuncLeave();
12875}
12876
12877// util::Lockable interface
12878////////////////////////////////////////////////////////////////////////////////
12879
12880/**
12881 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12882 * with the primary Machine instance (mPeer).
12883 */
12884RWLockHandle *SessionMachine::lockHandle() const
12885{
12886 AssertReturn(mPeer != NULL, NULL);
12887 return mPeer->lockHandle();
12888}
12889
12890// IInternalMachineControl methods
12891////////////////////////////////////////////////////////////////////////////////
12892
12893/**
12894 * Passes collected guest statistics to performance collector object
12895 */
12896HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12897 ULONG aCpuKernel, ULONG aCpuIdle,
12898 ULONG aMemTotal, ULONG aMemFree,
12899 ULONG aMemBalloon, ULONG aMemShared,
12900 ULONG aMemCache, ULONG aPageTotal,
12901 ULONG aAllocVMM, ULONG aFreeVMM,
12902 ULONG aBalloonedVMM, ULONG aSharedVMM,
12903 ULONG aVmNetRx, ULONG aVmNetTx)
12904{
12905#ifdef VBOX_WITH_RESOURCE_USAGE_API
12906 if (mCollectorGuest)
12907 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12908 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12909 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12910 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12911
12912 return S_OK;
12913#else
12914 NOREF(aValidStats);
12915 NOREF(aCpuUser);
12916 NOREF(aCpuKernel);
12917 NOREF(aCpuIdle);
12918 NOREF(aMemTotal);
12919 NOREF(aMemFree);
12920 NOREF(aMemBalloon);
12921 NOREF(aMemShared);
12922 NOREF(aMemCache);
12923 NOREF(aPageTotal);
12924 NOREF(aAllocVMM);
12925 NOREF(aFreeVMM);
12926 NOREF(aBalloonedVMM);
12927 NOREF(aSharedVMM);
12928 NOREF(aVmNetRx);
12929 NOREF(aVmNetTx);
12930 return E_NOTIMPL;
12931#endif
12932}
12933
12934////////////////////////////////////////////////////////////////////////////////
12935//
12936// SessionMachine task records
12937//
12938////////////////////////////////////////////////////////////////////////////////
12939
12940/**
12941 * Task record for saving the machine state.
12942 */
12943class SessionMachine::SaveStateTask
12944 : public Machine::Task
12945{
12946public:
12947 SaveStateTask(SessionMachine *m,
12948 Progress *p,
12949 const Utf8Str &t,
12950 Reason_T enmReason,
12951 const Utf8Str &strStateFilePath)
12952 : Task(m, p, t),
12953 m_enmReason(enmReason),
12954 m_strStateFilePath(strStateFilePath)
12955 {}
12956
12957private:
12958 void handler()
12959 {
12960 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12961 }
12962
12963 Reason_T m_enmReason;
12964 Utf8Str m_strStateFilePath;
12965
12966 friend class SessionMachine;
12967};
12968
12969/**
12970 * Task thread implementation for SessionMachine::SaveState(), called from
12971 * SessionMachine::taskHandler().
12972 *
12973 * @note Locks this object for writing.
12974 *
12975 * @param task
12976 * @return
12977 */
12978void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12979{
12980 LogFlowThisFuncEnter();
12981
12982 AutoCaller autoCaller(this);
12983 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12984 if (FAILED(autoCaller.rc()))
12985 {
12986 /* we might have been uninitialized because the session was accidentally
12987 * closed by the client, so don't assert */
12988 HRESULT rc = setError(E_FAIL,
12989 tr("The session has been accidentally closed"));
12990 task.m_pProgress->i_notifyComplete(rc);
12991 LogFlowThisFuncLeave();
12992 return;
12993 }
12994
12995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12996
12997 HRESULT rc = S_OK;
12998
12999 try
13000 {
13001 ComPtr<IInternalSessionControl> directControl;
13002 if (mData->mSession.mLockType == LockType_VM)
13003 directControl = mData->mSession.mDirectControl;
13004 if (directControl.isNull())
13005 throw setError(VBOX_E_INVALID_VM_STATE,
13006 tr("Trying to save state without a running VM"));
13007 alock.release();
13008 BOOL fSuspendedBySave;
13009 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13010 Assert(!fSuspendedBySave);
13011 alock.acquire();
13012
13013 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13014 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13015 throw E_FAIL);
13016
13017 if (SUCCEEDED(rc))
13018 {
13019 mSSData->strStateFilePath = task.m_strStateFilePath;
13020
13021 /* save all VM settings */
13022 rc = i_saveSettings(NULL, alock);
13023 // no need to check whether VirtualBox.xml needs saving also since
13024 // we can't have a name change pending at this point
13025 }
13026 else
13027 {
13028 // On failure, set the state to the state we had at the beginning.
13029 i_setMachineState(task.m_machineStateBackup);
13030 i_updateMachineStateOnClient();
13031
13032 // Delete the saved state file (might have been already created).
13033 // No need to check whether this is shared with a snapshot here
13034 // because we certainly created a fresh saved state file here.
13035 RTFileDelete(task.m_strStateFilePath.c_str());
13036 }
13037 }
13038 catch (HRESULT aRC) { rc = aRC; }
13039
13040 task.m_pProgress->i_notifyComplete(rc);
13041
13042 LogFlowThisFuncLeave();
13043}
13044
13045/**
13046 * @note Locks this object for writing.
13047 */
13048HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13049{
13050 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13051}
13052
13053HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13054{
13055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13056
13057 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13058 if (FAILED(rc)) return rc;
13059
13060 if ( mData->mMachineState != MachineState_Running
13061 && mData->mMachineState != MachineState_Paused
13062 )
13063 return setError(VBOX_E_INVALID_VM_STATE,
13064 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13065 Global::stringifyMachineState(mData->mMachineState));
13066
13067 ComObjPtr<Progress> pProgress;
13068 pProgress.createObject();
13069 rc = pProgress->init(i_getVirtualBox(),
13070 static_cast<IMachine *>(this) /* aInitiator */,
13071 tr("Saving the execution state of the virtual machine"),
13072 FALSE /* aCancelable */);
13073 if (FAILED(rc))
13074 return rc;
13075
13076 Utf8Str strStateFilePath;
13077 i_composeSavedStateFilename(strStateFilePath);
13078
13079 /* create and start the task on a separate thread (note that it will not
13080 * start working until we release alock) */
13081 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13082 rc = pTask->createThread();
13083 if (FAILED(rc))
13084 return rc;
13085
13086 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13087 i_setMachineState(MachineState_Saving);
13088 i_updateMachineStateOnClient();
13089
13090 pProgress.queryInterfaceTo(aProgress.asOutParam());
13091
13092 return S_OK;
13093}
13094
13095/**
13096 * @note Locks this object for writing.
13097 */
13098HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13099{
13100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13101
13102 HRESULT rc = i_checkStateDependency(MutableStateDep);
13103 if (FAILED(rc)) return rc;
13104
13105 if ( mData->mMachineState != MachineState_PoweredOff
13106 && mData->mMachineState != MachineState_Teleported
13107 && mData->mMachineState != MachineState_Aborted
13108 )
13109 return setError(VBOX_E_INVALID_VM_STATE,
13110 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13111 Global::stringifyMachineState(mData->mMachineState));
13112
13113 com::Utf8Str stateFilePathFull;
13114 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13115 if (RT_FAILURE(vrc))
13116 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13117 tr("Invalid saved state file path '%s' (%Rrc)"),
13118 aSavedStateFile.c_str(),
13119 vrc);
13120
13121 mSSData->strStateFilePath = stateFilePathFull;
13122
13123 /* The below i_setMachineState() will detect the state transition and will
13124 * update the settings file */
13125
13126 return i_setMachineState(MachineState_Saved);
13127}
13128
13129/**
13130 * @note Locks this object for writing.
13131 */
13132HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13133{
13134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13135
13136 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13137 if (FAILED(rc)) return rc;
13138
13139 if ( mData->mMachineState != MachineState_Saved
13140 && mData->mMachineState != MachineState_AbortedSaved)
13141 return setError(VBOX_E_INVALID_VM_STATE,
13142 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13143 Global::stringifyMachineState(mData->mMachineState));
13144
13145 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13146
13147 /*
13148 * Saved -> PoweredOff transition will be detected in the SessionMachine
13149 * and properly handled.
13150 */
13151 rc = i_setMachineState(MachineState_PoweredOff);
13152 return rc;
13153}
13154
13155
13156/**
13157 * @note Locks the same as #i_setMachineState() does.
13158 */
13159HRESULT SessionMachine::updateState(MachineState_T aState)
13160{
13161 return i_setMachineState(aState);
13162}
13163
13164/**
13165 * @note Locks this object for writing.
13166 */
13167HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13168{
13169 IProgress *pProgress(aProgress);
13170
13171 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13172
13173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13174
13175 if (mData->mSession.mState != SessionState_Locked)
13176 return VBOX_E_INVALID_OBJECT_STATE;
13177
13178 if (!mData->mSession.mProgress.isNull())
13179 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13180
13181 /* If we didn't reference the NAT network service yet, add a reference to
13182 * force a start */
13183 if (miNATNetworksStarted < 1)
13184 {
13185 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13186 {
13187 BOOL enabled;
13188 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13189 if ( FAILED(hrc)
13190 || !enabled)
13191 continue;
13192
13193 NetworkAttachmentType_T type;
13194 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13195 if ( SUCCEEDED(hrc)
13196 && type == NetworkAttachmentType_NATNetwork)
13197 {
13198 Bstr name;
13199 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13200 if (SUCCEEDED(hrc))
13201 {
13202 Utf8Str strName(name);
13203 LogRel(("VM '%s' starts using NAT network '%s'\n",
13204 mUserData->s.strName.c_str(), strName.c_str()));
13205 mPeer->lockHandle()->unlockWrite();
13206 mParent->i_natNetworkRefInc(strName);
13207#ifdef RT_LOCK_STRICT
13208 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13209#else
13210 mPeer->lockHandle()->lockWrite();
13211#endif
13212 }
13213 }
13214 }
13215 miNATNetworksStarted++;
13216 }
13217
13218 LogFlowThisFunc(("returns S_OK.\n"));
13219 return S_OK;
13220}
13221
13222/**
13223 * @note Locks this object for writing.
13224 */
13225HRESULT SessionMachine::endPowerUp(LONG aResult)
13226{
13227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13228
13229 if (mData->mSession.mState != SessionState_Locked)
13230 return VBOX_E_INVALID_OBJECT_STATE;
13231
13232 /* Finalize the LaunchVMProcess progress object. */
13233 if (mData->mSession.mProgress)
13234 {
13235 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13236 mData->mSession.mProgress.setNull();
13237 }
13238
13239 if (SUCCEEDED((HRESULT)aResult))
13240 {
13241#ifdef VBOX_WITH_RESOURCE_USAGE_API
13242 /* The VM has been powered up successfully, so it makes sense
13243 * now to offer the performance metrics for a running machine
13244 * object. Doing it earlier wouldn't be safe. */
13245 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13246 mData->mSession.mPID);
13247#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13248 }
13249
13250 return S_OK;
13251}
13252
13253/**
13254 * @note Locks this object for writing.
13255 */
13256HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13257{
13258 LogFlowThisFuncEnter();
13259
13260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13261
13262 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13263 E_FAIL);
13264
13265 /* create a progress object to track operation completion */
13266 ComObjPtr<Progress> pProgress;
13267 pProgress.createObject();
13268 pProgress->init(i_getVirtualBox(),
13269 static_cast<IMachine *>(this) /* aInitiator */,
13270 tr("Stopping the virtual machine"),
13271 FALSE /* aCancelable */);
13272
13273 /* fill in the console task data */
13274 mConsoleTaskData.mLastState = mData->mMachineState;
13275 mConsoleTaskData.mProgress = pProgress;
13276
13277 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13278 i_setMachineState(MachineState_Stopping);
13279
13280 pProgress.queryInterfaceTo(aProgress.asOutParam());
13281
13282 return S_OK;
13283}
13284
13285/**
13286 * @note Locks this object for writing.
13287 */
13288HRESULT SessionMachine::endPoweringDown(LONG aResult,
13289 const com::Utf8Str &aErrMsg)
13290{
13291 HRESULT const hrcResult = (HRESULT)aResult;
13292 LogFlowThisFuncEnter();
13293
13294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13295
13296 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13297 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13298 && mConsoleTaskData.mLastState != MachineState_Null,
13299 E_FAIL);
13300
13301 /*
13302 * On failure, set the state to the state we had when BeginPoweringDown()
13303 * was called (this is expected by Console::PowerDown() and the associated
13304 * task). On success the VM process already changed the state to
13305 * MachineState_PoweredOff, so no need to do anything.
13306 */
13307 if (FAILED(hrcResult))
13308 i_setMachineState(mConsoleTaskData.mLastState);
13309
13310 /* notify the progress object about operation completion */
13311 Assert(mConsoleTaskData.mProgress);
13312 if (SUCCEEDED(hrcResult))
13313 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13314 else
13315 {
13316 if (aErrMsg.length())
13317 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13318 COM_IIDOF(ISession),
13319 getComponentName(),
13320 aErrMsg.c_str());
13321 else
13322 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13323 }
13324
13325 /* clear out the temporary saved state data */
13326 mConsoleTaskData.mLastState = MachineState_Null;
13327 mConsoleTaskData.mProgress.setNull();
13328
13329 LogFlowThisFuncLeave();
13330 return S_OK;
13331}
13332
13333
13334/**
13335 * Goes through the USB filters of the given machine to see if the given
13336 * device matches any filter or not.
13337 *
13338 * @note Locks the same as USBController::hasMatchingFilter() does.
13339 */
13340HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13341 BOOL *aMatched,
13342 ULONG *aMaskedInterfaces)
13343{
13344 LogFlowThisFunc(("\n"));
13345
13346#ifdef VBOX_WITH_USB
13347 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13348#else
13349 NOREF(aDevice);
13350 NOREF(aMaskedInterfaces);
13351 *aMatched = FALSE;
13352#endif
13353
13354 return S_OK;
13355}
13356
13357/**
13358 * @note Locks the same as Host::captureUSBDevice() does.
13359 */
13360HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13361{
13362 LogFlowThisFunc(("\n"));
13363
13364#ifdef VBOX_WITH_USB
13365 /* if captureDeviceForVM() fails, it must have set extended error info */
13366 clearError();
13367 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13368 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13369 return rc;
13370
13371 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13372 AssertReturn(service, E_FAIL);
13373 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13374#else
13375 RT_NOREF(aId, aCaptureFilename);
13376 return E_NOTIMPL;
13377#endif
13378}
13379
13380/**
13381 * @note Locks the same as Host::detachUSBDevice() does.
13382 */
13383HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13384 BOOL aDone)
13385{
13386 LogFlowThisFunc(("\n"));
13387
13388#ifdef VBOX_WITH_USB
13389 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13390 AssertReturn(service, E_FAIL);
13391 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13392#else
13393 NOREF(aId);
13394 NOREF(aDone);
13395 return E_NOTIMPL;
13396#endif
13397}
13398
13399/**
13400 * Inserts all machine filters to the USB proxy service and then calls
13401 * Host::autoCaptureUSBDevices().
13402 *
13403 * Called by Console from the VM process upon VM startup.
13404 *
13405 * @note Locks what called methods lock.
13406 */
13407HRESULT SessionMachine::autoCaptureUSBDevices()
13408{
13409 LogFlowThisFunc(("\n"));
13410
13411#ifdef VBOX_WITH_USB
13412 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13413 AssertComRC(rc);
13414 NOREF(rc);
13415
13416 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13417 AssertReturn(service, E_FAIL);
13418 return service->autoCaptureDevicesForVM(this);
13419#else
13420 return S_OK;
13421#endif
13422}
13423
13424/**
13425 * Removes all machine filters from the USB proxy service and then calls
13426 * Host::detachAllUSBDevices().
13427 *
13428 * Called by Console from the VM process upon normal VM termination or by
13429 * SessionMachine::uninit() upon abnormal VM termination (from under the
13430 * Machine/SessionMachine lock).
13431 *
13432 * @note Locks what called methods lock.
13433 */
13434HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13435{
13436 LogFlowThisFunc(("\n"));
13437
13438#ifdef VBOX_WITH_USB
13439 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13440 AssertComRC(rc);
13441 NOREF(rc);
13442
13443 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13444 AssertReturn(service, E_FAIL);
13445 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13446#else
13447 NOREF(aDone);
13448 return S_OK;
13449#endif
13450}
13451
13452/**
13453 * @note Locks this object for writing.
13454 */
13455HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13456 ComPtr<IProgress> &aProgress)
13457{
13458 LogFlowThisFuncEnter();
13459
13460 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13461 /*
13462 * We don't assert below because it might happen that a non-direct session
13463 * informs us it is closed right after we've been uninitialized -- it's ok.
13464 */
13465
13466 /* get IInternalSessionControl interface */
13467 ComPtr<IInternalSessionControl> control(aSession);
13468
13469 ComAssertRet(!control.isNull(), E_INVALIDARG);
13470
13471 /* Creating a Progress object requires the VirtualBox lock, and
13472 * thus locking it here is required by the lock order rules. */
13473 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13474
13475 if (control == mData->mSession.mDirectControl)
13476 {
13477 /* The direct session is being normally closed by the client process
13478 * ----------------------------------------------------------------- */
13479
13480 /* go to the closing state (essential for all open*Session() calls and
13481 * for #i_checkForDeath()) */
13482 Assert(mData->mSession.mState == SessionState_Locked);
13483 mData->mSession.mState = SessionState_Unlocking;
13484
13485 /* set direct control to NULL to release the remote instance */
13486 mData->mSession.mDirectControl.setNull();
13487 LogFlowThisFunc(("Direct control is set to NULL\n"));
13488
13489 if (mData->mSession.mProgress)
13490 {
13491 /* finalize the progress, someone might wait if a frontend
13492 * closes the session before powering on the VM. */
13493 mData->mSession.mProgress->notifyComplete(E_FAIL,
13494 COM_IIDOF(ISession),
13495 getComponentName(),
13496 tr("The VM session was closed before any attempt to power it on"));
13497 mData->mSession.mProgress.setNull();
13498 }
13499
13500 /* Create the progress object the client will use to wait until
13501 * #i_checkForDeath() is called to uninitialize this session object after
13502 * it releases the IPC semaphore.
13503 * Note! Because we're "reusing" mProgress here, this must be a proxy
13504 * object just like for LaunchVMProcess. */
13505 Assert(mData->mSession.mProgress.isNull());
13506 ComObjPtr<ProgressProxy> progress;
13507 progress.createObject();
13508 ComPtr<IUnknown> pPeer(mPeer);
13509 progress->init(mParent, pPeer,
13510 Bstr(tr("Closing session")).raw(),
13511 FALSE /* aCancelable */);
13512 progress.queryInterfaceTo(aProgress.asOutParam());
13513 mData->mSession.mProgress = progress;
13514 }
13515 else
13516 {
13517 /* the remote session is being normally closed */
13518 bool found = false;
13519 for (Data::Session::RemoteControlList::iterator
13520 it = mData->mSession.mRemoteControls.begin();
13521 it != mData->mSession.mRemoteControls.end();
13522 ++it)
13523 {
13524 if (control == *it)
13525 {
13526 found = true;
13527 // This MUST be erase(it), not remove(*it) as the latter
13528 // triggers a very nasty use after free due to the place where
13529 // the value "lives".
13530 mData->mSession.mRemoteControls.erase(it);
13531 break;
13532 }
13533 }
13534 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13535 E_INVALIDARG);
13536 }
13537
13538 /* signal the client watcher thread, because the client is going away */
13539 mParent->i_updateClientWatcher();
13540
13541 LogFlowThisFuncLeave();
13542 return S_OK;
13543}
13544
13545HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13546 std::vector<com::Utf8Str> &aValues,
13547 std::vector<LONG64> &aTimestamps,
13548 std::vector<com::Utf8Str> &aFlags)
13549{
13550 LogFlowThisFunc(("\n"));
13551
13552#ifdef VBOX_WITH_GUEST_PROPS
13553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13554
13555 size_t cEntries = mHWData->mGuestProperties.size();
13556 aNames.resize(cEntries);
13557 aValues.resize(cEntries);
13558 aTimestamps.resize(cEntries);
13559 aFlags.resize(cEntries);
13560
13561 size_t i = 0;
13562 for (HWData::GuestPropertyMap::const_iterator
13563 it = mHWData->mGuestProperties.begin();
13564 it != mHWData->mGuestProperties.end();
13565 ++it, ++i)
13566 {
13567 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13568 aNames[i] = it->first;
13569 aValues[i] = it->second.strValue;
13570 aTimestamps[i] = it->second.mTimestamp;
13571
13572 /* If it is NULL, keep it NULL. */
13573 if (it->second.mFlags)
13574 {
13575 GuestPropWriteFlags(it->second.mFlags, szFlags);
13576 aFlags[i] = szFlags;
13577 }
13578 else
13579 aFlags[i] = "";
13580
13581 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13582 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13583 }
13584 return S_OK;
13585#else
13586 ReturnComNotImplemented();
13587#endif
13588}
13589
13590HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13591 const com::Utf8Str &aValue,
13592 LONG64 aTimestamp,
13593 const com::Utf8Str &aFlags,
13594 BOOL fWasDeleted)
13595{
13596 LogFlowThisFunc(("\n"));
13597
13598#ifdef VBOX_WITH_GUEST_PROPS
13599 try
13600 {
13601 /*
13602 * Convert input up front.
13603 */
13604 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13605 if (aFlags.length())
13606 {
13607 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13608 AssertRCReturn(vrc, E_INVALIDARG);
13609 }
13610
13611 /*
13612 * Now grab the object lock, validate the state and do the update.
13613 */
13614
13615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13616
13617 if (!Global::IsOnline(mData->mMachineState))
13618 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13619
13620 i_setModified(IsModified_MachineData);
13621 mHWData.backup();
13622
13623 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13624 if (it != mHWData->mGuestProperties.end())
13625 {
13626 if (!fWasDeleted)
13627 {
13628 it->second.strValue = aValue;
13629 it->second.mTimestamp = aTimestamp;
13630 it->second.mFlags = fFlags;
13631 }
13632 else
13633 mHWData->mGuestProperties.erase(it);
13634
13635 mData->mGuestPropertiesModified = TRUE;
13636 }
13637 else if (!fWasDeleted)
13638 {
13639 HWData::GuestProperty prop;
13640 prop.strValue = aValue;
13641 prop.mTimestamp = aTimestamp;
13642 prop.mFlags = fFlags;
13643
13644 mHWData->mGuestProperties[aName] = prop;
13645 mData->mGuestPropertiesModified = TRUE;
13646 }
13647
13648 alock.release();
13649
13650 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13651 }
13652 catch (...)
13653 {
13654 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13655 }
13656 return S_OK;
13657#else
13658 ReturnComNotImplemented();
13659#endif
13660}
13661
13662
13663HRESULT SessionMachine::lockMedia()
13664{
13665 AutoMultiWriteLock2 alock(this->lockHandle(),
13666 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13667
13668 AssertReturn( mData->mMachineState == MachineState_Starting
13669 || mData->mMachineState == MachineState_Restoring
13670 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13671
13672 clearError();
13673 alock.release();
13674 return i_lockMedia();
13675}
13676
13677HRESULT SessionMachine::unlockMedia()
13678{
13679 HRESULT hrc = i_unlockMedia();
13680 return hrc;
13681}
13682
13683HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13684 ComPtr<IMediumAttachment> &aNewAttachment)
13685{
13686 // request the host lock first, since might be calling Host methods for getting host drives;
13687 // next, protect the media tree all the while we're in here, as well as our member variables
13688 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13689 this->lockHandle(),
13690 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13691
13692 IMediumAttachment *iAttach = aAttachment;
13693 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13694
13695 Utf8Str ctrlName;
13696 LONG lPort;
13697 LONG lDevice;
13698 bool fTempEject;
13699 {
13700 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13701
13702 /* Need to query the details first, as the IMediumAttachment reference
13703 * might be to the original settings, which we are going to change. */
13704 ctrlName = pAttach->i_getControllerName();
13705 lPort = pAttach->i_getPort();
13706 lDevice = pAttach->i_getDevice();
13707 fTempEject = pAttach->i_getTempEject();
13708 }
13709
13710 if (!fTempEject)
13711 {
13712 /* Remember previously mounted medium. The medium before taking the
13713 * backup is not necessarily the same thing. */
13714 ComObjPtr<Medium> oldmedium;
13715 oldmedium = pAttach->i_getMedium();
13716
13717 i_setModified(IsModified_Storage);
13718 mMediumAttachments.backup();
13719
13720 // The backup operation makes the pAttach reference point to the
13721 // old settings. Re-get the correct reference.
13722 pAttach = i_findAttachment(*mMediumAttachments.data(),
13723 ctrlName,
13724 lPort,
13725 lDevice);
13726
13727 {
13728 AutoCaller autoAttachCaller(this);
13729 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13730
13731 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13732 if (!oldmedium.isNull())
13733 oldmedium->i_removeBackReference(mData->mUuid);
13734
13735 pAttach->i_updateMedium(NULL);
13736 pAttach->i_updateEjected();
13737 }
13738
13739 i_setModified(IsModified_Storage);
13740 }
13741 else
13742 {
13743 {
13744 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13745 pAttach->i_updateEjected();
13746 }
13747 }
13748
13749 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13750
13751 return S_OK;
13752}
13753
13754HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13755 com::Utf8Str &aResult)
13756{
13757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13758
13759 HRESULT hr = S_OK;
13760
13761 if (!mAuthLibCtx.hAuthLibrary)
13762 {
13763 /* Load the external authentication library. */
13764 Bstr authLibrary;
13765 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13766
13767 Utf8Str filename = authLibrary;
13768
13769 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13770 if (RT_FAILURE(vrc))
13771 hr = setErrorBoth(E_FAIL, vrc,
13772 tr("Could not load the external authentication library '%s' (%Rrc)"),
13773 filename.c_str(), vrc);
13774 }
13775
13776 /* The auth library might need the machine lock. */
13777 alock.release();
13778
13779 if (FAILED(hr))
13780 return hr;
13781
13782 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13783 {
13784 enum VRDEAuthParams
13785 {
13786 parmUuid = 1,
13787 parmGuestJudgement,
13788 parmUser,
13789 parmPassword,
13790 parmDomain,
13791 parmClientId
13792 };
13793
13794 AuthResult result = AuthResultAccessDenied;
13795
13796 Guid uuid(aAuthParams[parmUuid]);
13797 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13798 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13799
13800 result = AuthLibAuthenticate(&mAuthLibCtx,
13801 uuid.raw(), guestJudgement,
13802 aAuthParams[parmUser].c_str(),
13803 aAuthParams[parmPassword].c_str(),
13804 aAuthParams[parmDomain].c_str(),
13805 u32ClientId);
13806
13807 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13808 size_t cbPassword = aAuthParams[parmPassword].length();
13809 if (cbPassword)
13810 {
13811 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13812 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13813 }
13814
13815 if (result == AuthResultAccessGranted)
13816 aResult = "granted";
13817 else
13818 aResult = "denied";
13819
13820 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13821 aAuthParams[parmUser].c_str(), aResult.c_str()));
13822 }
13823 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13824 {
13825 enum VRDEAuthDisconnectParams
13826 {
13827 parmUuid = 1,
13828 parmClientId
13829 };
13830
13831 Guid uuid(aAuthParams[parmUuid]);
13832 uint32_t u32ClientId = 0;
13833 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13834 }
13835 else
13836 {
13837 hr = E_INVALIDARG;
13838 }
13839
13840 return hr;
13841}
13842
13843// public methods only for internal purposes
13844/////////////////////////////////////////////////////////////////////////////
13845
13846#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13847/**
13848 * Called from the client watcher thread to check for expected or unexpected
13849 * death of the client process that has a direct session to this machine.
13850 *
13851 * On Win32 and on OS/2, this method is called only when we've got the
13852 * mutex (i.e. the client has either died or terminated normally) so it always
13853 * returns @c true (the client is terminated, the session machine is
13854 * uninitialized).
13855 *
13856 * On other platforms, the method returns @c true if the client process has
13857 * terminated normally or abnormally and the session machine was uninitialized,
13858 * and @c false if the client process is still alive.
13859 *
13860 * @note Locks this object for writing.
13861 */
13862bool SessionMachine::i_checkForDeath()
13863{
13864 Uninit::Reason reason;
13865 bool terminated = false;
13866
13867 /* Enclose autoCaller with a block because calling uninit() from under it
13868 * will deadlock. */
13869 {
13870 AutoCaller autoCaller(this);
13871 if (!autoCaller.isOk())
13872 {
13873 /* return true if not ready, to cause the client watcher to exclude
13874 * the corresponding session from watching */
13875 LogFlowThisFunc(("Already uninitialized!\n"));
13876 return true;
13877 }
13878
13879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13880
13881 /* Determine the reason of death: if the session state is Closing here,
13882 * everything is fine. Otherwise it means that the client did not call
13883 * OnSessionEnd() before it released the IPC semaphore. This may happen
13884 * either because the client process has abnormally terminated, or
13885 * because it simply forgot to call ISession::Close() before exiting. We
13886 * threat the latter also as an abnormal termination (see
13887 * Session::uninit() for details). */
13888 reason = mData->mSession.mState == SessionState_Unlocking ?
13889 Uninit::Normal :
13890 Uninit::Abnormal;
13891
13892 if (mClientToken)
13893 terminated = mClientToken->release();
13894 } /* AutoCaller block */
13895
13896 if (terminated)
13897 uninit(reason);
13898
13899 return terminated;
13900}
13901
13902void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13903{
13904 LogFlowThisFunc(("\n"));
13905
13906 strTokenId.setNull();
13907
13908 AutoCaller autoCaller(this);
13909 AssertComRCReturnVoid(autoCaller.rc());
13910
13911 Assert(mClientToken);
13912 if (mClientToken)
13913 mClientToken->getId(strTokenId);
13914}
13915#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13916IToken *SessionMachine::i_getToken()
13917{
13918 LogFlowThisFunc(("\n"));
13919
13920 AutoCaller autoCaller(this);
13921 AssertComRCReturn(autoCaller.rc(), NULL);
13922
13923 Assert(mClientToken);
13924 if (mClientToken)
13925 return mClientToken->getToken();
13926 else
13927 return NULL;
13928}
13929#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13930
13931Machine::ClientToken *SessionMachine::i_getClientToken()
13932{
13933 LogFlowThisFunc(("\n"));
13934
13935 AutoCaller autoCaller(this);
13936 AssertComRCReturn(autoCaller.rc(), NULL);
13937
13938 return mClientToken;
13939}
13940
13941
13942/**
13943 * @note Locks this object for reading.
13944 */
13945HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13951
13952 ComPtr<IInternalSessionControl> directControl;
13953 {
13954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13955 if (mData->mSession.mLockType == LockType_VM)
13956 directControl = mData->mSession.mDirectControl;
13957 }
13958
13959 /* ignore notifications sent after #OnSessionEnd() is called */
13960 if (!directControl)
13961 return S_OK;
13962
13963 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13964}
13965
13966/**
13967 * @note Locks this object for reading.
13968 */
13969HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13970 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13971 const Utf8Str &aGuestIp, LONG aGuestPort)
13972{
13973 LogFlowThisFunc(("\n"));
13974
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13977
13978 ComPtr<IInternalSessionControl> directControl;
13979 {
13980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13981 if (mData->mSession.mLockType == LockType_VM)
13982 directControl = mData->mSession.mDirectControl;
13983 }
13984
13985 /* ignore notifications sent after #OnSessionEnd() is called */
13986 if (!directControl)
13987 return S_OK;
13988 /*
13989 * instead acting like callback we ask IVirtualBox deliver corresponding event
13990 */
13991
13992 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13993 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13994 return S_OK;
13995}
13996
13997/**
13998 * @note Locks this object for reading.
13999 */
14000HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14001{
14002 LogFlowThisFunc(("\n"));
14003
14004 AutoCaller autoCaller(this);
14005 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14006
14007 ComPtr<IInternalSessionControl> directControl;
14008 {
14009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14010 if (mData->mSession.mLockType == LockType_VM)
14011 directControl = mData->mSession.mDirectControl;
14012 }
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017
14018 return directControl->OnAudioAdapterChange(audioAdapter);
14019}
14020
14021/**
14022 * @note Locks this object for reading.
14023 */
14024HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14025{
14026 LogFlowThisFunc(("\n"));
14027
14028 AutoCaller autoCaller(this);
14029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14030
14031 ComPtr<IInternalSessionControl> directControl;
14032 {
14033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14034 if (mData->mSession.mLockType == LockType_VM)
14035 directControl = mData->mSession.mDirectControl;
14036 }
14037
14038 /* ignore notifications sent after #OnSessionEnd() is called */
14039 if (!directControl)
14040 return S_OK;
14041
14042 return directControl->OnSerialPortChange(serialPort);
14043}
14044
14045/**
14046 * @note Locks this object for reading.
14047 */
14048HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14049{
14050 LogFlowThisFunc(("\n"));
14051
14052 AutoCaller autoCaller(this);
14053 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14054
14055 ComPtr<IInternalSessionControl> directControl;
14056 {
14057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14058 if (mData->mSession.mLockType == LockType_VM)
14059 directControl = mData->mSession.mDirectControl;
14060 }
14061
14062 /* ignore notifications sent after #OnSessionEnd() is called */
14063 if (!directControl)
14064 return S_OK;
14065
14066 return directControl->OnParallelPortChange(parallelPort);
14067}
14068
14069/**
14070 * @note Locks this object for reading.
14071 */
14072HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14073{
14074 LogFlowThisFunc(("\n"));
14075
14076 AutoCaller autoCaller(this);
14077 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14078
14079 ComPtr<IInternalSessionControl> directControl;
14080 {
14081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14082 if (mData->mSession.mLockType == LockType_VM)
14083 directControl = mData->mSession.mDirectControl;
14084 }
14085
14086 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14087
14088 /* ignore notifications sent after #OnSessionEnd() is called */
14089 if (!directControl)
14090 return S_OK;
14091
14092 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14093}
14094
14095/**
14096 * @note Locks this object for reading.
14097 */
14098HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14099{
14100 LogFlowThisFunc(("\n"));
14101
14102 AutoCaller autoCaller(this);
14103 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14104
14105 ComPtr<IInternalSessionControl> directControl;
14106 {
14107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14108 if (mData->mSession.mLockType == LockType_VM)
14109 directControl = mData->mSession.mDirectControl;
14110 }
14111
14112 mParent->i_onMediumChanged(aAttachment);
14113
14114 /* ignore notifications sent after #OnSessionEnd() is called */
14115 if (!directControl)
14116 return S_OK;
14117
14118 return directControl->OnMediumChange(aAttachment, aForce);
14119}
14120
14121HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14122{
14123 LogFlowThisFunc(("\n"));
14124
14125 AutoCaller autoCaller(this);
14126 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14127
14128 ComPtr<IInternalSessionControl> directControl;
14129 {
14130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14131 if (mData->mSession.mLockType == LockType_VM)
14132 directControl = mData->mSession.mDirectControl;
14133 }
14134
14135 /* ignore notifications sent after #OnSessionEnd() is called */
14136 if (!directControl)
14137 return S_OK;
14138
14139 return directControl->OnVMProcessPriorityChange(aPriority);
14140}
14141
14142/**
14143 * @note Locks this object for reading.
14144 */
14145HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14146{
14147 LogFlowThisFunc(("\n"));
14148
14149 AutoCaller autoCaller(this);
14150 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14151
14152 ComPtr<IInternalSessionControl> directControl;
14153 {
14154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14155 if (mData->mSession.mLockType == LockType_VM)
14156 directControl = mData->mSession.mDirectControl;
14157 }
14158
14159 /* ignore notifications sent after #OnSessionEnd() is called */
14160 if (!directControl)
14161 return S_OK;
14162
14163 return directControl->OnCPUChange(aCPU, aRemove);
14164}
14165
14166HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14167{
14168 LogFlowThisFunc(("\n"));
14169
14170 AutoCaller autoCaller(this);
14171 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14172
14173 ComPtr<IInternalSessionControl> directControl;
14174 {
14175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14176 if (mData->mSession.mLockType == LockType_VM)
14177 directControl = mData->mSession.mDirectControl;
14178 }
14179
14180 /* ignore notifications sent after #OnSessionEnd() is called */
14181 if (!directControl)
14182 return S_OK;
14183
14184 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14185}
14186
14187/**
14188 * @note Locks this object for reading.
14189 */
14190HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14191{
14192 LogFlowThisFunc(("\n"));
14193
14194 AutoCaller autoCaller(this);
14195 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14196
14197 ComPtr<IInternalSessionControl> directControl;
14198 {
14199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14200 if (mData->mSession.mLockType == LockType_VM)
14201 directControl = mData->mSession.mDirectControl;
14202 }
14203
14204 /* ignore notifications sent after #OnSessionEnd() is called */
14205 if (!directControl)
14206 return S_OK;
14207
14208 return directControl->OnVRDEServerChange(aRestart);
14209}
14210
14211/**
14212 * @note Locks this object for reading.
14213 */
14214HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14215{
14216 LogFlowThisFunc(("\n"));
14217
14218 AutoCaller autoCaller(this);
14219 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14220
14221 ComPtr<IInternalSessionControl> directControl;
14222 {
14223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14224 if (mData->mSession.mLockType == LockType_VM)
14225 directControl = mData->mSession.mDirectControl;
14226 }
14227
14228 /* ignore notifications sent after #OnSessionEnd() is called */
14229 if (!directControl)
14230 return S_OK;
14231
14232 return directControl->OnRecordingChange(aEnable);
14233}
14234
14235/**
14236 * @note Locks this object for reading.
14237 */
14238HRESULT SessionMachine::i_onUSBControllerChange()
14239{
14240 LogFlowThisFunc(("\n"));
14241
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14244
14245 ComPtr<IInternalSessionControl> directControl;
14246 {
14247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14248 if (mData->mSession.mLockType == LockType_VM)
14249 directControl = mData->mSession.mDirectControl;
14250 }
14251
14252 /* ignore notifications sent after #OnSessionEnd() is called */
14253 if (!directControl)
14254 return S_OK;
14255
14256 return directControl->OnUSBControllerChange();
14257}
14258
14259/**
14260 * @note Locks this object for reading.
14261 */
14262HRESULT SessionMachine::i_onSharedFolderChange()
14263{
14264 LogFlowThisFunc(("\n"));
14265
14266 AutoCaller autoCaller(this);
14267 AssertComRCReturnRC(autoCaller.rc());
14268
14269 ComPtr<IInternalSessionControl> directControl;
14270 {
14271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14272 if (mData->mSession.mLockType == LockType_VM)
14273 directControl = mData->mSession.mDirectControl;
14274 }
14275
14276 /* ignore notifications sent after #OnSessionEnd() is called */
14277 if (!directControl)
14278 return S_OK;
14279
14280 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14281}
14282
14283/**
14284 * @note Locks this object for reading.
14285 */
14286HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14287{
14288 LogFlowThisFunc(("\n"));
14289
14290 AutoCaller autoCaller(this);
14291 AssertComRCReturnRC(autoCaller.rc());
14292
14293 ComPtr<IInternalSessionControl> directControl;
14294 {
14295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14296 if (mData->mSession.mLockType == LockType_VM)
14297 directControl = mData->mSession.mDirectControl;
14298 }
14299
14300 /* ignore notifications sent after #OnSessionEnd() is called */
14301 if (!directControl)
14302 return S_OK;
14303
14304 return directControl->OnClipboardModeChange(aClipboardMode);
14305}
14306
14307/**
14308 * @note Locks this object for reading.
14309 */
14310HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14311{
14312 LogFlowThisFunc(("\n"));
14313
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturnRC(autoCaller.rc());
14316
14317 ComPtr<IInternalSessionControl> directControl;
14318 {
14319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14320 if (mData->mSession.mLockType == LockType_VM)
14321 directControl = mData->mSession.mDirectControl;
14322 }
14323
14324 /* ignore notifications sent after #OnSessionEnd() is called */
14325 if (!directControl)
14326 return S_OK;
14327
14328 return directControl->OnClipboardFileTransferModeChange(aEnable);
14329}
14330
14331/**
14332 * @note Locks this object for reading.
14333 */
14334HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14335{
14336 LogFlowThisFunc(("\n"));
14337
14338 AutoCaller autoCaller(this);
14339 AssertComRCReturnRC(autoCaller.rc());
14340
14341 ComPtr<IInternalSessionControl> directControl;
14342 {
14343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14344 if (mData->mSession.mLockType == LockType_VM)
14345 directControl = mData->mSession.mDirectControl;
14346 }
14347
14348 /* ignore notifications sent after #OnSessionEnd() is called */
14349 if (!directControl)
14350 return S_OK;
14351
14352 return directControl->OnDnDModeChange(aDnDMode);
14353}
14354
14355/**
14356 * @note Locks this object for reading.
14357 */
14358HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14359{
14360 LogFlowThisFunc(("\n"));
14361
14362 AutoCaller autoCaller(this);
14363 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14364
14365 ComPtr<IInternalSessionControl> directControl;
14366 {
14367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14368 if (mData->mSession.mLockType == LockType_VM)
14369 directControl = mData->mSession.mDirectControl;
14370 }
14371
14372 /* ignore notifications sent after #OnSessionEnd() is called */
14373 if (!directControl)
14374 return S_OK;
14375
14376 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14377}
14378
14379/**
14380 * @note Locks this object for reading.
14381 */
14382HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14383{
14384 LogFlowThisFunc(("\n"));
14385
14386 AutoCaller autoCaller(this);
14387 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14388
14389 ComPtr<IInternalSessionControl> directControl;
14390 {
14391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14392 if (mData->mSession.mLockType == LockType_VM)
14393 directControl = mData->mSession.mDirectControl;
14394 }
14395
14396 /* ignore notifications sent after #OnSessionEnd() is called */
14397 if (!directControl)
14398 return S_OK;
14399
14400 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14401}
14402
14403/**
14404 * Returns @c true if this machine's USB controller reports it has a matching
14405 * filter for the given USB device and @c false otherwise.
14406 *
14407 * @note locks this object for reading.
14408 */
14409bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14410{
14411 AutoCaller autoCaller(this);
14412 /* silently return if not ready -- this method may be called after the
14413 * direct machine session has been called */
14414 if (!autoCaller.isOk())
14415 return false;
14416
14417#ifdef VBOX_WITH_USB
14418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14419
14420 switch (mData->mMachineState)
14421 {
14422 case MachineState_Starting:
14423 case MachineState_Restoring:
14424 case MachineState_TeleportingIn:
14425 case MachineState_Paused:
14426 case MachineState_Running:
14427 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14428 * elsewhere... */
14429 alock.release();
14430 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14431 default: break;
14432 }
14433#else
14434 NOREF(aDevice);
14435 NOREF(aMaskedIfs);
14436#endif
14437 return false;
14438}
14439
14440/**
14441 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14442 */
14443HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14444 IVirtualBoxErrorInfo *aError,
14445 ULONG aMaskedIfs,
14446 const com::Utf8Str &aCaptureFilename)
14447{
14448 LogFlowThisFunc(("\n"));
14449
14450 AutoCaller autoCaller(this);
14451
14452 /* This notification may happen after the machine object has been
14453 * uninitialized (the session was closed), so don't assert. */
14454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14455
14456 ComPtr<IInternalSessionControl> directControl;
14457 {
14458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14459 if (mData->mSession.mLockType == LockType_VM)
14460 directControl = mData->mSession.mDirectControl;
14461 }
14462
14463 /* fail on notifications sent after #OnSessionEnd() is called, it is
14464 * expected by the caller */
14465 if (!directControl)
14466 return E_FAIL;
14467
14468 /* No locks should be held at this point. */
14469 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14470 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14471
14472 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14473}
14474
14475/**
14476 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14477 */
14478HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14479 IVirtualBoxErrorInfo *aError)
14480{
14481 LogFlowThisFunc(("\n"));
14482
14483 AutoCaller autoCaller(this);
14484
14485 /* This notification may happen after the machine object has been
14486 * uninitialized (the session was closed), so don't assert. */
14487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14488
14489 ComPtr<IInternalSessionControl> directControl;
14490 {
14491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14492 if (mData->mSession.mLockType == LockType_VM)
14493 directControl = mData->mSession.mDirectControl;
14494 }
14495
14496 /* fail on notifications sent after #OnSessionEnd() is called, it is
14497 * expected by the caller */
14498 if (!directControl)
14499 return E_FAIL;
14500
14501 /* No locks should be held at this point. */
14502 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14503 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14504
14505 return directControl->OnUSBDeviceDetach(aId, aError);
14506}
14507
14508// protected methods
14509/////////////////////////////////////////////////////////////////////////////
14510
14511/**
14512 * Deletes the given file if it is no longer in use by either the current machine state
14513 * (if the machine is "saved") or any of the machine's snapshots.
14514 *
14515 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14516 * but is different for each SnapshotMachine. When calling this, the order of calling this
14517 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14518 * is therefore critical. I know, it's all rather messy.
14519 *
14520 * @param strStateFile
14521 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14522 * the test for whether the saved state file is in use.
14523 */
14524void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14525 Snapshot *pSnapshotToIgnore)
14526{
14527 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14528 if ( (strStateFile.isNotEmpty())
14529 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14530 )
14531 // ... and it must also not be shared with other snapshots
14532 if ( !mData->mFirstSnapshot
14533 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14534 // this checks the SnapshotMachine's state file paths
14535 )
14536 RTFileDelete(strStateFile.c_str());
14537}
14538
14539/**
14540 * Locks the attached media.
14541 *
14542 * All attached hard disks are locked for writing and DVD/floppy are locked for
14543 * reading. Parents of attached hard disks (if any) are locked for reading.
14544 *
14545 * This method also performs accessibility check of all media it locks: if some
14546 * media is inaccessible, the method will return a failure and a bunch of
14547 * extended error info objects per each inaccessible medium.
14548 *
14549 * Note that this method is atomic: if it returns a success, all media are
14550 * locked as described above; on failure no media is locked at all (all
14551 * succeeded individual locks will be undone).
14552 *
14553 * The caller is responsible for doing the necessary state sanity checks.
14554 *
14555 * The locks made by this method must be undone by calling #unlockMedia() when
14556 * no more needed.
14557 */
14558HRESULT SessionMachine::i_lockMedia()
14559{
14560 AutoCaller autoCaller(this);
14561 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14562
14563 AutoMultiWriteLock2 alock(this->lockHandle(),
14564 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14565
14566 /* bail out if trying to lock things with already set up locking */
14567 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14568
14569 MultiResult mrc(S_OK);
14570
14571 /* Collect locking information for all medium objects attached to the VM. */
14572 for (MediumAttachmentList::const_iterator
14573 it = mMediumAttachments->begin();
14574 it != mMediumAttachments->end();
14575 ++it)
14576 {
14577 MediumAttachment *pAtt = *it;
14578 DeviceType_T devType = pAtt->i_getType();
14579 Medium *pMedium = pAtt->i_getMedium();
14580
14581 MediumLockList *pMediumLockList(new MediumLockList());
14582 // There can be attachments without a medium (floppy/dvd), and thus
14583 // it's impossible to create a medium lock list. It still makes sense
14584 // to have the empty medium lock list in the map in case a medium is
14585 // attached later.
14586 if (pMedium != NULL)
14587 {
14588 MediumType_T mediumType = pMedium->i_getType();
14589 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14590 || mediumType == MediumType_Shareable;
14591 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14592
14593 alock.release();
14594 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14595 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14596 false /* fMediumLockWriteAll */,
14597 NULL,
14598 *pMediumLockList);
14599 alock.acquire();
14600 if (FAILED(mrc))
14601 {
14602 delete pMediumLockList;
14603 mData->mSession.mLockedMedia.Clear();
14604 break;
14605 }
14606 }
14607
14608 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14609 if (FAILED(rc))
14610 {
14611 mData->mSession.mLockedMedia.Clear();
14612 mrc = setError(rc,
14613 tr("Collecting locking information for all attached media failed"));
14614 break;
14615 }
14616 }
14617
14618 if (SUCCEEDED(mrc))
14619 {
14620 /* Now lock all media. If this fails, nothing is locked. */
14621 alock.release();
14622 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14623 alock.acquire();
14624 if (FAILED(rc))
14625 {
14626 mrc = setError(rc,
14627 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14628 }
14629 }
14630
14631 return mrc;
14632}
14633
14634/**
14635 * Undoes the locks made by by #lockMedia().
14636 */
14637HRESULT SessionMachine::i_unlockMedia()
14638{
14639 AutoCaller autoCaller(this);
14640 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14641
14642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14643
14644 /* we may be holding important error info on the current thread;
14645 * preserve it */
14646 ErrorInfoKeeper eik;
14647
14648 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14649 AssertComRC(rc);
14650 return rc;
14651}
14652
14653/**
14654 * Helper to change the machine state (reimplementation).
14655 *
14656 * @note Locks this object for writing.
14657 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14658 * it can cause crashes in random places due to unexpectedly committing
14659 * the current settings. The caller is responsible for that. The call
14660 * to saveStateSettings is fine, because this method does not commit.
14661 */
14662HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14663{
14664 LogFlowThisFuncEnter();
14665
14666 AutoCaller autoCaller(this);
14667 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14668
14669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14670
14671 MachineState_T oldMachineState = mData->mMachineState;
14672
14673 AssertMsgReturn(oldMachineState != aMachineState,
14674 ("oldMachineState=%s, aMachineState=%s\n",
14675 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14676 E_FAIL);
14677
14678 HRESULT rc = S_OK;
14679
14680 int stsFlags = 0;
14681 bool deleteSavedState = false;
14682
14683 /* detect some state transitions */
14684
14685 if ( ( ( oldMachineState == MachineState_Saved
14686 || oldMachineState == MachineState_AbortedSaved
14687 )
14688 && aMachineState == MachineState_Restoring
14689 )
14690 || ( ( oldMachineState == MachineState_PoweredOff
14691 || oldMachineState == MachineState_Teleported
14692 || oldMachineState == MachineState_Aborted
14693 )
14694 && ( aMachineState == MachineState_TeleportingIn
14695 || aMachineState == MachineState_Starting
14696 )
14697 )
14698 )
14699 {
14700 /* The EMT thread is about to start */
14701
14702 /* Nothing to do here for now... */
14703
14704 /// @todo NEWMEDIA don't let mDVDDrive and other children
14705 /// change anything when in the Starting/Restoring state
14706 }
14707 else if ( ( oldMachineState == MachineState_Running
14708 || oldMachineState == MachineState_Paused
14709 || oldMachineState == MachineState_Teleporting
14710 || oldMachineState == MachineState_OnlineSnapshotting
14711 || oldMachineState == MachineState_LiveSnapshotting
14712 || oldMachineState == MachineState_Stuck
14713 || oldMachineState == MachineState_Starting
14714 || oldMachineState == MachineState_Stopping
14715 || oldMachineState == MachineState_Saving
14716 || oldMachineState == MachineState_Restoring
14717 || oldMachineState == MachineState_TeleportingPausedVM
14718 || oldMachineState == MachineState_TeleportingIn
14719 )
14720 && ( aMachineState == MachineState_PoweredOff
14721 || aMachineState == MachineState_Saved
14722 || aMachineState == MachineState_Teleported
14723 || aMachineState == MachineState_Aborted
14724 || aMachineState == MachineState_AbortedSaved
14725 )
14726 )
14727 {
14728 /* The EMT thread has just stopped, unlock attached media. Note that as
14729 * opposed to locking that is done from Console, we do unlocking here
14730 * because the VM process may have aborted before having a chance to
14731 * properly unlock all media it locked. */
14732
14733 unlockMedia();
14734 }
14735
14736 if (oldMachineState == MachineState_Restoring)
14737 {
14738 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14739 {
14740 /*
14741 * delete the saved state file once the machine has finished
14742 * restoring from it (note that Console sets the state from
14743 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14744 * to give the user an ability to fix an error and retry --
14745 * we keep the saved state file in this case)
14746 */
14747 deleteSavedState = true;
14748 }
14749 }
14750 else if ( oldMachineState == MachineState_Saved
14751 && ( aMachineState == MachineState_PoweredOff
14752 || aMachineState == MachineState_Teleported
14753 )
14754 )
14755 {
14756 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14757 deleteSavedState = true;
14758 mData->mCurrentStateModified = TRUE;
14759 stsFlags |= SaveSTS_CurStateModified;
14760 }
14761 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14762 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14763
14764 if ( aMachineState == MachineState_Starting
14765 || aMachineState == MachineState_Restoring
14766 || aMachineState == MachineState_TeleportingIn
14767 )
14768 {
14769 /* set the current state modified flag to indicate that the current
14770 * state is no more identical to the state in the
14771 * current snapshot */
14772 if (!mData->mCurrentSnapshot.isNull())
14773 {
14774 mData->mCurrentStateModified = TRUE;
14775 stsFlags |= SaveSTS_CurStateModified;
14776 }
14777 }
14778
14779 if (deleteSavedState)
14780 {
14781 if (mRemoveSavedState)
14782 {
14783 Assert(!mSSData->strStateFilePath.isEmpty());
14784
14785 // it is safe to delete the saved state file if ...
14786 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14787 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14788 // ... none of the snapshots share the saved state file
14789 )
14790 RTFileDelete(mSSData->strStateFilePath.c_str());
14791 }
14792
14793 mSSData->strStateFilePath.setNull();
14794 stsFlags |= SaveSTS_StateFilePath;
14795 }
14796
14797 /* redirect to the underlying peer machine */
14798 mPeer->i_setMachineState(aMachineState);
14799
14800 if ( oldMachineState != MachineState_RestoringSnapshot
14801 && ( aMachineState == MachineState_PoweredOff
14802 || aMachineState == MachineState_Teleported
14803 || aMachineState == MachineState_Aborted
14804 || aMachineState == MachineState_AbortedSaved
14805 || aMachineState == MachineState_Saved))
14806 {
14807 /* the machine has stopped execution
14808 * (or the saved state file was adopted) */
14809 stsFlags |= SaveSTS_StateTimeStamp;
14810 }
14811
14812 if ( ( oldMachineState == MachineState_PoweredOff
14813 || oldMachineState == MachineState_Aborted
14814 || oldMachineState == MachineState_Teleported
14815 )
14816 && aMachineState == MachineState_Saved)
14817 {
14818 /* the saved state file was adopted */
14819 Assert(!mSSData->strStateFilePath.isEmpty());
14820 stsFlags |= SaveSTS_StateFilePath;
14821 }
14822
14823#ifdef VBOX_WITH_GUEST_PROPS
14824 if ( aMachineState == MachineState_PoweredOff
14825 || aMachineState == MachineState_Aborted
14826 || aMachineState == MachineState_Teleported)
14827 {
14828 /* Make sure any transient guest properties get removed from the
14829 * property store on shutdown. */
14830 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14831
14832 /* remove it from the settings representation */
14833 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14834 for (settings::GuestPropertiesList::iterator
14835 it = llGuestProperties.begin();
14836 it != llGuestProperties.end();
14837 /*nothing*/)
14838 {
14839 const settings::GuestProperty &prop = *it;
14840 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14841 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14842 {
14843 it = llGuestProperties.erase(it);
14844 fNeedsSaving = true;
14845 }
14846 else
14847 {
14848 ++it;
14849 }
14850 }
14851
14852 /* Additionally remove it from the HWData representation. Required to
14853 * keep everything in sync, as this is what the API keeps using. */
14854 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14855 for (HWData::GuestPropertyMap::iterator
14856 it = llHWGuestProperties.begin();
14857 it != llHWGuestProperties.end();
14858 /*nothing*/)
14859 {
14860 uint32_t fFlags = it->second.mFlags;
14861 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14862 {
14863 /* iterator where we need to continue after the erase call
14864 * (C++03 is a fact still, and it doesn't return the iterator
14865 * which would allow continuing) */
14866 HWData::GuestPropertyMap::iterator it2 = it;
14867 ++it2;
14868 llHWGuestProperties.erase(it);
14869 it = it2;
14870 fNeedsSaving = true;
14871 }
14872 else
14873 {
14874 ++it;
14875 }
14876 }
14877
14878 if (fNeedsSaving)
14879 {
14880 mData->mCurrentStateModified = TRUE;
14881 stsFlags |= SaveSTS_CurStateModified;
14882 }
14883 }
14884#endif /* VBOX_WITH_GUEST_PROPS */
14885
14886 rc = i_saveStateSettings(stsFlags);
14887
14888 if ( ( oldMachineState != MachineState_PoweredOff
14889 && oldMachineState != MachineState_Aborted
14890 && oldMachineState != MachineState_Teleported
14891 )
14892 && ( aMachineState == MachineState_PoweredOff
14893 || aMachineState == MachineState_Aborted
14894 || aMachineState == MachineState_Teleported
14895 )
14896 )
14897 {
14898 /* we've been shut down for any reason */
14899 /* no special action so far */
14900 }
14901
14902 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
14903 LogFlowThisFuncLeave();
14904 return rc;
14905}
14906
14907/**
14908 * Sends the current machine state value to the VM process.
14909 *
14910 * @note Locks this object for reading, then calls a client process.
14911 */
14912HRESULT SessionMachine::i_updateMachineStateOnClient()
14913{
14914 AutoCaller autoCaller(this);
14915 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14916
14917 ComPtr<IInternalSessionControl> directControl;
14918 {
14919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14920 AssertReturn(!!mData, E_FAIL);
14921 if (mData->mSession.mLockType == LockType_VM)
14922 directControl = mData->mSession.mDirectControl;
14923
14924 /* directControl may be already set to NULL here in #OnSessionEnd()
14925 * called too early by the direct session process while there is still
14926 * some operation (like deleting the snapshot) in progress. The client
14927 * process in this case is waiting inside Session::close() for the
14928 * "end session" process object to complete, while #uninit() called by
14929 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14930 * operation to complete. For now, we accept this inconsistent behavior
14931 * and simply do nothing here. */
14932
14933 if (mData->mSession.mState == SessionState_Unlocking)
14934 return S_OK;
14935 }
14936
14937 /* ignore notifications sent after #OnSessionEnd() is called */
14938 if (!directControl)
14939 return S_OK;
14940
14941 return directControl->UpdateMachineState(mData->mMachineState);
14942}
14943
14944
14945/*static*/
14946HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14947{
14948 va_list args;
14949 va_start(args, pcszMsg);
14950 HRESULT rc = setErrorInternalV(aResultCode,
14951 getStaticClassIID(),
14952 getStaticComponentName(),
14953 pcszMsg, args,
14954 false /* aWarning */,
14955 true /* aLogIt */);
14956 va_end(args);
14957 return rc;
14958}
14959
14960
14961HRESULT Machine::updateState(MachineState_T aState)
14962{
14963 NOREF(aState);
14964 ReturnComNotImplemented();
14965}
14966
14967HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14968{
14969 NOREF(aProgress);
14970 ReturnComNotImplemented();
14971}
14972
14973HRESULT Machine::endPowerUp(LONG aResult)
14974{
14975 NOREF(aResult);
14976 ReturnComNotImplemented();
14977}
14978
14979HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14980{
14981 NOREF(aProgress);
14982 ReturnComNotImplemented();
14983}
14984
14985HRESULT Machine::endPoweringDown(LONG aResult,
14986 const com::Utf8Str &aErrMsg)
14987{
14988 NOREF(aResult);
14989 NOREF(aErrMsg);
14990 ReturnComNotImplemented();
14991}
14992
14993HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14994 BOOL *aMatched,
14995 ULONG *aMaskedInterfaces)
14996{
14997 NOREF(aDevice);
14998 NOREF(aMatched);
14999 NOREF(aMaskedInterfaces);
15000 ReturnComNotImplemented();
15001
15002}
15003
15004HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15005{
15006 NOREF(aId); NOREF(aCaptureFilename);
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15011 BOOL aDone)
15012{
15013 NOREF(aId);
15014 NOREF(aDone);
15015 ReturnComNotImplemented();
15016}
15017
15018HRESULT Machine::autoCaptureUSBDevices()
15019{
15020 ReturnComNotImplemented();
15021}
15022
15023HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15024{
15025 NOREF(aDone);
15026 ReturnComNotImplemented();
15027}
15028
15029HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15030 ComPtr<IProgress> &aProgress)
15031{
15032 NOREF(aSession);
15033 NOREF(aProgress);
15034 ReturnComNotImplemented();
15035}
15036
15037HRESULT Machine::finishOnlineMergeMedium()
15038{
15039 ReturnComNotImplemented();
15040}
15041
15042HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15043 std::vector<com::Utf8Str> &aValues,
15044 std::vector<LONG64> &aTimestamps,
15045 std::vector<com::Utf8Str> &aFlags)
15046{
15047 NOREF(aNames);
15048 NOREF(aValues);
15049 NOREF(aTimestamps);
15050 NOREF(aFlags);
15051 ReturnComNotImplemented();
15052}
15053
15054HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15055 const com::Utf8Str &aValue,
15056 LONG64 aTimestamp,
15057 const com::Utf8Str &aFlags,
15058 BOOL fWasDeleted)
15059{
15060 NOREF(aName);
15061 NOREF(aValue);
15062 NOREF(aTimestamp);
15063 NOREF(aFlags);
15064 NOREF(fWasDeleted);
15065 ReturnComNotImplemented();
15066}
15067
15068HRESULT Machine::lockMedia()
15069{
15070 ReturnComNotImplemented();
15071}
15072
15073HRESULT Machine::unlockMedia()
15074{
15075 ReturnComNotImplemented();
15076}
15077
15078HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15079 ComPtr<IMediumAttachment> &aNewAttachment)
15080{
15081 NOREF(aAttachment);
15082 NOREF(aNewAttachment);
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15087 ULONG aCpuUser,
15088 ULONG aCpuKernel,
15089 ULONG aCpuIdle,
15090 ULONG aMemTotal,
15091 ULONG aMemFree,
15092 ULONG aMemBalloon,
15093 ULONG aMemShared,
15094 ULONG aMemCache,
15095 ULONG aPagedTotal,
15096 ULONG aMemAllocTotal,
15097 ULONG aMemFreeTotal,
15098 ULONG aMemBalloonTotal,
15099 ULONG aMemSharedTotal,
15100 ULONG aVmNetRx,
15101 ULONG aVmNetTx)
15102{
15103 NOREF(aValidStats);
15104 NOREF(aCpuUser);
15105 NOREF(aCpuKernel);
15106 NOREF(aCpuIdle);
15107 NOREF(aMemTotal);
15108 NOREF(aMemFree);
15109 NOREF(aMemBalloon);
15110 NOREF(aMemShared);
15111 NOREF(aMemCache);
15112 NOREF(aPagedTotal);
15113 NOREF(aMemAllocTotal);
15114 NOREF(aMemFreeTotal);
15115 NOREF(aMemBalloonTotal);
15116 NOREF(aMemSharedTotal);
15117 NOREF(aVmNetRx);
15118 NOREF(aVmNetTx);
15119 ReturnComNotImplemented();
15120}
15121
15122HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15123 com::Utf8Str &aResult)
15124{
15125 NOREF(aAuthParams);
15126 NOREF(aResult);
15127 ReturnComNotImplemented();
15128}
15129
15130com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15131{
15132 com::Utf8Str strControllerName = "Unknown";
15133 switch (aBusType)
15134 {
15135 case StorageBus_IDE:
15136 {
15137 strControllerName = "IDE";
15138 break;
15139 }
15140 case StorageBus_SATA:
15141 {
15142 strControllerName = "SATA";
15143 break;
15144 }
15145 case StorageBus_SCSI:
15146 {
15147 strControllerName = "SCSI";
15148 break;
15149 }
15150 case StorageBus_Floppy:
15151 {
15152 strControllerName = "Floppy";
15153 break;
15154 }
15155 case StorageBus_SAS:
15156 {
15157 strControllerName = "SAS";
15158 break;
15159 }
15160 case StorageBus_USB:
15161 {
15162 strControllerName = "USB";
15163 break;
15164 }
15165 default:
15166 break;
15167 }
15168 return strControllerName;
15169}
15170
15171HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15172{
15173 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15174
15175 AutoCaller autoCaller(this);
15176 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15177
15178 HRESULT rc = S_OK;
15179
15180 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15181 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15182 rc = getUSBDeviceFilters(usbDeviceFilters);
15183 if (FAILED(rc)) return rc;
15184
15185 NOREF(aFlags);
15186 com::Utf8Str osTypeId;
15187 ComObjPtr<GuestOSType> osType = NULL;
15188
15189 /* Get the guest os type as a string from the VB. */
15190 rc = getOSTypeId(osTypeId);
15191 if (FAILED(rc)) return rc;
15192
15193 /* Get the os type obj that coresponds, can be used to get
15194 * the defaults for this guest OS. */
15195 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15196 if (FAILED(rc)) return rc;
15197
15198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15199
15200 /* Let the OS type select 64-bit ness. */
15201 mHWData->mLongMode = osType->i_is64Bit()
15202 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15203
15204 /* Let the OS type enable the X2APIC */
15205 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15206
15207 /* This one covers IOAPICEnabled. */
15208 mBIOSSettings->i_applyDefaults(osType);
15209
15210 /* Initialize default record settings. */
15211 mRecordingSettings->i_applyDefaults();
15212
15213 /* Initialize default BIOS settings here */
15214 /* Hardware virtualization must be ON by default */
15215 mHWData->mAPIC = true;
15216 mHWData->mHWVirtExEnabled = true;
15217
15218 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15219 if (FAILED(rc)) return rc;
15220
15221 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15222 if (FAILED(rc)) return rc;
15223
15224 /* Graphics stuff. */
15225 GraphicsControllerType_T graphicsController;
15226 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15227 if (FAILED(rc)) return rc;
15228
15229 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15230 if (FAILED(rc)) return rc;
15231
15232 ULONG vramSize;
15233 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15237 if (FAILED(rc)) return rc;
15238
15239 BOOL fAccelerate2DVideoEnabled;
15240 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15241 if (FAILED(rc)) return rc;
15242
15243 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15244 if (FAILED(rc)) return rc;
15245
15246 BOOL fAccelerate3DEnabled;
15247 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15248 if (FAILED(rc)) return rc;
15249
15250 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15251 if (FAILED(rc)) return rc;
15252
15253 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15254 if (FAILED(rc)) return rc;
15255
15256 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15257 if (FAILED(rc)) return rc;
15258
15259 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15260 if (FAILED(rc)) return rc;
15261
15262 BOOL mRTCUseUTC;
15263 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15264 if (FAILED(rc)) return rc;
15265
15266 setRTCUseUTC(mRTCUseUTC);
15267 if (FAILED(rc)) return rc;
15268
15269 /* the setter does more than just the assignment, so use it */
15270 ChipsetType_T enmChipsetType;
15271 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = COMSETTER(ChipsetType)(enmChipsetType);
15275 if (FAILED(rc)) return rc;
15276
15277 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15278 if (FAILED(rc)) return rc;
15279
15280 /* Apply IOMMU defaults. */
15281 IommuType_T enmIommuType;
15282 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15283 if (FAILED(rc)) return rc;
15284
15285 rc = COMSETTER(IommuType)(enmIommuType);
15286 if (FAILED(rc)) return rc;
15287
15288 /* Apply network adapters defaults */
15289 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15290 mNetworkAdapters[slot]->i_applyDefaults(osType);
15291
15292 /* Apply serial port defaults */
15293 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15294 mSerialPorts[slot]->i_applyDefaults(osType);
15295
15296 /* Apply parallel port defaults - not OS dependent*/
15297 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15298 mParallelPorts[slot]->i_applyDefaults();
15299
15300 /* This one covers the TPM type. */
15301 mTrustedPlatformModule->i_applyDefaults(osType);
15302
15303 /* This one covers secure boot. */
15304 rc = mNvramStore->i_applyDefaults(osType);
15305 if (FAILED(rc)) return rc;
15306
15307 /* Audio stuff. */
15308 AudioControllerType_T audioController;
15309 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15310 if (FAILED(rc)) return rc;
15311
15312 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15313 if (FAILED(rc)) return rc;
15314
15315 AudioCodecType_T audioCodec;
15316 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15317 if (FAILED(rc)) return rc;
15318
15319 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15320 if (FAILED(rc)) return rc;
15321
15322 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15323 if (FAILED(rc)) return rc;
15324
15325 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15326 if (FAILED(rc)) return rc;
15327
15328 /* Storage Controllers */
15329 StorageControllerType_T hdStorageControllerType;
15330 StorageBus_T hdStorageBusType;
15331 StorageControllerType_T dvdStorageControllerType;
15332 StorageBus_T dvdStorageBusType;
15333 BOOL recommendedFloppy;
15334 ComPtr<IStorageController> floppyController;
15335 ComPtr<IStorageController> hdController;
15336 ComPtr<IStorageController> dvdController;
15337 Utf8Str strFloppyName, strDVDName, strHDName;
15338
15339 /* GUI auto generates controller names using bus type. Do the same*/
15340 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15341
15342 /* Floppy recommended? add one. */
15343 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15344 if (FAILED(rc)) return rc;
15345 if (recommendedFloppy)
15346 {
15347 rc = addStorageController(strFloppyName,
15348 StorageBus_Floppy,
15349 floppyController);
15350 if (FAILED(rc)) return rc;
15351 }
15352
15353 /* Setup one DVD storage controller. */
15354 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15355 if (FAILED(rc)) return rc;
15356
15357 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15358 if (FAILED(rc)) return rc;
15359
15360 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15361
15362 rc = addStorageController(strDVDName,
15363 dvdStorageBusType,
15364 dvdController);
15365 if (FAILED(rc)) return rc;
15366
15367 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15368 if (FAILED(rc)) return rc;
15369
15370 /* Setup one HDD storage controller. */
15371 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15372 if (FAILED(rc)) return rc;
15373
15374 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15375 if (FAILED(rc)) return rc;
15376
15377 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15378
15379 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15380 {
15381 rc = addStorageController(strHDName,
15382 hdStorageBusType,
15383 hdController);
15384 if (FAILED(rc)) return rc;
15385
15386 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15387 if (FAILED(rc)) return rc;
15388 }
15389 else
15390 {
15391 /* The HD controller is the same as DVD: */
15392 hdController = dvdController;
15393 }
15394
15395 /* Limit the AHCI port count if it's used because windows has trouble with
15396 * too many ports and other guest (OS X in particular) may take extra long
15397 * boot: */
15398
15399 // pParent = static_cast<Medium*>(aP)
15400 IStorageController *temp = hdController;
15401 ComObjPtr<StorageController> storageController;
15402 storageController = static_cast<StorageController *>(temp);
15403
15404 // tempHDController = aHDController;
15405 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15406 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15407 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15408 storageController->COMSETTER(PortCount)(1);
15409
15410 /* USB stuff */
15411
15412 bool ohciEnabled = false;
15413
15414 ComPtr<IUSBController> usbController;
15415 BOOL recommendedUSB3;
15416 BOOL recommendedUSB;
15417 BOOL usbProxyAvailable;
15418
15419 getUSBProxyAvailable(&usbProxyAvailable);
15420 if (FAILED(rc)) return rc;
15421
15422 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15423 if (FAILED(rc)) return rc;
15424 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15425 if (FAILED(rc)) return rc;
15426
15427 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15428 {
15429#ifdef VBOX_WITH_EXTPACK
15430 /* USB 3.0 is only available if the proper ExtPack is installed. */
15431 ExtPackManager *aManager = mParent->i_getExtPackManager();
15432 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15433 {
15434 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15435 if (FAILED(rc)) return rc;
15436
15437 /* xHci includes OHCI */
15438 ohciEnabled = true;
15439 }
15440#endif
15441 }
15442 if ( !ohciEnabled
15443 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15444 {
15445 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15446 if (FAILED(rc)) return rc;
15447 ohciEnabled = true;
15448
15449#ifdef VBOX_WITH_EXTPACK
15450 /* USB 2.0 is only available if the proper ExtPack is installed.
15451 * Note. Configuring EHCI here and providing messages about
15452 * the missing extpack isn't exactly clean, but it is a
15453 * necessary evil to patch over legacy compatability issues
15454 * introduced by the new distribution model. */
15455 ExtPackManager *manager = mParent->i_getExtPackManager();
15456 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15457 {
15458 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15459 if (FAILED(rc)) return rc;
15460 }
15461#endif
15462 }
15463
15464 /* Set recommended human interface device types: */
15465 BOOL recommendedUSBHID;
15466 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15467 if (FAILED(rc)) return rc;
15468
15469 if (recommendedUSBHID)
15470 {
15471 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15472 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15473 if (!ohciEnabled && !usbDeviceFilters.isNull())
15474 {
15475 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15476 if (FAILED(rc)) return rc;
15477 }
15478 }
15479
15480 BOOL recommendedUSBTablet;
15481 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15482 if (FAILED(rc)) return rc;
15483
15484 if (recommendedUSBTablet)
15485 {
15486 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15487 if (!ohciEnabled && !usbDeviceFilters.isNull())
15488 {
15489 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15490 if (FAILED(rc)) return rc;
15491 }
15492 }
15493
15494 /* Enable the VMMDev testing feature for bootsector VMs: */
15495 if (osTypeId == "VBoxBS_64")
15496 {
15497 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15498 if (FAILED(rc))
15499 return rc;
15500 }
15501
15502 return S_OK;
15503}
15504
15505/* This isn't handled entirely by the wrapper generator yet. */
15506#ifdef VBOX_WITH_XPCOM
15507NS_DECL_CLASSINFO(SessionMachine)
15508NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15509
15510NS_DECL_CLASSINFO(SnapshotMachine)
15511NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15512#endif
Note: See TracBrowser for help on using the repository browser.

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