VirtualBox

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

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

Main/MachineImpl: Always return a NVRAM file path as the TPM might be active even when EFI is not selected

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 541.6 KB
Line 
1/* $Id: MachineImpl.cpp 93639 2022-02-07 13:49:57Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63#include "StringifyEnums.h"
64
65#include <iprt/asm.h>
66#include <iprt/path.h>
67#include <iprt/dir.h>
68#include <iprt/env.h>
69#include <iprt/lockvalidator.h>
70#include <iprt/process.h>
71#include <iprt/cpp/utils.h>
72#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
73#include <iprt/sha.h>
74#include <iprt/string.h>
75#include <iprt/ctype.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/VMMDev.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#ifdef VBOX_WITH_SHARED_CLIPBOARD
92# include <VBox/HostServices/VBoxClipboardSvc.h>
93#endif
94
95#include "VBox/com/MultiResult.h"
96
97#include <algorithm>
98
99#ifdef VBOX_WITH_DTRACE_R3_MAIN
100# include "dtrace/VBoxAPI.h"
101#endif
102
103#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
104# define HOSTSUFF_EXE ".exe"
105#else /* !RT_OS_WINDOWS */
106# define HOSTSUFF_EXE ""
107#endif /* !RT_OS_WINDOWS */
108
109// defines / prototypes
110/////////////////////////////////////////////////////////////////////////////
111
112/////////////////////////////////////////////////////////////////////////////
113// Machine::Data structure
114/////////////////////////////////////////////////////////////////////////////
115
116Machine::Data::Data()
117{
118 mRegistered = FALSE;
119 pMachineConfigFile = NULL;
120 /* Contains hints on what has changed when the user is using the VM (config
121 * changes, running the VM, ...). This is used to decide if a config needs
122 * to be written to disk. */
123 flModifications = 0;
124 /* VM modification usually also trigger setting the current state to
125 * "Modified". Although this is not always the case. An e.g. is the VM
126 * initialization phase or when snapshot related data is changed. The
127 * actually behavior is controlled by the following flag. */
128 m_fAllowStateModification = false;
129 mAccessible = FALSE;
130 /* mUuid is initialized in Machine::init() */
131
132 mMachineState = MachineState_PoweredOff;
133 RTTimeNow(&mLastStateChange);
134
135 mMachineStateDeps = 0;
136 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
137 mMachineStateChangePending = 0;
138
139 mCurrentStateModified = TRUE;
140 mGuestPropertiesModified = FALSE;
141
142 mSession.mPID = NIL_RTPROCESS;
143 mSession.mLockType = LockType_Null;
144 mSession.mState = SessionState_Unlocked;
145}
146
147Machine::Data::~Data()
148{
149 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
150 {
151 RTSemEventMultiDestroy(mMachineStateDepsSem);
152 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
153 }
154 if (pMachineConfigFile)
155 {
156 delete pMachineConfigFile;
157 pMachineConfigFile = NULL;
158 }
159}
160
161/////////////////////////////////////////////////////////////////////////////
162// Machine::HWData structure
163/////////////////////////////////////////////////////////////////////////////
164
165Machine::HWData::HWData()
166{
167 /* default values for a newly created machine */
168 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
169 mMemorySize = 128;
170 mCPUCount = 1;
171 mCPUHotPlugEnabled = false;
172 mMemoryBalloonSize = 0;
173 mPageFusionEnabled = false;
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
177 mHWVirtExVPIDEnabled = true;
178 mHWVirtExUXEnabled = true;
179 mHWVirtExForceEnabled = false;
180 mHWVirtExUseNativeApi = false;
181 mHWVirtExVirtVmsaveVmload = true;
182#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
183 mPAEEnabled = true;
184#else
185 mPAEEnabled = false;
186#endif
187 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
188 mTripleFaultReset = false;
189 mAPIC = true;
190 mX2APIC = false;
191 mIBPBOnVMExit = false;
192 mIBPBOnVMEntry = false;
193 mSpecCtrl = false;
194 mSpecCtrlByHost = false;
195 mL1DFlushOnSched = true;
196 mL1DFlushOnVMEntry = false;
197 mMDSClearOnSched = true;
198 mMDSClearOnVMEntry = false;
199 mNestedHWVirt = false;
200 mHPETEnabled = false;
201 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
202 mCpuIdPortabilityLevel = 0;
203 mCpuProfile = "host";
204
205 /* default boot order: floppy - DVD - HDD */
206 mBootOrder[0] = DeviceType_Floppy;
207 mBootOrder[1] = DeviceType_DVD;
208 mBootOrder[2] = DeviceType_HardDisk;
209 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
210 mBootOrder[i] = DeviceType_Null;
211
212 mClipboardMode = ClipboardMode_Disabled;
213 mClipboardFileTransfersEnabled = FALSE;
214
215 mDnDMode = DnDMode_Disabled;
216
217 mFirmwareType = FirmwareType_BIOS;
218 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
219 mPointingHIDType = PointingHIDType_PS2Mouse;
220 mChipsetType = ChipsetType_PIIX3;
221 mIommuType = IommuType_None;
222 mParavirtProvider = ParavirtProvider_Default;
223 mEmulatedUSBCardReaderEnabled = FALSE;
224
225 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
226 mCPUAttached[i] = false;
227
228 mIOCacheEnabled = true;
229 mIOCacheSize = 5; /* 5MB */
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param strOsType OS Type string (stored as is if aOsType is NULL).
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
284 * scheme (includes the UUID).
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 const Utf8Str &strOsType,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 if (llGroups.size())
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Let the OS type select 64-bit ness. */
347 mHWData->mLongMode = aOsType->i_is64Bit()
348 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
349
350 /* Let the OS type enable the X2APIC */
351 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
352
353 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
354 AssertComRC(rc);
355 }
356 else if (!strOsType.isEmpty())
357 {
358 /* Store OS type */
359 mUserData->s.strOsType = strOsType;
360
361 /* No guest OS type object. Pick some plausible defaults which the
362 * host can handle. There's no way to know or validate anything. */
363 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 mHWData->mX2APIC = false;
365 }
366
367 /* Apply BIOS defaults. */
368 mBIOSSettings->i_applyDefaults(aOsType);
369
370 /* Apply TPM defaults. */
371 mTrustedPlatformModule->i_applyDefaults(aOsType);
372
373 /* Apply record defaults. */
374 mRecordingSettings->i_applyDefaults();
375
376 /* Apply network adapters defaults */
377 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
378 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
379
380 /* Apply serial port defaults */
381 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
382 mSerialPorts[slot]->i_applyDefaults(aOsType);
383
384 /* Apply parallel port defaults */
385 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
386 mParallelPorts[slot]->i_applyDefaults();
387
388 /* Enable the VMMDev testing feature for bootsector VMs: */
389 if (aOsType && aOsType->i_id() == "VBoxBS_64")
390 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
391
392 /* At this point the changing of the current state modification
393 * flag is allowed. */
394 i_allowStateModification();
395
396 /* commit all changes made during the initialization */
397 i_commit();
398 }
399
400 /* Confirm a successful initialization when it's the case */
401 if (SUCCEEDED(rc))
402 {
403 if (mData->mAccessible)
404 autoInitSpan.setSucceeded();
405 else
406 autoInitSpan.setLimited();
407 }
408
409 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
410 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
411 mData->mRegistered,
412 mData->mAccessible,
413 rc));
414
415 LogFlowThisFuncLeave();
416
417 return rc;
418}
419
420/**
421 * Initializes a new instance with data from machine XML (formerly Init_Registered).
422 * Gets called in two modes:
423 *
424 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
425 * UUID is specified and we mark the machine as "registered";
426 *
427 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
428 * and the machine remains unregistered until RegisterMachine() is called.
429 *
430 * @param aParent Associated parent object
431 * @param strConfigFile Local file system path to the VM settings file (can
432 * be relative to the VirtualBox config directory).
433 * @param aId UUID of the machine or NULL (see above).
434 *
435 * @return Success indicator. if not S_OK, the machine object is invalid
436 */
437HRESULT Machine::initFromSettings(VirtualBox *aParent,
438 const Utf8Str &strConfigFile,
439 const Guid *aId)
440{
441 LogFlowThisFuncEnter();
442 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
443
444 /* Enclose the state transition NotReady->InInit->Ready */
445 AutoInitSpan autoInitSpan(this);
446 AssertReturn(autoInitSpan.isOk(), E_FAIL);
447
448 HRESULT rc = initImpl(aParent, strConfigFile);
449 if (FAILED(rc)) return rc;
450
451 if (aId)
452 {
453 // loading a registered VM:
454 unconst(mData->mUuid) = *aId;
455 mData->mRegistered = TRUE;
456 // now load the settings from XML:
457 rc = i_registeredInit();
458 // this calls initDataAndChildObjects() and loadSettings()
459 }
460 else
461 {
462 // opening an unregistered VM (VirtualBox::OpenMachine()):
463 rc = initDataAndChildObjects();
464
465 if (SUCCEEDED(rc))
466 {
467 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
468 mData->mAccessible = TRUE;
469
470 try
471 {
472 // load and parse machine XML; this will throw on XML or logic errors
473 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
474
475 // reject VM UUID duplicates, they can happen if someone
476 // tries to register an already known VM config again
477 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
478 true /* fPermitInaccessible */,
479 false /* aDoSetError */,
480 NULL) != VBOX_E_OBJECT_NOT_FOUND)
481 {
482 throw setError(E_FAIL,
483 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
484 mData->m_strConfigFile.c_str());
485 }
486
487 // use UUID from machine config
488 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
489
490 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
491 NULL /* puuidRegistry */);
492 if (FAILED(rc)) throw rc;
493
494 /* At this point the changing of the current state modification
495 * flag is allowed. */
496 i_allowStateModification();
497
498 i_commit();
499 }
500 catch (HRESULT err)
501 {
502 /* we assume that error info is set by the thrower */
503 rc = err;
504 }
505 catch (...)
506 {
507 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
508 }
509 }
510 }
511
512 /* Confirm a successful initialization when it's the case */
513 if (SUCCEEDED(rc))
514 {
515 if (mData->mAccessible)
516 autoInitSpan.setSucceeded();
517 else
518 {
519 autoInitSpan.setLimited();
520
521 // uninit media from this machine's media registry, or else
522 // reloading the settings will fail
523 mParent->i_unregisterMachineMedia(i_getId());
524 }
525 }
526
527 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
528 "rc=%08X\n",
529 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
530 mData->mRegistered, mData->mAccessible, rc));
531
532 LogFlowThisFuncLeave();
533
534 return rc;
535}
536
537/**
538 * Initializes a new instance from a machine config that is already in memory
539 * (import OVF case). Since we are importing, the UUID in the machine
540 * config is ignored and we always generate a fresh one.
541 *
542 * @param aParent Associated parent object.
543 * @param strName Name for the new machine; this overrides what is specified in config.
544 * @param strSettingsFilename File name of .vbox file.
545 * @param config Machine configuration loaded and parsed from XML.
546 *
547 * @return Success indicator. if not S_OK, the machine object is invalid
548 */
549HRESULT Machine::init(VirtualBox *aParent,
550 const Utf8Str &strName,
551 const Utf8Str &strSettingsFilename,
552 const settings::MachineConfigFile &config)
553{
554 LogFlowThisFuncEnter();
555
556 /* Enclose the state transition NotReady->InInit->Ready */
557 AutoInitSpan autoInitSpan(this);
558 AssertReturn(autoInitSpan.isOk(), E_FAIL);
559
560 HRESULT rc = initImpl(aParent, strSettingsFilename);
561 if (FAILED(rc)) return rc;
562
563 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
564 if (FAILED(rc)) return rc;
565
566 rc = initDataAndChildObjects();
567
568 if (SUCCEEDED(rc))
569 {
570 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
571 mData->mAccessible = TRUE;
572
573 // create empty machine config for instance data
574 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
575
576 // generate fresh UUID, ignore machine config
577 unconst(mData->mUuid).create();
578
579 rc = i_loadMachineDataFromSettings(config,
580 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
581
582 // override VM name as well, it may be different
583 mUserData->s.strName = strName;
584
585 if (SUCCEEDED(rc))
586 {
587 /* At this point the changing of the current state modification
588 * flag is allowed. */
589 i_allowStateModification();
590
591 /* commit all changes made during the initialization */
592 i_commit();
593 }
594 }
595
596 /* Confirm a successful initialization when it's the case */
597 if (SUCCEEDED(rc))
598 {
599 if (mData->mAccessible)
600 autoInitSpan.setSucceeded();
601 else
602 {
603 /* Ignore all errors from unregistering, they would destroy
604- * the more interesting error information we already have,
605- * pinpointing the issue with the VM config. */
606 ErrorInfoKeeper eik;
607
608 autoInitSpan.setLimited();
609
610 // uninit media from this machine's media registry, or else
611 // reloading the settings will fail
612 mParent->i_unregisterMachineMedia(i_getId());
613 }
614 }
615
616 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
617 "rc=%08X\n",
618 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
619 mData->mRegistered, mData->mAccessible, rc));
620
621 LogFlowThisFuncLeave();
622
623 return rc;
624}
625
626/**
627 * Shared code between the various init() implementations.
628 * @param aParent The VirtualBox object.
629 * @param strConfigFile Settings file.
630 * @return
631 */
632HRESULT Machine::initImpl(VirtualBox *aParent,
633 const Utf8Str &strConfigFile)
634{
635 LogFlowThisFuncEnter();
636
637 AssertReturn(aParent, E_INVALIDARG);
638 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
639
640 HRESULT rc = S_OK;
641
642 /* share the parent weakly */
643 unconst(mParent) = aParent;
644
645 /* allocate the essential machine data structure (the rest will be
646 * allocated later by initDataAndChildObjects() */
647 mData.allocate();
648
649 /* memorize the config file name (as provided) */
650 mData->m_strConfigFile = strConfigFile;
651
652 /* get the full file name */
653 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
654 if (RT_FAILURE(vrc1))
655 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
656 tr("Invalid machine settings file name '%s' (%Rrc)"),
657 strConfigFile.c_str(),
658 vrc1);
659
660 LogFlowThisFuncLeave();
661
662 return rc;
663}
664
665/**
666 * Tries to create a machine settings file in the path stored in the machine
667 * instance data. Used when a new machine is created to fail gracefully if
668 * the settings file could not be written (e.g. because machine dir is read-only).
669 * @return
670 */
671HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
672{
673 HRESULT rc = S_OK;
674
675 // when we create a new machine, we must be able to create the settings file
676 RTFILE f = NIL_RTFILE;
677 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
678 if ( RT_SUCCESS(vrc)
679 || vrc == VERR_SHARING_VIOLATION
680 )
681 {
682 if (RT_SUCCESS(vrc))
683 RTFileClose(f);
684 if (!fForceOverwrite)
685 rc = setError(VBOX_E_FILE_ERROR,
686 tr("Machine settings file '%s' already exists"),
687 mData->m_strConfigFileFull.c_str());
688 else
689 {
690 /* try to delete the config file, as otherwise the creation
691 * of a new settings file will fail. */
692 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
693 if (RT_FAILURE(vrc2))
694 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
695 tr("Could not delete the existing settings file '%s' (%Rrc)"),
696 mData->m_strConfigFileFull.c_str(), vrc2);
697 }
698 }
699 else if ( vrc != VERR_FILE_NOT_FOUND
700 && vrc != VERR_PATH_NOT_FOUND
701 )
702 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
703 tr("Invalid machine settings file name '%s' (%Rrc)"),
704 mData->m_strConfigFileFull.c_str(),
705 vrc);
706 return rc;
707}
708
709/**
710 * Initializes the registered machine by loading the settings file.
711 * This method is separated from #init() in order to make it possible to
712 * retry the operation after VirtualBox startup instead of refusing to
713 * startup the whole VirtualBox server in case if the settings file of some
714 * registered VM is invalid or inaccessible.
715 *
716 * @note Must be always called from this object's write lock
717 * (unless called from #init() that doesn't need any locking).
718 * @note Locks the mUSBController method for writing.
719 * @note Subclasses must not call this method.
720 */
721HRESULT Machine::i_registeredInit()
722{
723 AssertReturn(!i_isSessionMachine(), E_FAIL);
724 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
725 AssertReturn(mData->mUuid.isValid(), E_FAIL);
726 AssertReturn(!mData->mAccessible, E_FAIL);
727
728 HRESULT rc = initDataAndChildObjects();
729
730 if (SUCCEEDED(rc))
731 {
732 /* Temporarily reset the registered flag in order to let setters
733 * potentially called from loadSettings() succeed (isMutable() used in
734 * all setters will return FALSE for a Machine instance if mRegistered
735 * is TRUE). */
736 mData->mRegistered = FALSE;
737
738 try
739 {
740 // load and parse machine XML; this will throw on XML or logic errors
741 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
742
743 if (mData->mUuid != mData->pMachineConfigFile->uuid)
744 throw setError(E_FAIL,
745 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
746 mData->pMachineConfigFile->uuid.raw(),
747 mData->m_strConfigFileFull.c_str(),
748 mData->mUuid.toString().c_str(),
749 mParent->i_settingsFilePath().c_str());
750
751 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
752 NULL /* const Guid *puuidRegistry */);
753 if (FAILED(rc)) throw rc;
754 }
755 catch (HRESULT err)
756 {
757 /* we assume that error info is set by the thrower */
758 rc = err;
759 }
760 catch (...)
761 {
762 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
763 }
764
765 /* Restore the registered flag (even on failure) */
766 mData->mRegistered = TRUE;
767 }
768
769 if (SUCCEEDED(rc))
770 {
771 /* Set mAccessible to TRUE only if we successfully locked and loaded
772 * the settings file */
773 mData->mAccessible = TRUE;
774
775 /* commit all changes made during loading the settings file */
776 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
777 /// @todo r=klaus for some reason the settings loading logic backs up
778 // the settings, and therefore a commit is needed. Should probably be changed.
779 }
780 else
781 {
782 /* If the machine is registered, then, instead of returning a
783 * failure, we mark it as inaccessible and set the result to
784 * success to give it a try later */
785
786 /* fetch the current error info */
787 mData->mAccessError = com::ErrorInfo();
788 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
789
790 /* rollback all changes */
791 i_rollback(false /* aNotify */);
792
793 // uninit media from this machine's media registry, or else
794 // reloading the settings will fail
795 mParent->i_unregisterMachineMedia(i_getId());
796
797 /* uninitialize the common part to make sure all data is reset to
798 * default (null) values */
799 uninitDataAndChildObjects();
800
801 rc = S_OK;
802 }
803
804 return rc;
805}
806
807/**
808 * Uninitializes the instance.
809 * Called either from FinalRelease() or by the parent when it gets destroyed.
810 *
811 * @note The caller of this method must make sure that this object
812 * a) doesn't have active callers on the current thread and b) is not locked
813 * by the current thread; otherwise uninit() will hang either a) due to
814 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
815 * a dead-lock caused by this thread waiting for all callers on the other
816 * threads are done but preventing them from doing so by holding a lock.
817 */
818void Machine::uninit()
819{
820 LogFlowThisFuncEnter();
821
822 Assert(!isWriteLockOnCurrentThread());
823
824 Assert(!uRegistryNeedsSaving);
825 if (uRegistryNeedsSaving)
826 {
827 AutoCaller autoCaller(this);
828 if (SUCCEEDED(autoCaller.rc()))
829 {
830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
831 i_saveSettings(NULL, alock, Machine::SaveS_Force);
832 }
833 }
834
835 /* Enclose the state transition Ready->InUninit->NotReady */
836 AutoUninitSpan autoUninitSpan(this);
837 if (autoUninitSpan.uninitDone())
838 return;
839
840 Assert(!i_isSnapshotMachine());
841 Assert(!i_isSessionMachine());
842 Assert(!!mData);
843
844 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
845 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
846
847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
848
849 if (!mData->mSession.mMachine.isNull())
850 {
851 /* Theoretically, this can only happen if the VirtualBox server has been
852 * terminated while there were clients running that owned open direct
853 * sessions. Since in this case we are definitely called by
854 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
855 * won't happen on the client watcher thread (because it has a
856 * VirtualBox caller for the duration of the
857 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
858 * cannot happen until the VirtualBox caller is released). This is
859 * important, because SessionMachine::uninit() cannot correctly operate
860 * after we return from this method (it expects the Machine instance is
861 * still valid). We'll call it ourselves below.
862 */
863 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
864 (SessionMachine*)mData->mSession.mMachine));
865
866 if (Global::IsOnlineOrTransient(mData->mMachineState))
867 {
868 Log1WarningThisFunc(("Setting state to Aborted!\n"));
869 /* set machine state using SessionMachine reimplementation */
870 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
871 }
872
873 /*
874 * Uninitialize SessionMachine using public uninit() to indicate
875 * an unexpected uninitialization.
876 */
877 mData->mSession.mMachine->uninit();
878 /* SessionMachine::uninit() must set mSession.mMachine to null */
879 Assert(mData->mSession.mMachine.isNull());
880 }
881
882 // uninit media from this machine's media registry, if they're still there
883 Guid uuidMachine(i_getId());
884
885 /* the lock is no more necessary (SessionMachine is uninitialized) */
886 alock.release();
887
888 /* XXX This will fail with
889 * "cannot be closed because it is still attached to 1 virtual machines"
890 * because at this point we did not call uninitDataAndChildObjects() yet
891 * and therefore also removeBackReference() for all these mediums was not called! */
892
893 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
894 mParent->i_unregisterMachineMedia(uuidMachine);
895
896 // has machine been modified?
897 if (mData->flModifications)
898 {
899 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
900 i_rollback(false /* aNotify */);
901 }
902
903 if (mData->mAccessible)
904 uninitDataAndChildObjects();
905
906 /* free the essential data structure last */
907 mData.free();
908
909 LogFlowThisFuncLeave();
910}
911
912// Wrapped IMachine properties
913/////////////////////////////////////////////////////////////////////////////
914HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
915{
916 /* mParent is constant during life time, no need to lock */
917 ComObjPtr<VirtualBox> pVirtualBox(mParent);
918 aParent = pVirtualBox;
919
920 return S_OK;
921}
922
923
924HRESULT Machine::getAccessible(BOOL *aAccessible)
925{
926 /* In some cases (medium registry related), it is necessary to be able to
927 * go through the list of all machines. Happens when an inaccessible VM
928 * has a sensible medium registry. */
929 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
931
932 HRESULT rc = S_OK;
933
934 if (!mData->mAccessible)
935 {
936 /* try to initialize the VM once more if not accessible */
937
938 AutoReinitSpan autoReinitSpan(this);
939 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
940
941#ifdef DEBUG
942 LogFlowThisFunc(("Dumping media backreferences\n"));
943 mParent->i_dumpAllBackRefs();
944#endif
945
946 if (mData->pMachineConfigFile)
947 {
948 // reset the XML file to force loadSettings() (called from i_registeredInit())
949 // to parse it again; the file might have changed
950 delete mData->pMachineConfigFile;
951 mData->pMachineConfigFile = NULL;
952 }
953
954 rc = i_registeredInit();
955
956 if (SUCCEEDED(rc) && mData->mAccessible)
957 {
958 autoReinitSpan.setSucceeded();
959
960 /* make sure interesting parties will notice the accessibility
961 * state change */
962 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
963 mParent->i_onMachineDataChanged(mData->mUuid);
964 }
965 }
966
967 if (SUCCEEDED(rc))
968 *aAccessible = mData->mAccessible;
969
970 LogFlowThisFuncLeave();
971
972 return rc;
973}
974
975HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
976{
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
980 {
981 /* return shortly */
982 aAccessError = NULL;
983 return S_OK;
984 }
985
986 HRESULT rc = S_OK;
987
988 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
989 rc = errorInfo.createObject();
990 if (SUCCEEDED(rc))
991 {
992 errorInfo->init(mData->mAccessError.getResultCode(),
993 mData->mAccessError.getInterfaceID().ref(),
994 Utf8Str(mData->mAccessError.getComponent()).c_str(),
995 Utf8Str(mData->mAccessError.getText()));
996 aAccessError = errorInfo;
997 }
998
999 return rc;
1000}
1001
1002HRESULT Machine::getName(com::Utf8Str &aName)
1003{
1004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1005
1006 aName = mUserData->s.strName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::setName(const com::Utf8Str &aName)
1012{
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 i_setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1033{
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 aDescription = mUserData->s.strDescription;
1037
1038 return S_OK;
1039}
1040
1041HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1042{
1043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 // this can be done in principle in any state as it doesn't affect the VM
1046 // significantly, but play safe by not messing around while complex
1047 // activities are going on
1048 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1049 if (FAILED(rc)) return rc;
1050
1051 i_setModified(IsModified_MachineData);
1052 mUserData.backup();
1053 mUserData->s.strDescription = aDescription;
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::getId(com::Guid &aId)
1059{
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 aId = mData->mUuid;
1063
1064 return S_OK;
1065}
1066
1067HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1068{
1069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1070 aGroups.resize(mUserData->s.llGroups.size());
1071 size_t i = 0;
1072 for (StringsList::const_iterator
1073 it = mUserData->s.llGroups.begin();
1074 it != mUserData->s.llGroups.end();
1075 ++it, ++i)
1076 aGroups[i] = (*it);
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1082{
1083 StringsList llGroups;
1084 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1085 if (FAILED(rc))
1086 return rc;
1087
1088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1089
1090 rc = i_checkStateDependency(MutableOrSavedStateDep);
1091 if (FAILED(rc)) return rc;
1092
1093 i_setModified(IsModified_MachineData);
1094 mUserData.backup();
1095 mUserData->s.llGroups = llGroups;
1096
1097 return S_OK;
1098}
1099
1100HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1101{
1102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 aOSTypeId = mUserData->s.strOsType;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1110{
1111 /* look up the object by Id to check it is valid */
1112 ComObjPtr<GuestOSType> pGuestOSType;
1113 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1114
1115 /* when setting, always use the "etalon" value for consistency -- lookup
1116 * by ID is case-insensitive and the input value may have different case */
1117 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1118
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mUserData.backup();
1126 mUserData->s.strOsType = osTypeId;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aFirmwareType = mHWData->mFirmwareType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mFirmwareType = aFirmwareType;
1150 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1151 alock.release();
1152
1153 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aPointingHIDType = mHWData->mPointingHIDType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 i_setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mPointingHIDType = aPointingHIDType;
1200
1201 return S_OK;
1202}
1203
1204HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1205{
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *aChipsetType = mHWData->mChipsetType;
1209
1210 return S_OK;
1211}
1212
1213HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1214{
1215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1216
1217 HRESULT rc = i_checkStateDependency(MutableStateDep);
1218 if (FAILED(rc)) return rc;
1219
1220 if (aChipsetType != mHWData->mChipsetType)
1221 {
1222 i_setModified(IsModified_MachineData);
1223 mHWData.backup();
1224 mHWData->mChipsetType = aChipsetType;
1225
1226 // Resize network adapter array, to be finalized on commit/rollback.
1227 // We must not throw away entries yet, otherwise settings are lost
1228 // without a way to roll back.
1229 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1230 size_t oldCount = mNetworkAdapters.size();
1231 if (newCount > oldCount)
1232 {
1233 mNetworkAdapters.resize(newCount);
1234 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1235 {
1236 unconst(mNetworkAdapters[slot]).createObject();
1237 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1238 }
1239 }
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aIommuType = mHWData->mIommuType;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setIommuType(IommuType_T aIommuType)
1255{
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = i_checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aIommuType != mHWData->mIommuType)
1262 {
1263 if (aIommuType == IommuType_Intel)
1264 {
1265#ifndef VBOX_WITH_IOMMU_INTEL
1266 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1267 return E_UNEXPECTED;
1268#endif
1269 }
1270
1271 i_setModified(IsModified_MachineData);
1272 mHWData.backup();
1273 mHWData->mIommuType = aIommuType;
1274 }
1275
1276 return S_OK;
1277}
1278
1279HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1280{
1281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1282
1283 aParavirtDebug = mHWData->mParavirtDebug;
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1288{
1289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1290
1291 HRESULT rc = i_checkStateDependency(MutableStateDep);
1292 if (FAILED(rc)) return rc;
1293
1294 /** @todo Parse/validate options? */
1295 if (aParavirtDebug != mHWData->mParavirtDebug)
1296 {
1297 i_setModified(IsModified_MachineData);
1298 mHWData.backup();
1299 mHWData->mParavirtDebug = aParavirtDebug;
1300 }
1301
1302 return S_OK;
1303}
1304
1305HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1306{
1307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 *aParavirtProvider = mHWData->mParavirtProvider;
1310
1311 return S_OK;
1312}
1313
1314HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1315{
1316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1317
1318 HRESULT rc = i_checkStateDependency(MutableStateDep);
1319 if (FAILED(rc)) return rc;
1320
1321 if (aParavirtProvider != mHWData->mParavirtProvider)
1322 {
1323 i_setModified(IsModified_MachineData);
1324 mHWData.backup();
1325 mHWData->mParavirtProvider = aParavirtProvider;
1326 }
1327
1328 return S_OK;
1329}
1330
1331HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1332{
1333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 *aParavirtProvider = mHWData->mParavirtProvider;
1336 switch (mHWData->mParavirtProvider)
1337 {
1338 case ParavirtProvider_None:
1339 case ParavirtProvider_HyperV:
1340 case ParavirtProvider_KVM:
1341 case ParavirtProvider_Minimal:
1342 break;
1343
1344 /* Resolve dynamic provider types to the effective types. */
1345 default:
1346 {
1347 ComObjPtr<GuestOSType> pGuestOSType;
1348 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1349 pGuestOSType);
1350 if (FAILED(hrc2) || pGuestOSType.isNull())
1351 {
1352 *aParavirtProvider = ParavirtProvider_None;
1353 break;
1354 }
1355
1356 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1357 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1358
1359 switch (mHWData->mParavirtProvider)
1360 {
1361 case ParavirtProvider_Legacy:
1362 {
1363 if (fOsXGuest)
1364 *aParavirtProvider = ParavirtProvider_Minimal;
1365 else
1366 *aParavirtProvider = ParavirtProvider_None;
1367 break;
1368 }
1369
1370 case ParavirtProvider_Default:
1371 {
1372 if (fOsXGuest)
1373 *aParavirtProvider = ParavirtProvider_Minimal;
1374 else if ( mUserData->s.strOsType == "Windows11_64"
1375 || mUserData->s.strOsType == "Windows10"
1376 || mUserData->s.strOsType == "Windows10_64"
1377 || mUserData->s.strOsType == "Windows81"
1378 || mUserData->s.strOsType == "Windows81_64"
1379 || mUserData->s.strOsType == "Windows8"
1380 || mUserData->s.strOsType == "Windows8_64"
1381 || mUserData->s.strOsType == "Windows7"
1382 || mUserData->s.strOsType == "Windows7_64"
1383 || mUserData->s.strOsType == "WindowsVista"
1384 || mUserData->s.strOsType == "WindowsVista_64"
1385 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1386 || mUserData->s.strOsType.startsWith("Windows201"))
1387 && mUserData->s.strOsType.endsWith("_64"))
1388 || mUserData->s.strOsType == "Windows2012"
1389 || mUserData->s.strOsType == "Windows2012_64"
1390 || mUserData->s.strOsType == "Windows2008"
1391 || mUserData->s.strOsType == "Windows2008_64")
1392 {
1393 *aParavirtProvider = ParavirtProvider_HyperV;
1394 }
1395 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1396 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1397 || mUserData->s.strOsType == "Linux"
1398 || mUserData->s.strOsType == "Linux_64"
1399 || mUserData->s.strOsType == "ArchLinux"
1400 || mUserData->s.strOsType == "ArchLinux_64"
1401 || mUserData->s.strOsType == "Debian"
1402 || mUserData->s.strOsType == "Debian_64"
1403 || mUserData->s.strOsType == "Fedora"
1404 || mUserData->s.strOsType == "Fedora_64"
1405 || mUserData->s.strOsType == "Gentoo"
1406 || mUserData->s.strOsType == "Gentoo_64"
1407 || mUserData->s.strOsType == "Mandriva"
1408 || mUserData->s.strOsType == "Mandriva_64"
1409 || mUserData->s.strOsType == "OpenSUSE"
1410 || mUserData->s.strOsType == "OpenSUSE_64"
1411 || mUserData->s.strOsType == "Oracle"
1412 || mUserData->s.strOsType == "Oracle_64"
1413 || mUserData->s.strOsType == "RedHat"
1414 || mUserData->s.strOsType == "RedHat_64"
1415 || mUserData->s.strOsType == "Turbolinux"
1416 || mUserData->s.strOsType == "Turbolinux_64"
1417 || mUserData->s.strOsType == "Ubuntu"
1418 || mUserData->s.strOsType == "Ubuntu_64"
1419 || mUserData->s.strOsType == "Xandros"
1420 || mUserData->s.strOsType == "Xandros_64")
1421 {
1422 *aParavirtProvider = ParavirtProvider_KVM;
1423 }
1424 else
1425 *aParavirtProvider = ParavirtProvider_None;
1426 break;
1427 }
1428
1429 default: AssertFailedBreak(); /* Shut up MSC. */
1430 }
1431 break;
1432 }
1433 }
1434
1435 Assert( *aParavirtProvider == ParavirtProvider_None
1436 || *aParavirtProvider == ParavirtProvider_Minimal
1437 || *aParavirtProvider == ParavirtProvider_HyperV
1438 || *aParavirtProvider == ParavirtProvider_KVM);
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 aHardwareVersion = mHWData->mHWVersion;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1452{
1453 /* check known version */
1454 Utf8Str hwVersion = aHardwareVersion;
1455 if ( hwVersion.compare("1") != 0
1456 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1457 return setError(E_INVALIDARG,
1458 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1459
1460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 HRESULT rc = i_checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mHWVersion = aHardwareVersion;
1468
1469 return S_OK;
1470}
1471
1472HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1473{
1474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1475
1476 if (!mHWData->mHardwareUUID.isZero())
1477 aHardwareUUID = mHWData->mHardwareUUID;
1478 else
1479 aHardwareUUID = mData->mUuid;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1485{
1486 if (!aHardwareUUID.isValid())
1487 return E_INVALIDARG;
1488
1489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1490
1491 HRESULT rc = i_checkStateDependency(MutableStateDep);
1492 if (FAILED(rc)) return rc;
1493
1494 i_setModified(IsModified_MachineData);
1495 mHWData.backup();
1496 if (aHardwareUUID == mData->mUuid)
1497 mHWData->mHardwareUUID.clear();
1498 else
1499 mHWData->mHardwareUUID = aHardwareUUID;
1500
1501 return S_OK;
1502}
1503
1504HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1505{
1506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 *aMemorySize = mHWData->mMemorySize;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::setMemorySize(ULONG aMemorySize)
1514{
1515 /* check RAM limits */
1516 if ( aMemorySize < MM_RAM_MIN_IN_MB
1517 || aMemorySize > MM_RAM_MAX_IN_MB
1518 )
1519 return setError(E_INVALIDARG,
1520 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1521 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1522
1523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 HRESULT rc = i_checkStateDependency(MutableStateDep);
1526 if (FAILED(rc)) return rc;
1527
1528 i_setModified(IsModified_MachineData);
1529 mHWData.backup();
1530 mHWData->mMemorySize = aMemorySize;
1531
1532 return S_OK;
1533}
1534
1535HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1536{
1537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 *aCPUCount = mHWData->mCPUCount;
1540
1541 return S_OK;
1542}
1543
1544HRESULT Machine::setCPUCount(ULONG aCPUCount)
1545{
1546 /* check CPU limits */
1547 if ( aCPUCount < SchemaDefs::MinCPUCount
1548 || aCPUCount > SchemaDefs::MaxCPUCount
1549 )
1550 return setError(E_INVALIDARG,
1551 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1552 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1553
1554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1557 if (mHWData->mCPUHotPlugEnabled)
1558 {
1559 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1560 {
1561 if (mHWData->mCPUAttached[idx])
1562 return setError(E_INVALIDARG,
1563 tr("There is still a CPU attached to socket %lu."
1564 "Detach the CPU before removing the socket"),
1565 aCPUCount, idx+1);
1566 }
1567 }
1568
1569 HRESULT rc = i_checkStateDependency(MutableStateDep);
1570 if (FAILED(rc)) return rc;
1571
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574 mHWData->mCPUCount = aCPUCount;
1575
1576 return S_OK;
1577}
1578
1579HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1580{
1581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1582
1583 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1584
1585 return S_OK;
1586}
1587
1588HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1589{
1590 HRESULT rc = S_OK;
1591
1592 /* check throttle limits */
1593 if ( aCPUExecutionCap < 1
1594 || aCPUExecutionCap > 100
1595 )
1596 return setError(E_INVALIDARG,
1597 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1598 aCPUExecutionCap, 1, 100);
1599
1600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 alock.release();
1603 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1604 alock.acquire();
1605 if (FAILED(rc)) return rc;
1606
1607 i_setModified(IsModified_MachineData);
1608 mHWData.backup();
1609 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1610
1611 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1612 if (Global::IsOnline(mData->mMachineState))
1613 i_saveSettings(NULL, alock);
1614
1615 return S_OK;
1616}
1617
1618HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1619{
1620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1621
1622 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1623
1624 return S_OK;
1625}
1626
1627HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1628{
1629 HRESULT rc = S_OK;
1630
1631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1632
1633 rc = i_checkStateDependency(MutableStateDep);
1634 if (FAILED(rc)) return rc;
1635
1636 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1637 {
1638 if (aCPUHotPlugEnabled)
1639 {
1640 i_setModified(IsModified_MachineData);
1641 mHWData.backup();
1642
1643 /* Add the amount of CPUs currently attached */
1644 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1645 mHWData->mCPUAttached[i] = true;
1646 }
1647 else
1648 {
1649 /*
1650 * We can disable hotplug only if the amount of maximum CPUs is equal
1651 * to the amount of attached CPUs
1652 */
1653 unsigned cCpusAttached = 0;
1654 unsigned iHighestId = 0;
1655
1656 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1657 {
1658 if (mHWData->mCPUAttached[i])
1659 {
1660 cCpusAttached++;
1661 iHighestId = i;
1662 }
1663 }
1664
1665 if ( (cCpusAttached != mHWData->mCPUCount)
1666 || (iHighestId >= mHWData->mCPUCount))
1667 return setError(E_INVALIDARG,
1668 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1669
1670 i_setModified(IsModified_MachineData);
1671 mHWData.backup();
1672 }
1673 }
1674
1675 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1676
1677 return rc;
1678}
1679
1680HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1681{
1682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1683
1684 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1685
1686 return S_OK;
1687}
1688
1689HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1690{
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1694 if (SUCCEEDED(hrc))
1695 {
1696 i_setModified(IsModified_MachineData);
1697 mHWData.backup();
1698 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1699 }
1700 return hrc;
1701}
1702
1703HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1704{
1705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1706 aCPUProfile = mHWData->mCpuProfile;
1707 return S_OK;
1708}
1709
1710HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1711{
1712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1713 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1714 if (SUCCEEDED(hrc))
1715 {
1716 i_setModified(IsModified_MachineData);
1717 mHWData.backup();
1718 /* Empty equals 'host'. */
1719 if (aCPUProfile.isNotEmpty())
1720 mHWData->mCpuProfile = aCPUProfile;
1721 else
1722 mHWData->mCpuProfile = "host";
1723 }
1724 return hrc;
1725}
1726
1727HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1728{
1729#ifdef VBOX_WITH_USB_CARDREADER
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1733
1734 return S_OK;
1735#else
1736 NOREF(aEmulatedUSBCardReaderEnabled);
1737 return E_NOTIMPL;
1738#endif
1739}
1740
1741HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1742{
1743#ifdef VBOX_WITH_USB_CARDREADER
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1747 if (FAILED(rc)) return rc;
1748
1749 i_setModified(IsModified_MachineData);
1750 mHWData.backup();
1751 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1752
1753 return S_OK;
1754#else
1755 NOREF(aEmulatedUSBCardReaderEnabled);
1756 return E_NOTIMPL;
1757#endif
1758}
1759
1760HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763
1764 *aHPETEnabled = mHWData->mHPETEnabled;
1765
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1770{
1771 HRESULT rc = S_OK;
1772
1773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1774
1775 rc = i_checkStateDependency(MutableStateDep);
1776 if (FAILED(rc)) return rc;
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780
1781 mHWData->mHPETEnabled = aHPETEnabled;
1782
1783 return rc;
1784}
1785
1786/** @todo this method should not be public */
1787HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1788{
1789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1790
1791 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1792
1793 return S_OK;
1794}
1795
1796/**
1797 * Set the memory balloon size.
1798 *
1799 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1800 * we have to make sure that we never call IGuest from here.
1801 */
1802HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1803{
1804 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1805#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1806 /* check limits */
1807 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1808 return setError(E_INVALIDARG,
1809 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1810 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1811
1812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1813
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1817
1818 return S_OK;
1819#else
1820 NOREF(aMemoryBalloonSize);
1821 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1822#endif
1823}
1824
1825HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1826{
1827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1830 return S_OK;
1831}
1832
1833HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1834{
1835#ifdef VBOX_WITH_PAGE_SHARING
1836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1839 i_setModified(IsModified_MachineData);
1840 mHWData.backup();
1841 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1842 return S_OK;
1843#else
1844 NOREF(aPageFusionEnabled);
1845 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1846#endif
1847}
1848
1849HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1850{
1851 /* mBIOSSettings is constant during life time, no need to lock */
1852 aBIOSSettings = mBIOSSettings;
1853
1854 return S_OK;
1855}
1856
1857HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1858{
1859 /* mTrustedPlatformModule is constant during life time, no need to lock */
1860 aTrustedPlatformModule = mTrustedPlatformModule;
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1866{
1867 /* mNvramStore is constant during life time, no need to lock */
1868 aNvramStore = mNvramStore;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 aRecordingSettings = mRecordingSettings;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 aGraphicsAdapter = mGraphicsAdapter;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1892{
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 switch (aProperty)
1896 {
1897 case CPUPropertyType_PAE:
1898 *aValue = mHWData->mPAEEnabled;
1899 break;
1900
1901 case CPUPropertyType_LongMode:
1902 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1903 *aValue = TRUE;
1904 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1905 *aValue = FALSE;
1906#if HC_ARCH_BITS == 64
1907 else
1908 *aValue = TRUE;
1909#else
1910 else
1911 {
1912 *aValue = FALSE;
1913
1914 ComObjPtr<GuestOSType> pGuestOSType;
1915 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1916 pGuestOSType);
1917 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1918 {
1919 if (pGuestOSType->i_is64Bit())
1920 {
1921 ComObjPtr<Host> pHost = mParent->i_host();
1922 alock.release();
1923
1924 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1925 if (FAILED(hrc2))
1926 *aValue = FALSE;
1927 }
1928 }
1929 }
1930#endif
1931 break;
1932
1933 case CPUPropertyType_TripleFaultReset:
1934 *aValue = mHWData->mTripleFaultReset;
1935 break;
1936
1937 case CPUPropertyType_APIC:
1938 *aValue = mHWData->mAPIC;
1939 break;
1940
1941 case CPUPropertyType_X2APIC:
1942 *aValue = mHWData->mX2APIC;
1943 break;
1944
1945 case CPUPropertyType_IBPBOnVMExit:
1946 *aValue = mHWData->mIBPBOnVMExit;
1947 break;
1948
1949 case CPUPropertyType_IBPBOnVMEntry:
1950 *aValue = mHWData->mIBPBOnVMEntry;
1951 break;
1952
1953 case CPUPropertyType_SpecCtrl:
1954 *aValue = mHWData->mSpecCtrl;
1955 break;
1956
1957 case CPUPropertyType_SpecCtrlByHost:
1958 *aValue = mHWData->mSpecCtrlByHost;
1959 break;
1960
1961 case CPUPropertyType_HWVirt:
1962 *aValue = mHWData->mNestedHWVirt;
1963 break;
1964
1965 case CPUPropertyType_L1DFlushOnEMTScheduling:
1966 *aValue = mHWData->mL1DFlushOnSched;
1967 break;
1968
1969 case CPUPropertyType_L1DFlushOnVMEntry:
1970 *aValue = mHWData->mL1DFlushOnVMEntry;
1971 break;
1972
1973 case CPUPropertyType_MDSClearOnEMTScheduling:
1974 *aValue = mHWData->mMDSClearOnSched;
1975 break;
1976
1977 case CPUPropertyType_MDSClearOnVMEntry:
1978 *aValue = mHWData->mMDSClearOnVMEntry;
1979 break;
1980
1981 default:
1982 return E_INVALIDARG;
1983 }
1984 return S_OK;
1985}
1986
1987HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1988{
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 HRESULT rc = i_checkStateDependency(MutableStateDep);
1992 if (FAILED(rc)) return rc;
1993
1994 switch (aProperty)
1995 {
1996 case CPUPropertyType_PAE:
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mPAEEnabled = !!aValue;
2000 break;
2001
2002 case CPUPropertyType_LongMode:
2003 i_setModified(IsModified_MachineData);
2004 mHWData.backup();
2005 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2006 break;
2007
2008 case CPUPropertyType_TripleFaultReset:
2009 i_setModified(IsModified_MachineData);
2010 mHWData.backup();
2011 mHWData->mTripleFaultReset = !!aValue;
2012 break;
2013
2014 case CPUPropertyType_APIC:
2015 if (mHWData->mX2APIC)
2016 aValue = TRUE;
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mAPIC = !!aValue;
2020 break;
2021
2022 case CPUPropertyType_X2APIC:
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mX2APIC = !!aValue;
2026 if (aValue)
2027 mHWData->mAPIC = !!aValue;
2028 break;
2029
2030 case CPUPropertyType_IBPBOnVMExit:
2031 i_setModified(IsModified_MachineData);
2032 mHWData.backup();
2033 mHWData->mIBPBOnVMExit = !!aValue;
2034 break;
2035
2036 case CPUPropertyType_IBPBOnVMEntry:
2037 i_setModified(IsModified_MachineData);
2038 mHWData.backup();
2039 mHWData->mIBPBOnVMEntry = !!aValue;
2040 break;
2041
2042 case CPUPropertyType_SpecCtrl:
2043 i_setModified(IsModified_MachineData);
2044 mHWData.backup();
2045 mHWData->mSpecCtrl = !!aValue;
2046 break;
2047
2048 case CPUPropertyType_SpecCtrlByHost:
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mSpecCtrlByHost = !!aValue;
2052 break;
2053
2054 case CPUPropertyType_HWVirt:
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mNestedHWVirt = !!aValue;
2058 break;
2059
2060 case CPUPropertyType_L1DFlushOnEMTScheduling:
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mL1DFlushOnSched = !!aValue;
2064 break;
2065
2066 case CPUPropertyType_L1DFlushOnVMEntry:
2067 i_setModified(IsModified_MachineData);
2068 mHWData.backup();
2069 mHWData->mL1DFlushOnVMEntry = !!aValue;
2070 break;
2071
2072 case CPUPropertyType_MDSClearOnEMTScheduling:
2073 i_setModified(IsModified_MachineData);
2074 mHWData.backup();
2075 mHWData->mMDSClearOnSched = !!aValue;
2076 break;
2077
2078 case CPUPropertyType_MDSClearOnVMEntry:
2079 i_setModified(IsModified_MachineData);
2080 mHWData.backup();
2081 mHWData->mMDSClearOnVMEntry = !!aValue;
2082 break;
2083
2084 default:
2085 return E_INVALIDARG;
2086 }
2087 return S_OK;
2088}
2089
2090HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2091 ULONG *aValEcx, ULONG *aValEdx)
2092{
2093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2094 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2095 {
2096 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2097 it != mHWData->mCpuIdLeafList.end();
2098 ++it)
2099 {
2100 if (aOrdinal == 0)
2101 {
2102 const settings::CpuIdLeaf &rLeaf= *it;
2103 *aIdx = rLeaf.idx;
2104 *aSubIdx = rLeaf.idxSub;
2105 *aValEax = rLeaf.uEax;
2106 *aValEbx = rLeaf.uEbx;
2107 *aValEcx = rLeaf.uEcx;
2108 *aValEdx = rLeaf.uEdx;
2109 return S_OK;
2110 }
2111 aOrdinal--;
2112 }
2113 }
2114 return E_INVALIDARG;
2115}
2116
2117HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 /*
2122 * Search the list.
2123 */
2124 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2125 {
2126 const settings::CpuIdLeaf &rLeaf= *it;
2127 if ( rLeaf.idx == aIdx
2128 && ( aSubIdx == UINT32_MAX
2129 || rLeaf.idxSub == aSubIdx) )
2130 {
2131 *aValEax = rLeaf.uEax;
2132 *aValEbx = rLeaf.uEbx;
2133 *aValEcx = rLeaf.uEcx;
2134 *aValEdx = rLeaf.uEdx;
2135 return S_OK;
2136 }
2137 }
2138
2139 return E_INVALIDARG;
2140}
2141
2142
2143HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2144{
2145 /*
2146 * Validate input before taking locks and checking state.
2147 */
2148 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2149 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2150 if ( aIdx >= UINT32_C(0x20)
2151 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2152 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2153 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2154
2155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2156 HRESULT rc = i_checkStateDependency(MutableStateDep);
2157 if (FAILED(rc)) return rc;
2158
2159 /*
2160 * Impose a maximum number of leaves.
2161 */
2162 if (mHWData->mCpuIdLeafList.size() > 256)
2163 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2164
2165 /*
2166 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2167 */
2168 i_setModified(IsModified_MachineData);
2169 mHWData.backup();
2170
2171 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2172 {
2173 settings::CpuIdLeaf &rLeaf= *it;
2174 if ( rLeaf.idx == aIdx
2175 && ( aSubIdx == UINT32_MAX
2176 || rLeaf.idxSub == aSubIdx) )
2177 it = mHWData->mCpuIdLeafList.erase(it);
2178 else
2179 ++it;
2180 }
2181
2182 settings::CpuIdLeaf NewLeaf;
2183 NewLeaf.idx = aIdx;
2184 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2185 NewLeaf.uEax = aValEax;
2186 NewLeaf.uEbx = aValEbx;
2187 NewLeaf.uEcx = aValEcx;
2188 NewLeaf.uEdx = aValEdx;
2189 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2190 return S_OK;
2191}
2192
2193HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2194{
2195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 HRESULT rc = i_checkStateDependency(MutableStateDep);
2198 if (FAILED(rc)) return rc;
2199
2200 /*
2201 * Do the removal.
2202 */
2203 bool fModified = mHWData.isBackedUp();
2204 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2205 {
2206 settings::CpuIdLeaf &rLeaf= *it;
2207 if ( rLeaf.idx == aIdx
2208 && ( aSubIdx == UINT32_MAX
2209 || rLeaf.idxSub == aSubIdx) )
2210 {
2211 if (!fModified)
2212 {
2213 fModified = true;
2214 i_setModified(IsModified_MachineData);
2215 mHWData.backup();
2216 // Start from the beginning, since mHWData.backup() creates
2217 // a new list, causing iterator mixup. This makes sure that
2218 // the settings are not unnecessarily marked as modified,
2219 // at the price of extra list walking.
2220 it = mHWData->mCpuIdLeafList.begin();
2221 }
2222 else
2223 it = mHWData->mCpuIdLeafList.erase(it);
2224 }
2225 else
2226 ++it;
2227 }
2228
2229 return S_OK;
2230}
2231
2232HRESULT Machine::removeAllCPUIDLeaves()
2233{
2234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2235
2236 HRESULT rc = i_checkStateDependency(MutableStateDep);
2237 if (FAILED(rc)) return rc;
2238
2239 if (mHWData->mCpuIdLeafList.size() > 0)
2240 {
2241 i_setModified(IsModified_MachineData);
2242 mHWData.backup();
2243
2244 mHWData->mCpuIdLeafList.clear();
2245 }
2246
2247 return S_OK;
2248}
2249HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2250{
2251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2252
2253 switch(aProperty)
2254 {
2255 case HWVirtExPropertyType_Enabled:
2256 *aValue = mHWData->mHWVirtExEnabled;
2257 break;
2258
2259 case HWVirtExPropertyType_VPID:
2260 *aValue = mHWData->mHWVirtExVPIDEnabled;
2261 break;
2262
2263 case HWVirtExPropertyType_NestedPaging:
2264 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2265 break;
2266
2267 case HWVirtExPropertyType_UnrestrictedExecution:
2268 *aValue = mHWData->mHWVirtExUXEnabled;
2269 break;
2270
2271 case HWVirtExPropertyType_LargePages:
2272 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2273 break;
2274
2275 case HWVirtExPropertyType_Force:
2276 *aValue = mHWData->mHWVirtExForceEnabled;
2277 break;
2278
2279 case HWVirtExPropertyType_UseNativeApi:
2280 *aValue = mHWData->mHWVirtExUseNativeApi;
2281 break;
2282
2283 case HWVirtExPropertyType_VirtVmsaveVmload:
2284 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2285 break;
2286
2287 default:
2288 return E_INVALIDARG;
2289 }
2290 return S_OK;
2291}
2292
2293HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2294{
2295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 HRESULT rc = i_checkStateDependency(MutableStateDep);
2298 if (FAILED(rc)) return rc;
2299
2300 switch (aProperty)
2301 {
2302 case HWVirtExPropertyType_Enabled:
2303 i_setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mHWVirtExEnabled = !!aValue;
2306 break;
2307
2308 case HWVirtExPropertyType_VPID:
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2312 break;
2313
2314 case HWVirtExPropertyType_NestedPaging:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2318 break;
2319
2320 case HWVirtExPropertyType_UnrestrictedExecution:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mHWVirtExUXEnabled = !!aValue;
2324 break;
2325
2326 case HWVirtExPropertyType_LargePages:
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2330 break;
2331
2332 case HWVirtExPropertyType_Force:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mHWVirtExForceEnabled = !!aValue;
2336 break;
2337
2338 case HWVirtExPropertyType_UseNativeApi:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mHWVirtExUseNativeApi = !!aValue;
2342 break;
2343
2344 case HWVirtExPropertyType_VirtVmsaveVmload:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2348 break;
2349
2350 default:
2351 return E_INVALIDARG;
2352 }
2353
2354 return S_OK;
2355}
2356
2357HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2358{
2359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2362
2363 return S_OK;
2364}
2365
2366HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2367{
2368 /** @todo (r=dmik):
2369 * 1. Allow to change the name of the snapshot folder containing snapshots
2370 * 2. Rename the folder on disk instead of just changing the property
2371 * value (to be smart and not to leave garbage). Note that it cannot be
2372 * done here because the change may be rolled back. Thus, the right
2373 * place is #saveSettings().
2374 */
2375
2376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2377
2378 HRESULT rc = i_checkStateDependency(MutableStateDep);
2379 if (FAILED(rc)) return rc;
2380
2381 if (!mData->mCurrentSnapshot.isNull())
2382 return setError(E_FAIL,
2383 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2384
2385 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2386
2387 if (strSnapshotFolder.isEmpty())
2388 strSnapshotFolder = "Snapshots";
2389 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2390 if (RT_FAILURE(vrc))
2391 return setErrorBoth(E_FAIL, vrc,
2392 tr("Invalid snapshot folder '%s' (%Rrc)"),
2393 strSnapshotFolder.c_str(), vrc);
2394
2395 i_setModified(IsModified_MachineData);
2396 mUserData.backup();
2397
2398 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2404{
2405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 aMediumAttachments.resize(mMediumAttachments->size());
2408 size_t i = 0;
2409 for (MediumAttachmentList::const_iterator
2410 it = mMediumAttachments->begin();
2411 it != mMediumAttachments->end();
2412 ++it, ++i)
2413 aMediumAttachments[i] = *it;
2414
2415 return S_OK;
2416}
2417
2418HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2419{
2420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 Assert(!!mVRDEServer);
2423
2424 aVRDEServer = mVRDEServer;
2425
2426 return S_OK;
2427}
2428
2429HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2430{
2431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2432
2433 aAudioAdapter = mAudioAdapter;
2434
2435 return S_OK;
2436}
2437
2438HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2439{
2440#ifdef VBOX_WITH_VUSB
2441 clearError();
2442 MultiResult rc(S_OK);
2443
2444# ifdef VBOX_WITH_USB
2445 rc = mParent->i_host()->i_checkUSBProxyService();
2446 if (FAILED(rc)) return rc;
2447# endif
2448
2449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2450
2451 aUSBControllers.resize(mUSBControllers->size());
2452 size_t i = 0;
2453 for (USBControllerList::const_iterator
2454 it = mUSBControllers->begin();
2455 it != mUSBControllers->end();
2456 ++it, ++i)
2457 aUSBControllers[i] = *it;
2458
2459 return S_OK;
2460#else
2461 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2462 * extended error info to indicate that USB is simply not available
2463 * (w/o treating it as a failure), for example, as in OSE */
2464 NOREF(aUSBControllers);
2465 ReturnComNotImplemented();
2466#endif /* VBOX_WITH_VUSB */
2467}
2468
2469HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2470{
2471#ifdef VBOX_WITH_VUSB
2472 clearError();
2473 MultiResult rc(S_OK);
2474
2475# ifdef VBOX_WITH_USB
2476 rc = mParent->i_host()->i_checkUSBProxyService();
2477 if (FAILED(rc)) return rc;
2478# endif
2479
2480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 aUSBDeviceFilters = mUSBDeviceFilters;
2483 return rc;
2484#else
2485 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2486 * extended error info to indicate that USB is simply not available
2487 * (w/o treating it as a failure), for example, as in OSE */
2488 NOREF(aUSBDeviceFilters);
2489 ReturnComNotImplemented();
2490#endif /* VBOX_WITH_VUSB */
2491}
2492
2493HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2494{
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 aSettingsFilePath = mData->m_strConfigFileFull;
2498
2499 return S_OK;
2500}
2501
2502HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2503{
2504 RT_NOREF(aSettingsFilePath);
2505 ReturnComNotImplemented();
2506}
2507
2508HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2509{
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2513 if (FAILED(rc)) return rc;
2514
2515 if (!mData->pMachineConfigFile->fileExists())
2516 // this is a new machine, and no config file exists yet:
2517 *aSettingsModified = TRUE;
2518 else
2519 *aSettingsModified = (mData->flModifications != 0);
2520
2521 return S_OK;
2522}
2523
2524HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2525{
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 *aSessionState = mData->mSession.mState;
2529
2530 return S_OK;
2531}
2532
2533HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2534{
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 aSessionName = mData->mSession.mName;
2538
2539 return S_OK;
2540}
2541
2542HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2543{
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 *aSessionPID = mData->mSession.mPID;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getState(MachineState_T *aState)
2552{
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555 *aState = mData->mMachineState;
2556 Assert(mData->mMachineState != MachineState_Null);
2557
2558 return S_OK;
2559}
2560
2561HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2562{
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2566
2567 return S_OK;
2568}
2569
2570HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 aStateFilePath = mSSData->strStateFilePath;
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 i_getLogFolder(aLogFolder);
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 aCurrentSnapshot = mData->mCurrentSnapshot;
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2598{
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2602 ? 0
2603 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2604
2605 return S_OK;
2606}
2607
2608HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2609{
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 /* Note: for machines with no snapshots, we always return FALSE
2613 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2614 * reasons :) */
2615
2616 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2617 ? FALSE
2618 : mData->mCurrentStateModified;
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2624{
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aSharedFolders.resize(mHWData->mSharedFolders.size());
2628 size_t i = 0;
2629 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2630 it = mHWData->mSharedFolders.begin();
2631 it != mHWData->mSharedFolders.end();
2632 ++it, ++i)
2633 aSharedFolders[i] = *it;
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 *aClipboardMode = mHWData->mClipboardMode;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2648{
2649 HRESULT rc = S_OK;
2650
2651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 alock.release();
2654 rc = i_onClipboardModeChange(aClipboardMode);
2655 alock.acquire();
2656 if (FAILED(rc)) return rc;
2657
2658 i_setModified(IsModified_MachineData);
2659 mHWData.backup();
2660 mHWData->mClipboardMode = aClipboardMode;
2661
2662 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2663 if (Global::IsOnline(mData->mMachineState))
2664 i_saveSettings(NULL, alock);
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2679{
2680 HRESULT rc = S_OK;
2681
2682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 alock.release();
2685 rc = i_onClipboardFileTransferModeChange(aEnabled);
2686 alock.acquire();
2687 if (FAILED(rc)) return rc;
2688
2689 i_setModified(IsModified_MachineData);
2690 mHWData.backup();
2691 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2692
2693 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2694 if (Global::IsOnline(mData->mMachineState))
2695 i_saveSettings(NULL, alock);
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aDnDMode = mHWData->mDnDMode;
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2710{
2711 HRESULT rc = S_OK;
2712
2713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 alock.release();
2716 rc = i_onDnDModeChange(aDnDMode);
2717
2718 alock.acquire();
2719 if (FAILED(rc)) return rc;
2720
2721 i_setModified(IsModified_MachineData);
2722 mHWData.backup();
2723 mHWData->mDnDMode = aDnDMode;
2724
2725 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2726 if (Global::IsOnline(mData->mMachineState))
2727 i_saveSettings(NULL, alock);
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2733{
2734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 aStorageControllers.resize(mStorageControllers->size());
2737 size_t i = 0;
2738 for (StorageControllerList::const_iterator
2739 it = mStorageControllers->begin();
2740 it != mStorageControllers->end();
2741 ++it, ++i)
2742 aStorageControllers[i] = *it;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *aEnabled = mUserData->s.fTeleporterEnabled;
2752
2753 return S_OK;
2754}
2755
2756HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2757{
2758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 /* Only allow it to be set to true when PoweredOff or Aborted.
2761 (Clearing it is always permitted.) */
2762 if ( aTeleporterEnabled
2763 && mData->mRegistered
2764 && ( !i_isSessionMachine()
2765 || ( mData->mMachineState != MachineState_PoweredOff
2766 && mData->mMachineState != MachineState_Teleported
2767 && mData->mMachineState != MachineState_Aborted
2768 )
2769 )
2770 )
2771 return setError(VBOX_E_INVALID_VM_STATE,
2772 tr("The machine is not powered off (state is %s)"),
2773 Global::stringifyMachineState(mData->mMachineState));
2774
2775 i_setModified(IsModified_MachineData);
2776 mUserData.backup();
2777 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2778
2779 return S_OK;
2780}
2781
2782HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2783{
2784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2787
2788 return S_OK;
2789}
2790
2791HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2792{
2793 if (aTeleporterPort >= _64K)
2794 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2795
2796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2799 if (FAILED(rc)) return rc;
2800
2801 i_setModified(IsModified_MachineData);
2802 mUserData.backup();
2803 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2818{
2819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2822 if (FAILED(rc)) return rc;
2823
2824 i_setModified(IsModified_MachineData);
2825 mUserData.backup();
2826 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2827
2828 return S_OK;
2829}
2830
2831HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2840{
2841 /*
2842 * Hash the password first.
2843 */
2844 com::Utf8Str aT = aTeleporterPassword;
2845
2846 if (!aT.isEmpty())
2847 {
2848 if (VBoxIsPasswordHashed(&aT))
2849 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2850 VBoxHashPassword(&aT);
2851 }
2852
2853 /*
2854 * Do the update.
2855 */
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2858 if (SUCCEEDED(hrc))
2859 {
2860 i_setModified(IsModified_MachineData);
2861 mUserData.backup();
2862 mUserData->s.strTeleporterPassword = aT;
2863 }
2864
2865 return hrc;
2866}
2867
2868HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2869{
2870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2871
2872 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /* Only allow it to be set to true when PoweredOff or Aborted.
2882 (Clearing it is always permitted.) */
2883 if ( aRTCUseUTC
2884 && mData->mRegistered
2885 && ( !i_isSessionMachine()
2886 || ( mData->mMachineState != MachineState_PoweredOff
2887 && mData->mMachineState != MachineState_Teleported
2888 && mData->mMachineState != MachineState_Aborted
2889 )
2890 )
2891 )
2892 return setError(VBOX_E_INVALID_VM_STATE,
2893 tr("The machine is not powered off (state is %s)"),
2894 Global::stringifyMachineState(mData->mMachineState));
2895
2896 i_setModified(IsModified_MachineData);
2897 mUserData.backup();
2898 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2904{
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2913{
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 HRESULT rc = i_checkStateDependency(MutableStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 i_setModified(IsModified_MachineData);
2920 mHWData.backup();
2921 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2922
2923 return S_OK;
2924}
2925
2926HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2927{
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 *aIOCacheSize = mHWData->mIOCacheSize;
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2936{
2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 HRESULT rc = i_checkStateDependency(MutableStateDep);
2940 if (FAILED(rc)) return rc;
2941
2942 i_setModified(IsModified_MachineData);
2943 mHWData.backup();
2944 mHWData->mIOCacheSize = aIOCacheSize;
2945
2946 return S_OK;
2947}
2948
2949
2950/**
2951 * @note Locks objects!
2952 */
2953HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2954 LockType_T aLockType)
2955{
2956 /* check the session state */
2957 SessionState_T state;
2958 HRESULT rc = aSession->COMGETTER(State)(&state);
2959 if (FAILED(rc)) return rc;
2960
2961 if (state != SessionState_Unlocked)
2962 return setError(VBOX_E_INVALID_OBJECT_STATE,
2963 tr("The given session is busy"));
2964
2965 // get the client's IInternalSessionControl interface
2966 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2967 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2968 E_INVALIDARG);
2969
2970 // session name (only used in some code paths)
2971 Utf8Str strSessionName;
2972
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 if (!mData->mRegistered)
2976 return setError(E_UNEXPECTED,
2977 tr("The machine '%s' is not registered"),
2978 mUserData->s.strName.c_str());
2979
2980 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2981
2982 SessionState_T oldState = mData->mSession.mState;
2983 /* Hack: in case the session is closing and there is a progress object
2984 * which allows waiting for the session to be closed, take the opportunity
2985 * and do a limited wait (max. 1 second). This helps a lot when the system
2986 * is busy and thus session closing can take a little while. */
2987 if ( mData->mSession.mState == SessionState_Unlocking
2988 && mData->mSession.mProgress)
2989 {
2990 alock.release();
2991 mData->mSession.mProgress->WaitForCompletion(1000);
2992 alock.acquire();
2993 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2994 }
2995
2996 // try again now
2997 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2998 // (i.e. session machine exists)
2999 && (aLockType == LockType_Shared) // caller wants a shared link to the
3000 // existing session that holds the write lock:
3001 )
3002 {
3003 // OK, share the session... we are now dealing with three processes:
3004 // 1) VBoxSVC (where this code runs);
3005 // 2) process C: the caller's client process (who wants a shared session);
3006 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3007
3008 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3009 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3010 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3011 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3012 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3013
3014 /*
3015 * Release the lock before calling the client process. It's safe here
3016 * since the only thing to do after we get the lock again is to add
3017 * the remote control to the list (which doesn't directly influence
3018 * anything).
3019 */
3020 alock.release();
3021
3022 // get the console of the session holding the write lock (this is a remote call)
3023 ComPtr<IConsole> pConsoleW;
3024 if (mData->mSession.mLockType == LockType_VM)
3025 {
3026 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3027 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3028 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3029 if (FAILED(rc))
3030 // the failure may occur w/o any error info (from RPC), so provide one
3031 return setError(VBOX_E_VM_ERROR,
3032 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3033 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3034 }
3035
3036 // share the session machine and W's console with the caller's session
3037 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3038 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3039 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3040
3041 if (FAILED(rc))
3042 // the failure may occur w/o any error info (from RPC), so provide one
3043 return setError(VBOX_E_VM_ERROR,
3044 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3045 alock.acquire();
3046
3047 // need to revalidate the state after acquiring the lock again
3048 if (mData->mSession.mState != SessionState_Locked)
3049 {
3050 pSessionControl->Uninitialize();
3051 return setError(VBOX_E_INVALID_SESSION_STATE,
3052 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3053 mUserData->s.strName.c_str());
3054 }
3055
3056 // add the caller's session to the list
3057 mData->mSession.mRemoteControls.push_back(pSessionControl);
3058 }
3059 else if ( mData->mSession.mState == SessionState_Locked
3060 || mData->mSession.mState == SessionState_Unlocking
3061 )
3062 {
3063 // sharing not permitted, or machine still unlocking:
3064 return setError(VBOX_E_INVALID_OBJECT_STATE,
3065 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3066 mUserData->s.strName.c_str());
3067 }
3068 else
3069 {
3070 // machine is not locked: then write-lock the machine (create the session machine)
3071
3072 // must not be busy
3073 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3074
3075 // get the caller's session PID
3076 RTPROCESS pid = NIL_RTPROCESS;
3077 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3078 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3079 Assert(pid != NIL_RTPROCESS);
3080
3081 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3082
3083 if (fLaunchingVMProcess)
3084 {
3085 if (mData->mSession.mPID == NIL_RTPROCESS)
3086 {
3087 // two or more clients racing for a lock, the one which set the
3088 // session state to Spawning will win, the others will get an
3089 // error as we can't decide here if waiting a little would help
3090 // (only for shared locks this would avoid an error)
3091 return setError(VBOX_E_INVALID_OBJECT_STATE,
3092 tr("The machine '%s' already has a lock request pending"),
3093 mUserData->s.strName.c_str());
3094 }
3095
3096 // this machine is awaiting for a spawning session to be opened:
3097 // then the calling process must be the one that got started by
3098 // LaunchVMProcess()
3099
3100 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3101 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3102
3103#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3104 /* Hardened windows builds spawns three processes when a VM is
3105 launched, the 3rd one is the one that will end up here. */
3106 RTPROCESS pidParent;
3107 int vrc = RTProcQueryParent(pid, &pidParent);
3108 if (RT_SUCCESS(vrc))
3109 vrc = RTProcQueryParent(pidParent, &pidParent);
3110 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3111 || vrc == VERR_ACCESS_DENIED)
3112 {
3113 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3114 mData->mSession.mPID = pid;
3115 }
3116#endif
3117
3118 if (mData->mSession.mPID != pid)
3119 return setError(E_ACCESSDENIED,
3120 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3121 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3122 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3123 }
3124
3125 // create the mutable SessionMachine from the current machine
3126 ComObjPtr<SessionMachine> sessionMachine;
3127 sessionMachine.createObject();
3128 rc = sessionMachine->init(this);
3129 AssertComRC(rc);
3130
3131 /* NOTE: doing return from this function after this point but
3132 * before the end is forbidden since it may call SessionMachine::uninit()
3133 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3134 * lock while still holding the Machine lock in alock so that a deadlock
3135 * is possible due to the wrong lock order. */
3136
3137 if (SUCCEEDED(rc))
3138 {
3139 /*
3140 * Set the session state to Spawning to protect against subsequent
3141 * attempts to open a session and to unregister the machine after
3142 * we release the lock.
3143 */
3144 SessionState_T origState = mData->mSession.mState;
3145 mData->mSession.mState = SessionState_Spawning;
3146
3147#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3148 /* Get the client token ID to be passed to the client process */
3149 Utf8Str strTokenId;
3150 sessionMachine->i_getTokenId(strTokenId);
3151 Assert(!strTokenId.isEmpty());
3152#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3153 /* Get the client token to be passed to the client process */
3154 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3155 /* The token is now "owned" by pToken, fix refcount */
3156 if (!pToken.isNull())
3157 pToken->Release();
3158#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3159
3160 /*
3161 * Release the lock before calling the client process -- it will call
3162 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3163 * because the state is Spawning, so that LaunchVMProcess() and
3164 * LockMachine() calls will fail. This method, called before we
3165 * acquire the lock again, will fail because of the wrong PID.
3166 *
3167 * Note that mData->mSession.mRemoteControls accessed outside
3168 * the lock may not be modified when state is Spawning, so it's safe.
3169 */
3170 alock.release();
3171
3172 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3173#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3174 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3175#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3176 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3177 /* Now the token is owned by the client process. */
3178 pToken.setNull();
3179#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3180 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3181
3182 /* The failure may occur w/o any error info (from RPC), so provide one */
3183 if (FAILED(rc))
3184 setError(VBOX_E_VM_ERROR,
3185 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3186
3187 // get session name, either to remember or to compare against
3188 // the already known session name.
3189 {
3190 Bstr bstrSessionName;
3191 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3192 if (SUCCEEDED(rc2))
3193 strSessionName = bstrSessionName;
3194 }
3195
3196 if ( SUCCEEDED(rc)
3197 && fLaunchingVMProcess
3198 )
3199 {
3200 /* complete the remote session initialization */
3201
3202 /* get the console from the direct session */
3203 ComPtr<IConsole> console;
3204 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3205 ComAssertComRC(rc);
3206
3207 if (SUCCEEDED(rc) && !console)
3208 {
3209 ComAssert(!!console);
3210 rc = E_FAIL;
3211 }
3212
3213 /* assign machine & console to the remote session */
3214 if (SUCCEEDED(rc))
3215 {
3216 /*
3217 * after LaunchVMProcess(), the first and the only
3218 * entry in remoteControls is that remote session
3219 */
3220 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3221 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3222 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3223
3224 /* The failure may occur w/o any error info (from RPC), so provide one */
3225 if (FAILED(rc))
3226 setError(VBOX_E_VM_ERROR,
3227 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3228 }
3229
3230 if (FAILED(rc))
3231 pSessionControl->Uninitialize();
3232 }
3233
3234 /* acquire the lock again */
3235 alock.acquire();
3236
3237 /* Restore the session state */
3238 mData->mSession.mState = origState;
3239 }
3240
3241 // finalize spawning anyway (this is why we don't return on errors above)
3242 if (fLaunchingVMProcess)
3243 {
3244 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3245 /* Note that the progress object is finalized later */
3246 /** @todo Consider checking mData->mSession.mProgress for cancellation
3247 * around here. */
3248
3249 /* We don't reset mSession.mPID here because it is necessary for
3250 * SessionMachine::uninit() to reap the child process later. */
3251
3252 if (FAILED(rc))
3253 {
3254 /* Close the remote session, remove the remote control from the list
3255 * and reset session state to Closed (@note keep the code in sync
3256 * with the relevant part in checkForSpawnFailure()). */
3257
3258 Assert(mData->mSession.mRemoteControls.size() == 1);
3259 if (mData->mSession.mRemoteControls.size() == 1)
3260 {
3261 ErrorInfoKeeper eik;
3262 mData->mSession.mRemoteControls.front()->Uninitialize();
3263 }
3264
3265 mData->mSession.mRemoteControls.clear();
3266 mData->mSession.mState = SessionState_Unlocked;
3267 }
3268 }
3269 else
3270 {
3271 /* memorize PID of the directly opened session */
3272 if (SUCCEEDED(rc))
3273 mData->mSession.mPID = pid;
3274 }
3275
3276 if (SUCCEEDED(rc))
3277 {
3278 mData->mSession.mLockType = aLockType;
3279 /* memorize the direct session control and cache IUnknown for it */
3280 mData->mSession.mDirectControl = pSessionControl;
3281 mData->mSession.mState = SessionState_Locked;
3282 if (!fLaunchingVMProcess)
3283 mData->mSession.mName = strSessionName;
3284 /* associate the SessionMachine with this Machine */
3285 mData->mSession.mMachine = sessionMachine;
3286
3287 /* request an IUnknown pointer early from the remote party for later
3288 * identity checks (it will be internally cached within mDirectControl
3289 * at least on XPCOM) */
3290 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3291 NOREF(unk);
3292 }
3293
3294 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3295 * would break the lock order */
3296 alock.release();
3297
3298 /* uninitialize the created session machine on failure */
3299 if (FAILED(rc))
3300 sessionMachine->uninit();
3301 }
3302
3303 if (SUCCEEDED(rc))
3304 {
3305 /*
3306 * tell the client watcher thread to update the set of
3307 * machines that have open sessions
3308 */
3309 mParent->i_updateClientWatcher();
3310
3311 if (oldState != SessionState_Locked)
3312 /* fire an event */
3313 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3314 }
3315
3316 return rc;
3317}
3318
3319/**
3320 * @note Locks objects!
3321 */
3322HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3323 const com::Utf8Str &aName,
3324 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3325 ComPtr<IProgress> &aProgress)
3326{
3327 Utf8Str strFrontend(aName);
3328 /* "emergencystop" doesn't need the session, so skip the checks/interface
3329 * retrieval. This code doesn't quite fit in here, but introducing a
3330 * special API method would be even more effort, and would require explicit
3331 * support by every API client. It's better to hide the feature a bit. */
3332 if (strFrontend != "emergencystop")
3333 CheckComArgNotNull(aSession);
3334
3335 HRESULT rc = S_OK;
3336 if (strFrontend.isEmpty())
3337 {
3338 Bstr bstrFrontend;
3339 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3340 if (FAILED(rc))
3341 return rc;
3342 strFrontend = bstrFrontend;
3343 if (strFrontend.isEmpty())
3344 {
3345 ComPtr<ISystemProperties> systemProperties;
3346 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3347 if (FAILED(rc))
3348 return rc;
3349 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3350 if (FAILED(rc))
3351 return rc;
3352 strFrontend = bstrFrontend;
3353 }
3354 /* paranoia - emergencystop is not a valid default */
3355 if (strFrontend == "emergencystop")
3356 strFrontend = Utf8Str::Empty;
3357 }
3358 /* default frontend: Qt GUI */
3359 if (strFrontend.isEmpty())
3360 strFrontend = "GUI/Qt";
3361
3362 if (strFrontend != "emergencystop")
3363 {
3364 /* check the session state */
3365 SessionState_T state;
3366 rc = aSession->COMGETTER(State)(&state);
3367 if (FAILED(rc))
3368 return rc;
3369
3370 if (state != SessionState_Unlocked)
3371 return setError(VBOX_E_INVALID_OBJECT_STATE,
3372 tr("The given session is busy"));
3373
3374 /* get the IInternalSessionControl interface */
3375 ComPtr<IInternalSessionControl> control(aSession);
3376 ComAssertMsgRet(!control.isNull(),
3377 ("No IInternalSessionControl interface"),
3378 E_INVALIDARG);
3379
3380 /* get the teleporter enable state for the progress object init. */
3381 BOOL fTeleporterEnabled;
3382 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3383 if (FAILED(rc))
3384 return rc;
3385
3386 /* create a progress object */
3387 ComObjPtr<ProgressProxy> progress;
3388 progress.createObject();
3389 rc = progress->init(mParent,
3390 static_cast<IMachine*>(this),
3391 Bstr(tr("Starting VM")).raw(),
3392 TRUE /* aCancelable */,
3393 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3394 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3395 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3396 2 /* uFirstOperationWeight */,
3397 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3398
3399 if (SUCCEEDED(rc))
3400 {
3401 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3402 if (SUCCEEDED(rc))
3403 {
3404 aProgress = progress;
3405
3406 /* signal the client watcher thread */
3407 mParent->i_updateClientWatcher();
3408
3409 /* fire an event */
3410 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3411 }
3412 }
3413 }
3414 else
3415 {
3416 /* no progress object - either instant success or failure */
3417 aProgress = NULL;
3418
3419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3420
3421 if (mData->mSession.mState != SessionState_Locked)
3422 return setError(VBOX_E_INVALID_OBJECT_STATE,
3423 tr("The machine '%s' is not locked by a session"),
3424 mUserData->s.strName.c_str());
3425
3426 /* must have a VM process associated - do not kill normal API clients
3427 * with an open session */
3428 if (!Global::IsOnline(mData->mMachineState))
3429 return setError(VBOX_E_INVALID_OBJECT_STATE,
3430 tr("The machine '%s' does not have a VM process"),
3431 mUserData->s.strName.c_str());
3432
3433 /* forcibly terminate the VM process */
3434 if (mData->mSession.mPID != NIL_RTPROCESS)
3435 RTProcTerminate(mData->mSession.mPID);
3436
3437 /* signal the client watcher thread, as most likely the client has
3438 * been terminated */
3439 mParent->i_updateClientWatcher();
3440 }
3441
3442 return rc;
3443}
3444
3445HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3446{
3447 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3448 return setError(E_INVALIDARG,
3449 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3450 aPosition, SchemaDefs::MaxBootPosition);
3451
3452 if (aDevice == DeviceType_USB)
3453 return setError(E_NOTIMPL,
3454 tr("Booting from USB device is currently not supported"));
3455
3456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3457
3458 HRESULT rc = i_checkStateDependency(MutableStateDep);
3459 if (FAILED(rc)) return rc;
3460
3461 i_setModified(IsModified_MachineData);
3462 mHWData.backup();
3463 mHWData->mBootOrder[aPosition - 1] = aDevice;
3464
3465 return S_OK;
3466}
3467
3468HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3469{
3470 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3471 return setError(E_INVALIDARG,
3472 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3473 aPosition, SchemaDefs::MaxBootPosition);
3474
3475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3476
3477 *aDevice = mHWData->mBootOrder[aPosition - 1];
3478
3479 return S_OK;
3480}
3481
3482HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3483 LONG aControllerPort,
3484 LONG aDevice,
3485 DeviceType_T aType,
3486 const ComPtr<IMedium> &aMedium)
3487{
3488 IMedium *aM = aMedium;
3489 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3490 aName.c_str(), aControllerPort, aDevice, aType, aM));
3491
3492 // request the host lock first, since might be calling Host methods for getting host drives;
3493 // next, protect the media tree all the while we're in here, as well as our member variables
3494 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3495 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3496
3497 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3498 if (FAILED(rc)) return rc;
3499
3500 /// @todo NEWMEDIA implicit machine registration
3501 if (!mData->mRegistered)
3502 return setError(VBOX_E_INVALID_OBJECT_STATE,
3503 tr("Cannot attach storage devices to an unregistered machine"));
3504
3505 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3506
3507 /* Check for an existing controller. */
3508 ComObjPtr<StorageController> ctl;
3509 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3510 if (FAILED(rc)) return rc;
3511
3512 StorageControllerType_T ctrlType;
3513 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3514 if (FAILED(rc))
3515 return setError(E_FAIL,
3516 tr("Could not get type of controller '%s'"),
3517 aName.c_str());
3518
3519 bool fSilent = false;
3520 Utf8Str strReconfig;
3521
3522 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3523 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3524 if ( mData->mMachineState == MachineState_Paused
3525 && strReconfig == "1")
3526 fSilent = true;
3527
3528 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3529 bool fHotplug = false;
3530 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3531 fHotplug = true;
3532
3533 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3534 return setError(VBOX_E_INVALID_VM_STATE,
3535 tr("Controller '%s' does not support hotplugging"),
3536 aName.c_str());
3537
3538 // check that the port and device are not out of range
3539 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3540 if (FAILED(rc)) return rc;
3541
3542 /* check if the device slot is already busy */
3543 MediumAttachment *pAttachTemp;
3544 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3545 aName,
3546 aControllerPort,
3547 aDevice)))
3548 {
3549 Medium *pMedium = pAttachTemp->i_getMedium();
3550 if (pMedium)
3551 {
3552 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3553 return setError(VBOX_E_OBJECT_IN_USE,
3554 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3555 pMedium->i_getLocationFull().c_str(),
3556 aControllerPort,
3557 aDevice,
3558 aName.c_str());
3559 }
3560 else
3561 return setError(VBOX_E_OBJECT_IN_USE,
3562 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3563 aControllerPort, aDevice, aName.c_str());
3564 }
3565
3566 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3567 if (aMedium && medium.isNull())
3568 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3569
3570 AutoCaller mediumCaller(medium);
3571 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3572
3573 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3574
3575 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3576 && !medium.isNull()
3577 && ( medium->i_getType() != MediumType_Readonly
3578 || medium->i_getDeviceType() != DeviceType_DVD)
3579 )
3580 return setError(VBOX_E_OBJECT_IN_USE,
3581 tr("Medium '%s' is already attached to this virtual machine"),
3582 medium->i_getLocationFull().c_str());
3583
3584 if (!medium.isNull())
3585 {
3586 MediumType_T mtype = medium->i_getType();
3587 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3588 // For DVDs it's not written to the config file, so needs no global config
3589 // version bump. For floppies it's a new attribute "type", which is ignored
3590 // by older VirtualBox version, so needs no global config version bump either.
3591 // For hard disks this type is not accepted.
3592 if (mtype == MediumType_MultiAttach)
3593 {
3594 // This type is new with VirtualBox 4.0 and therefore requires settings
3595 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3596 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3597 // two reasons: The medium type is a property of the media registry tree, which
3598 // can reside in the global config file (for pre-4.0 media); we would therefore
3599 // possibly need to bump the global config version. We don't want to do that though
3600 // because that might make downgrading to pre-4.0 impossible.
3601 // As a result, we can only use these two new types if the medium is NOT in the
3602 // global registry:
3603 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3604 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3605 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3606 )
3607 return setError(VBOX_E_INVALID_OBJECT_STATE,
3608 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3609 "to machines that were created with VirtualBox 4.0 or later"),
3610 medium->i_getLocationFull().c_str());
3611 }
3612 }
3613
3614 bool fIndirect = false;
3615 if (!medium.isNull())
3616 fIndirect = medium->i_isReadOnly();
3617 bool associate = true;
3618
3619 do
3620 {
3621 if ( aType == DeviceType_HardDisk
3622 && mMediumAttachments.isBackedUp())
3623 {
3624 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3625
3626 /* check if the medium was attached to the VM before we started
3627 * changing attachments in which case the attachment just needs to
3628 * be restored */
3629 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3630 {
3631 AssertReturn(!fIndirect, E_FAIL);
3632
3633 /* see if it's the same bus/channel/device */
3634 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3635 {
3636 /* the simplest case: restore the whole attachment
3637 * and return, nothing else to do */
3638 mMediumAttachments->push_back(pAttachTemp);
3639
3640 /* Reattach the medium to the VM. */
3641 if (fHotplug || fSilent)
3642 {
3643 mediumLock.release();
3644 treeLock.release();
3645 alock.release();
3646
3647 MediumLockList *pMediumLockList(new MediumLockList());
3648
3649 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3650 medium /* pToLockWrite */,
3651 false /* fMediumLockWriteAll */,
3652 NULL,
3653 *pMediumLockList);
3654 alock.acquire();
3655 if (FAILED(rc))
3656 delete pMediumLockList;
3657 else
3658 {
3659 mData->mSession.mLockedMedia.Unlock();
3660 alock.release();
3661 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3662 mData->mSession.mLockedMedia.Lock();
3663 alock.acquire();
3664 }
3665 alock.release();
3666
3667 if (SUCCEEDED(rc))
3668 {
3669 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3670 /* Remove lock list in case of error. */
3671 if (FAILED(rc))
3672 {
3673 mData->mSession.mLockedMedia.Unlock();
3674 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3675 mData->mSession.mLockedMedia.Lock();
3676 }
3677 }
3678 }
3679
3680 return S_OK;
3681 }
3682
3683 /* bus/channel/device differ; we need a new attachment object,
3684 * but don't try to associate it again */
3685 associate = false;
3686 break;
3687 }
3688 }
3689
3690 /* go further only if the attachment is to be indirect */
3691 if (!fIndirect)
3692 break;
3693
3694 /* perform the so called smart attachment logic for indirect
3695 * attachments. Note that smart attachment is only applicable to base
3696 * hard disks. */
3697
3698 if (medium->i_getParent().isNull())
3699 {
3700 /* first, investigate the backup copy of the current hard disk
3701 * attachments to make it possible to re-attach existing diffs to
3702 * another device slot w/o losing their contents */
3703 if (mMediumAttachments.isBackedUp())
3704 {
3705 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3706
3707 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3708 uint32_t foundLevel = 0;
3709
3710 for (MediumAttachmentList::const_iterator
3711 it = oldAtts.begin();
3712 it != oldAtts.end();
3713 ++it)
3714 {
3715 uint32_t level = 0;
3716 MediumAttachment *pAttach = *it;
3717 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3718 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3719 if (pMedium.isNull())
3720 continue;
3721
3722 if (pMedium->i_getBase(&level) == medium)
3723 {
3724 /* skip the hard disk if its currently attached (we
3725 * cannot attach the same hard disk twice) */
3726 if (i_findAttachment(*mMediumAttachments.data(),
3727 pMedium))
3728 continue;
3729
3730 /* matched device, channel and bus (i.e. attached to the
3731 * same place) will win and immediately stop the search;
3732 * otherwise the attachment that has the youngest
3733 * descendant of medium will be used
3734 */
3735 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3736 {
3737 /* the simplest case: restore the whole attachment
3738 * and return, nothing else to do */
3739 mMediumAttachments->push_back(*it);
3740
3741 /* Reattach the medium to the VM. */
3742 if (fHotplug || fSilent)
3743 {
3744 mediumLock.release();
3745 treeLock.release();
3746 alock.release();
3747
3748 MediumLockList *pMediumLockList(new MediumLockList());
3749
3750 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3751 medium /* pToLockWrite */,
3752 false /* fMediumLockWriteAll */,
3753 NULL,
3754 *pMediumLockList);
3755 alock.acquire();
3756 if (FAILED(rc))
3757 delete pMediumLockList;
3758 else
3759 {
3760 mData->mSession.mLockedMedia.Unlock();
3761 alock.release();
3762 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3763 mData->mSession.mLockedMedia.Lock();
3764 alock.acquire();
3765 }
3766 alock.release();
3767
3768 if (SUCCEEDED(rc))
3769 {
3770 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3771 /* Remove lock list in case of error. */
3772 if (FAILED(rc))
3773 {
3774 mData->mSession.mLockedMedia.Unlock();
3775 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3776 mData->mSession.mLockedMedia.Lock();
3777 }
3778 }
3779 }
3780
3781 return S_OK;
3782 }
3783 else if ( foundIt == oldAtts.end()
3784 || level > foundLevel /* prefer younger */
3785 )
3786 {
3787 foundIt = it;
3788 foundLevel = level;
3789 }
3790 }
3791 }
3792
3793 if (foundIt != oldAtts.end())
3794 {
3795 /* use the previously attached hard disk */
3796 medium = (*foundIt)->i_getMedium();
3797 mediumCaller.attach(medium);
3798 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3799 mediumLock.attach(medium);
3800 /* not implicit, doesn't require association with this VM */
3801 fIndirect = false;
3802 associate = false;
3803 /* go right to the MediumAttachment creation */
3804 break;
3805 }
3806 }
3807
3808 /* must give up the medium lock and medium tree lock as below we
3809 * go over snapshots, which needs a lock with higher lock order. */
3810 mediumLock.release();
3811 treeLock.release();
3812
3813 /* then, search through snapshots for the best diff in the given
3814 * hard disk's chain to base the new diff on */
3815
3816 ComObjPtr<Medium> base;
3817 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3818 while (snap)
3819 {
3820 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3821
3822 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3823
3824 MediumAttachment *pAttachFound = NULL;
3825 uint32_t foundLevel = 0;
3826
3827 for (MediumAttachmentList::const_iterator
3828 it = snapAtts.begin();
3829 it != snapAtts.end();
3830 ++it)
3831 {
3832 MediumAttachment *pAttach = *it;
3833 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3834 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3835 if (pMedium.isNull())
3836 continue;
3837
3838 uint32_t level = 0;
3839 if (pMedium->i_getBase(&level) == medium)
3840 {
3841 /* matched device, channel and bus (i.e. attached to the
3842 * same place) will win and immediately stop the search;
3843 * otherwise the attachment that has the youngest
3844 * descendant of medium will be used
3845 */
3846 if ( pAttach->i_getDevice() == aDevice
3847 && pAttach->i_getPort() == aControllerPort
3848 && pAttach->i_getControllerName() == aName
3849 )
3850 {
3851 pAttachFound = pAttach;
3852 break;
3853 }
3854 else if ( !pAttachFound
3855 || level > foundLevel /* prefer younger */
3856 )
3857 {
3858 pAttachFound = pAttach;
3859 foundLevel = level;
3860 }
3861 }
3862 }
3863
3864 if (pAttachFound)
3865 {
3866 base = pAttachFound->i_getMedium();
3867 break;
3868 }
3869
3870 snap = snap->i_getParent();
3871 }
3872
3873 /* re-lock medium tree and the medium, as we need it below */
3874 treeLock.acquire();
3875 mediumLock.acquire();
3876
3877 /* found a suitable diff, use it as a base */
3878 if (!base.isNull())
3879 {
3880 medium = base;
3881 mediumCaller.attach(medium);
3882 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3883 mediumLock.attach(medium);
3884 }
3885 }
3886
3887 Utf8Str strFullSnapshotFolder;
3888 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3889
3890 ComObjPtr<Medium> diff;
3891 diff.createObject();
3892 // store this diff in the same registry as the parent
3893 Guid uuidRegistryParent;
3894 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3895 {
3896 // parent image has no registry: this can happen if we're attaching a new immutable
3897 // image that has not yet been attached (medium then points to the base and we're
3898 // creating the diff image for the immutable, and the parent is not yet registered);
3899 // put the parent in the machine registry then
3900 mediumLock.release();
3901 treeLock.release();
3902 alock.release();
3903 i_addMediumToRegistry(medium);
3904 alock.acquire();
3905 treeLock.acquire();
3906 mediumLock.acquire();
3907 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3908 }
3909 rc = diff->init(mParent,
3910 medium->i_getPreferredDiffFormat(),
3911 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3912 uuidRegistryParent,
3913 DeviceType_HardDisk);
3914 if (FAILED(rc)) return rc;
3915
3916 /* Apply the normal locking logic to the entire chain. */
3917 MediumLockList *pMediumLockList(new MediumLockList());
3918 mediumLock.release();
3919 treeLock.release();
3920 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3921 diff /* pToLockWrite */,
3922 false /* fMediumLockWriteAll */,
3923 medium,
3924 *pMediumLockList);
3925 treeLock.acquire();
3926 mediumLock.acquire();
3927 if (SUCCEEDED(rc))
3928 {
3929 mediumLock.release();
3930 treeLock.release();
3931 rc = pMediumLockList->Lock();
3932 treeLock.acquire();
3933 mediumLock.acquire();
3934 if (FAILED(rc))
3935 setError(rc,
3936 tr("Could not lock medium when creating diff '%s'"),
3937 diff->i_getLocationFull().c_str());
3938 else
3939 {
3940 /* will release the lock before the potentially lengthy
3941 * operation, so protect with the special state */
3942 MachineState_T oldState = mData->mMachineState;
3943 i_setMachineState(MachineState_SettingUp);
3944
3945 mediumLock.release();
3946 treeLock.release();
3947 alock.release();
3948
3949 rc = medium->i_createDiffStorage(diff,
3950 medium->i_getPreferredDiffVariant(),
3951 pMediumLockList,
3952 NULL /* aProgress */,
3953 true /* aWait */,
3954 false /* aNotify */);
3955
3956 alock.acquire();
3957 treeLock.acquire();
3958 mediumLock.acquire();
3959
3960 i_setMachineState(oldState);
3961 }
3962 }
3963
3964 /* Unlock the media and free the associated memory. */
3965 delete pMediumLockList;
3966
3967 if (FAILED(rc)) return rc;
3968
3969 /* use the created diff for the actual attachment */
3970 medium = diff;
3971 mediumCaller.attach(medium);
3972 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3973 mediumLock.attach(medium);
3974 }
3975 while (0);
3976
3977 ComObjPtr<MediumAttachment> attachment;
3978 attachment.createObject();
3979 rc = attachment->init(this,
3980 medium,
3981 aName,
3982 aControllerPort,
3983 aDevice,
3984 aType,
3985 fIndirect,
3986 false /* fPassthrough */,
3987 false /* fTempEject */,
3988 false /* fNonRotational */,
3989 false /* fDiscard */,
3990 fHotplug /* fHotPluggable */,
3991 Utf8Str::Empty);
3992 if (FAILED(rc)) return rc;
3993
3994 if (associate && !medium.isNull())
3995 {
3996 // as the last step, associate the medium to the VM
3997 rc = medium->i_addBackReference(mData->mUuid);
3998 // here we can fail because of Deleting, or being in process of creating a Diff
3999 if (FAILED(rc)) return rc;
4000
4001 mediumLock.release();
4002 treeLock.release();
4003 alock.release();
4004 i_addMediumToRegistry(medium);
4005 alock.acquire();
4006 treeLock.acquire();
4007 mediumLock.acquire();
4008 }
4009
4010 /* success: finally remember the attachment */
4011 i_setModified(IsModified_Storage);
4012 mMediumAttachments.backup();
4013 mMediumAttachments->push_back(attachment);
4014
4015 mediumLock.release();
4016 treeLock.release();
4017 alock.release();
4018
4019 if (fHotplug || fSilent)
4020 {
4021 if (!medium.isNull())
4022 {
4023 MediumLockList *pMediumLockList(new MediumLockList());
4024
4025 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4026 medium /* pToLockWrite */,
4027 false /* fMediumLockWriteAll */,
4028 NULL,
4029 *pMediumLockList);
4030 alock.acquire();
4031 if (FAILED(rc))
4032 delete pMediumLockList;
4033 else
4034 {
4035 mData->mSession.mLockedMedia.Unlock();
4036 alock.release();
4037 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4038 mData->mSession.mLockedMedia.Lock();
4039 alock.acquire();
4040 }
4041 alock.release();
4042 }
4043
4044 if (SUCCEEDED(rc))
4045 {
4046 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4047 /* Remove lock list in case of error. */
4048 if (FAILED(rc))
4049 {
4050 mData->mSession.mLockedMedia.Unlock();
4051 mData->mSession.mLockedMedia.Remove(attachment);
4052 mData->mSession.mLockedMedia.Lock();
4053 }
4054 }
4055 }
4056
4057 /* Save modified registries, but skip this machine as it's the caller's
4058 * job to save its settings like all other settings changes. */
4059 mParent->i_unmarkRegistryModified(i_getId());
4060 mParent->i_saveModifiedRegistries();
4061
4062 if (SUCCEEDED(rc))
4063 {
4064 if (fIndirect && medium != aM)
4065 mParent->i_onMediumConfigChanged(medium);
4066 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4067 }
4068
4069 return rc;
4070}
4071
4072HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4073 LONG aDevice)
4074{
4075 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4076 aName.c_str(), aControllerPort, aDevice));
4077
4078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4079
4080 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4081 if (FAILED(rc)) return rc;
4082
4083 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4084
4085 /* Check for an existing controller. */
4086 ComObjPtr<StorageController> ctl;
4087 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4088 if (FAILED(rc)) return rc;
4089
4090 StorageControllerType_T ctrlType;
4091 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4092 if (FAILED(rc))
4093 return setError(E_FAIL,
4094 tr("Could not get type of controller '%s'"),
4095 aName.c_str());
4096
4097 bool fSilent = false;
4098 Utf8Str strReconfig;
4099
4100 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4101 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4102 if ( mData->mMachineState == MachineState_Paused
4103 && strReconfig == "1")
4104 fSilent = true;
4105
4106 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4107 bool fHotplug = false;
4108 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4109 fHotplug = true;
4110
4111 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4112 return setError(VBOX_E_INVALID_VM_STATE,
4113 tr("Controller '%s' does not support hotplugging"),
4114 aName.c_str());
4115
4116 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4117 aName,
4118 aControllerPort,
4119 aDevice);
4120 if (!pAttach)
4121 return setError(VBOX_E_OBJECT_NOT_FOUND,
4122 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4123 aDevice, aControllerPort, aName.c_str());
4124
4125 if (fHotplug && !pAttach->i_getHotPluggable())
4126 return setError(VBOX_E_NOT_SUPPORTED,
4127 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4128 aDevice, aControllerPort, aName.c_str());
4129
4130 /*
4131 * The VM has to detach the device before we delete any implicit diffs.
4132 * If this fails we can roll back without loosing data.
4133 */
4134 if (fHotplug || fSilent)
4135 {
4136 alock.release();
4137 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4138 alock.acquire();
4139 }
4140 if (FAILED(rc)) return rc;
4141
4142 /* If we are here everything went well and we can delete the implicit now. */
4143 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4144
4145 alock.release();
4146
4147 /* Save modified registries, but skip this machine as it's the caller's
4148 * job to save its settings like all other settings changes. */
4149 mParent->i_unmarkRegistryModified(i_getId());
4150 mParent->i_saveModifiedRegistries();
4151
4152 if (SUCCEEDED(rc))
4153 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4154
4155 return rc;
4156}
4157
4158HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4159 LONG aDevice, BOOL aPassthrough)
4160{
4161 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4162 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4163
4164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4165
4166 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4167 if (FAILED(rc)) return rc;
4168
4169 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4170
4171 /* Check for an existing controller. */
4172 ComObjPtr<StorageController> ctl;
4173 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4174 if (FAILED(rc)) return rc;
4175
4176 StorageControllerType_T ctrlType;
4177 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4178 if (FAILED(rc))
4179 return setError(E_FAIL,
4180 tr("Could not get type of controller '%s'"),
4181 aName.c_str());
4182
4183 bool fSilent = false;
4184 Utf8Str strReconfig;
4185
4186 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4187 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4188 if ( mData->mMachineState == MachineState_Paused
4189 && strReconfig == "1")
4190 fSilent = true;
4191
4192 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4193 bool fHotplug = false;
4194 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4195 fHotplug = true;
4196
4197 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4198 return setError(VBOX_E_INVALID_VM_STATE,
4199 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4200 aName.c_str());
4201
4202 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4203 aName,
4204 aControllerPort,
4205 aDevice);
4206 if (!pAttach)
4207 return setError(VBOX_E_OBJECT_NOT_FOUND,
4208 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4209 aDevice, aControllerPort, aName.c_str());
4210
4211
4212 i_setModified(IsModified_Storage);
4213 mMediumAttachments.backup();
4214
4215 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4216
4217 if (pAttach->i_getType() != DeviceType_DVD)
4218 return setError(E_INVALIDARG,
4219 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4220 aDevice, aControllerPort, aName.c_str());
4221
4222 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4223
4224 pAttach->i_updatePassthrough(!!aPassthrough);
4225
4226 attLock.release();
4227 alock.release();
4228 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4229 if (SUCCEEDED(rc) && fValueChanged)
4230 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4231
4232 return rc;
4233}
4234
4235HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4236 LONG aDevice, BOOL aTemporaryEject)
4237{
4238
4239 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4240 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4241
4242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4243
4244 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4245 if (FAILED(rc)) return rc;
4246
4247 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4248 aName,
4249 aControllerPort,
4250 aDevice);
4251 if (!pAttach)
4252 return setError(VBOX_E_OBJECT_NOT_FOUND,
4253 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4254 aDevice, aControllerPort, aName.c_str());
4255
4256
4257 i_setModified(IsModified_Storage);
4258 mMediumAttachments.backup();
4259
4260 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4261
4262 if (pAttach->i_getType() != DeviceType_DVD)
4263 return setError(E_INVALIDARG,
4264 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4265 aDevice, aControllerPort, aName.c_str());
4266 pAttach->i_updateTempEject(!!aTemporaryEject);
4267
4268 return S_OK;
4269}
4270
4271HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4272 LONG aDevice, BOOL aNonRotational)
4273{
4274
4275 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4276 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4277
4278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4279
4280 HRESULT rc = i_checkStateDependency(MutableStateDep);
4281 if (FAILED(rc)) return rc;
4282
4283 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4284
4285 if (Global::IsOnlineOrTransient(mData->mMachineState))
4286 return setError(VBOX_E_INVALID_VM_STATE,
4287 tr("Invalid machine state: %s"),
4288 Global::stringifyMachineState(mData->mMachineState));
4289
4290 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4291 aName,
4292 aControllerPort,
4293 aDevice);
4294 if (!pAttach)
4295 return setError(VBOX_E_OBJECT_NOT_FOUND,
4296 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4297 aDevice, aControllerPort, aName.c_str());
4298
4299
4300 i_setModified(IsModified_Storage);
4301 mMediumAttachments.backup();
4302
4303 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4304
4305 if (pAttach->i_getType() != DeviceType_HardDisk)
4306 return setError(E_INVALIDARG,
4307 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4308 aDevice, aControllerPort, aName.c_str());
4309 pAttach->i_updateNonRotational(!!aNonRotational);
4310
4311 return S_OK;
4312}
4313
4314HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4315 LONG aDevice, BOOL aDiscard)
4316{
4317
4318 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4319 aName.c_str(), aControllerPort, aDevice, aDiscard));
4320
4321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4322
4323 HRESULT rc = i_checkStateDependency(MutableStateDep);
4324 if (FAILED(rc)) return rc;
4325
4326 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4327
4328 if (Global::IsOnlineOrTransient(mData->mMachineState))
4329 return setError(VBOX_E_INVALID_VM_STATE,
4330 tr("Invalid machine state: %s"),
4331 Global::stringifyMachineState(mData->mMachineState));
4332
4333 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4334 aName,
4335 aControllerPort,
4336 aDevice);
4337 if (!pAttach)
4338 return setError(VBOX_E_OBJECT_NOT_FOUND,
4339 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4340 aDevice, aControllerPort, aName.c_str());
4341
4342
4343 i_setModified(IsModified_Storage);
4344 mMediumAttachments.backup();
4345
4346 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4347
4348 if (pAttach->i_getType() != DeviceType_HardDisk)
4349 return setError(E_INVALIDARG,
4350 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4351 aDevice, aControllerPort, aName.c_str());
4352 pAttach->i_updateDiscard(!!aDiscard);
4353
4354 return S_OK;
4355}
4356
4357HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4358 LONG aDevice, BOOL aHotPluggable)
4359{
4360 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4361 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4362
4363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4364
4365 HRESULT rc = i_checkStateDependency(MutableStateDep);
4366 if (FAILED(rc)) return rc;
4367
4368 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4369
4370 if (Global::IsOnlineOrTransient(mData->mMachineState))
4371 return setError(VBOX_E_INVALID_VM_STATE,
4372 tr("Invalid machine state: %s"),
4373 Global::stringifyMachineState(mData->mMachineState));
4374
4375 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4376 aName,
4377 aControllerPort,
4378 aDevice);
4379 if (!pAttach)
4380 return setError(VBOX_E_OBJECT_NOT_FOUND,
4381 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4382 aDevice, aControllerPort, aName.c_str());
4383
4384 /* Check for an existing controller. */
4385 ComObjPtr<StorageController> ctl;
4386 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4387 if (FAILED(rc)) return rc;
4388
4389 StorageControllerType_T ctrlType;
4390 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4391 if (FAILED(rc))
4392 return setError(E_FAIL,
4393 tr("Could not get type of controller '%s'"),
4394 aName.c_str());
4395
4396 if (!i_isControllerHotplugCapable(ctrlType))
4397 return setError(VBOX_E_NOT_SUPPORTED,
4398 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4399 aName.c_str());
4400
4401 i_setModified(IsModified_Storage);
4402 mMediumAttachments.backup();
4403
4404 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4405
4406 if (pAttach->i_getType() == DeviceType_Floppy)
4407 return setError(E_INVALIDARG,
4408 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4409 aDevice, aControllerPort, aName.c_str());
4410 pAttach->i_updateHotPluggable(!!aHotPluggable);
4411
4412 return S_OK;
4413}
4414
4415HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4416 LONG aDevice)
4417{
4418 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4419 aName.c_str(), aControllerPort, aDevice));
4420
4421 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4422}
4423
4424HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4425 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4426{
4427 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4428 aName.c_str(), aControllerPort, aDevice));
4429
4430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4431
4432 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4433 if (FAILED(rc)) return rc;
4434
4435 if (Global::IsOnlineOrTransient(mData->mMachineState))
4436 return setError(VBOX_E_INVALID_VM_STATE,
4437 tr("Invalid machine state: %s"),
4438 Global::stringifyMachineState(mData->mMachineState));
4439
4440 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4441 aName,
4442 aControllerPort,
4443 aDevice);
4444 if (!pAttach)
4445 return setError(VBOX_E_OBJECT_NOT_FOUND,
4446 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4447 aDevice, aControllerPort, aName.c_str());
4448
4449
4450 i_setModified(IsModified_Storage);
4451 mMediumAttachments.backup();
4452
4453 IBandwidthGroup *iB = aBandwidthGroup;
4454 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4455 if (aBandwidthGroup && group.isNull())
4456 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4457
4458 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4459
4460 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4461 if (strBandwidthGroupOld.isNotEmpty())
4462 {
4463 /* Get the bandwidth group object and release it - this must not fail. */
4464 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4465 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4466 Assert(SUCCEEDED(rc));
4467
4468 pBandwidthGroupOld->i_release();
4469 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4470 }
4471
4472 if (!group.isNull())
4473 {
4474 group->i_reference();
4475 pAttach->i_updateBandwidthGroup(group->i_getName());
4476 }
4477
4478 return S_OK;
4479}
4480
4481HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 DeviceType_T aType)
4485{
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aType));
4488
4489 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4490}
4491
4492
4493HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4494 LONG aControllerPort,
4495 LONG aDevice,
4496 BOOL aForce)
4497{
4498 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4499 aName.c_str(), aControllerPort, aForce));
4500
4501 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4502}
4503
4504HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4505 LONG aControllerPort,
4506 LONG aDevice,
4507 const ComPtr<IMedium> &aMedium,
4508 BOOL aForce)
4509{
4510 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4511 aName.c_str(), aControllerPort, aDevice, aForce));
4512
4513 // request the host lock first, since might be calling Host methods for getting host drives;
4514 // next, protect the media tree all the while we're in here, as well as our member variables
4515 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4516 this->lockHandle(),
4517 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4518
4519 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4520 aName,
4521 aControllerPort,
4522 aDevice);
4523 if (pAttach.isNull())
4524 return setError(VBOX_E_OBJECT_NOT_FOUND,
4525 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4526 aDevice, aControllerPort, aName.c_str());
4527
4528 /* Remember previously mounted medium. The medium before taking the
4529 * backup is not necessarily the same thing. */
4530 ComObjPtr<Medium> oldmedium;
4531 oldmedium = pAttach->i_getMedium();
4532
4533 IMedium *iM = aMedium;
4534 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4535 if (aMedium && pMedium.isNull())
4536 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4537
4538 AutoCaller mediumCaller(pMedium);
4539 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4540
4541 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4542 if (pMedium)
4543 {
4544 DeviceType_T mediumType = pAttach->i_getType();
4545 switch (mediumType)
4546 {
4547 case DeviceType_DVD:
4548 case DeviceType_Floppy:
4549 break;
4550
4551 default:
4552 return setError(VBOX_E_INVALID_OBJECT_STATE,
4553 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4554 aControllerPort,
4555 aDevice,
4556 aName.c_str());
4557 }
4558 }
4559
4560 i_setModified(IsModified_Storage);
4561 mMediumAttachments.backup();
4562
4563 {
4564 // The backup operation makes the pAttach reference point to the
4565 // old settings. Re-get the correct reference.
4566 pAttach = i_findAttachment(*mMediumAttachments.data(),
4567 aName,
4568 aControllerPort,
4569 aDevice);
4570 if (!oldmedium.isNull())
4571 oldmedium->i_removeBackReference(mData->mUuid);
4572 if (!pMedium.isNull())
4573 {
4574 pMedium->i_addBackReference(mData->mUuid);
4575
4576 mediumLock.release();
4577 multiLock.release();
4578 i_addMediumToRegistry(pMedium);
4579 multiLock.acquire();
4580 mediumLock.acquire();
4581 }
4582
4583 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4584 pAttach->i_updateMedium(pMedium);
4585 }
4586
4587 i_setModified(IsModified_Storage);
4588
4589 mediumLock.release();
4590 multiLock.release();
4591 HRESULT rc = i_onMediumChange(pAttach, aForce);
4592 multiLock.acquire();
4593 mediumLock.acquire();
4594
4595 /* On error roll back this change only. */
4596 if (FAILED(rc))
4597 {
4598 if (!pMedium.isNull())
4599 pMedium->i_removeBackReference(mData->mUuid);
4600 pAttach = i_findAttachment(*mMediumAttachments.data(),
4601 aName,
4602 aControllerPort,
4603 aDevice);
4604 /* If the attachment is gone in the meantime, bail out. */
4605 if (pAttach.isNull())
4606 return rc;
4607 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4608 if (!oldmedium.isNull())
4609 oldmedium->i_addBackReference(mData->mUuid);
4610 pAttach->i_updateMedium(oldmedium);
4611 }
4612
4613 mediumLock.release();
4614 multiLock.release();
4615
4616 /* Save modified registries, but skip this machine as it's the caller's
4617 * job to save its settings like all other settings changes. */
4618 mParent->i_unmarkRegistryModified(i_getId());
4619 mParent->i_saveModifiedRegistries();
4620
4621 return rc;
4622}
4623HRESULT Machine::getMedium(const com::Utf8Str &aName,
4624 LONG aControllerPort,
4625 LONG aDevice,
4626 ComPtr<IMedium> &aMedium)
4627{
4628 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4629 aName.c_str(), aControllerPort, aDevice));
4630
4631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 aMedium = NULL;
4634
4635 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4636 aName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4642 aDevice, aControllerPort, aName.c_str());
4643
4644 aMedium = pAttach->i_getMedium();
4645
4646 return S_OK;
4647}
4648
4649HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4650{
4651 if (aSlot < RT_ELEMENTS(mSerialPorts))
4652 {
4653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4654 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4655 return S_OK;
4656 }
4657 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4658}
4659
4660HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4661{
4662 if (aSlot < RT_ELEMENTS(mParallelPorts))
4663 {
4664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4665 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4666 return S_OK;
4667 }
4668 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4669}
4670
4671
4672HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4673{
4674 /* Do not assert if slot is out of range, just return the advertised
4675 status. testdriver/vbox.py triggers this in logVmInfo. */
4676 if (aSlot >= mNetworkAdapters.size())
4677 return setError(E_INVALIDARG,
4678 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4679 aSlot, mNetworkAdapters.size());
4680
4681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4684
4685 return S_OK;
4686}
4687
4688HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4689{
4690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4691
4692 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4693 size_t i = 0;
4694 for (settings::StringsMap::const_iterator
4695 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4696 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4697 ++it, ++i)
4698 aKeys[i] = it->first;
4699
4700 return S_OK;
4701}
4702
4703 /**
4704 * @note Locks this object for reading.
4705 */
4706HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4707 com::Utf8Str &aValue)
4708{
4709 /* start with nothing found */
4710 aValue = "";
4711
4712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4713
4714 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4715 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4716 // found:
4717 aValue = it->second; // source is a Utf8Str
4718
4719 /* return the result to caller (may be empty) */
4720 return S_OK;
4721}
4722
4723 /**
4724 * @note Locks mParent for writing + this object for writing.
4725 */
4726HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4727{
4728 /* Because control characters in aKey have caused problems in the settings
4729 * they are rejected unless the key should be deleted. */
4730 if (!aValue.isEmpty())
4731 {
4732 for (size_t i = 0; i < aKey.length(); ++i)
4733 {
4734 char ch = aKey[i];
4735 if (RTLocCIsCntrl(ch))
4736 return E_INVALIDARG;
4737 }
4738 }
4739
4740 Utf8Str strOldValue; // empty
4741
4742 // locking note: we only hold the read lock briefly to look up the old value,
4743 // then release it and call the onExtraCanChange callbacks. There is a small
4744 // chance of a race insofar as the callback might be called twice if two callers
4745 // change the same key at the same time, but that's a much better solution
4746 // than the deadlock we had here before. The actual changing of the extradata
4747 // is then performed under the write lock and race-free.
4748
4749 // look up the old value first; if nothing has changed then we need not do anything
4750 {
4751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4752
4753 // For snapshots don't even think about allowing changes, extradata
4754 // is global for a machine, so there is nothing snapshot specific.
4755 if (i_isSnapshotMachine())
4756 return setError(VBOX_E_INVALID_VM_STATE,
4757 tr("Cannot set extradata for a snapshot"));
4758
4759 // check if the right IMachine instance is used
4760 if (mData->mRegistered && !i_isSessionMachine())
4761 return setError(VBOX_E_INVALID_VM_STATE,
4762 tr("Cannot set extradata for an immutable machine"));
4763
4764 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4765 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4766 strOldValue = it->second;
4767 }
4768
4769 bool fChanged;
4770 if ((fChanged = (strOldValue != aValue)))
4771 {
4772 // ask for permission from all listeners outside the locks;
4773 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4774 // lock to copy the list of callbacks to invoke
4775 Bstr bstrError;
4776 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4777 {
4778 const char *sep = bstrError.isEmpty() ? "" : ": ";
4779 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4780 return setError(E_ACCESSDENIED,
4781 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4782 aKey.c_str(),
4783 aValue.c_str(),
4784 sep,
4785 bstrError.raw());
4786 }
4787
4788 // data is changing and change not vetoed: then write it out under the lock
4789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4790
4791 if (aValue.isEmpty())
4792 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4793 else
4794 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4795 // creates a new key if needed
4796
4797 bool fNeedsGlobalSaveSettings = false;
4798 // This saving of settings is tricky: there is no "old state" for the
4799 // extradata items at all (unlike all other settings), so the old/new
4800 // settings comparison would give a wrong result!
4801 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4802
4803 if (fNeedsGlobalSaveSettings)
4804 {
4805 // save the global settings; for that we should hold only the VirtualBox lock
4806 alock.release();
4807 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4808 mParent->i_saveSettings();
4809 }
4810 }
4811
4812 // fire notification outside the lock
4813 if (fChanged)
4814 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4815
4816 return S_OK;
4817}
4818
4819HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4820{
4821 aProgress = NULL;
4822 NOREF(aSettingsFilePath);
4823 ReturnComNotImplemented();
4824}
4825
4826HRESULT Machine::saveSettings()
4827{
4828 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4831 if (FAILED(rc)) return rc;
4832
4833 /* the settings file path may never be null */
4834 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4835
4836 /* save all VM data excluding snapshots */
4837 bool fNeedsGlobalSaveSettings = false;
4838 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4839 mlock.release();
4840
4841 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4842 {
4843 // save the global settings; for that we should hold only the VirtualBox lock
4844 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4845 rc = mParent->i_saveSettings();
4846 }
4847
4848 return rc;
4849}
4850
4851
4852HRESULT Machine::discardSettings()
4853{
4854 /*
4855 * We need to take the machine list lock here as well as the machine one
4856 * or we'll get into trouble should any media stuff require rolling back.
4857 *
4858 * Details:
4859 *
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4861 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4862 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
4863 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4864 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4865 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4866 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4867 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4869 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4870 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4872 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4873 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
4874 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
4875 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4876 * 0:005> k
4877 * # Child-SP RetAddr Call Site
4878 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4879 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4880 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4881 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4882 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4883 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4884 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4885 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4886 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4887 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4888 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4889 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4890 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4891 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4892 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4893 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4894 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4895 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4896 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4897 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4898 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4899 *
4900 */
4901 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4903
4904 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4905 if (FAILED(rc)) return rc;
4906
4907 /*
4908 * during this rollback, the session will be notified if data has
4909 * been actually changed
4910 */
4911 i_rollback(true /* aNotify */);
4912
4913 return S_OK;
4914}
4915
4916/** @note Locks objects! */
4917HRESULT Machine::unregister(AutoCaller &autoCaller,
4918 CleanupMode_T aCleanupMode,
4919 std::vector<ComPtr<IMedium> > &aMedia)
4920{
4921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4922
4923 Guid id(i_getId());
4924
4925 if (mData->mSession.mState != SessionState_Unlocked)
4926 return setError(VBOX_E_INVALID_OBJECT_STATE,
4927 tr("Cannot unregister the machine '%s' while it is locked"),
4928 mUserData->s.strName.c_str());
4929
4930 // wait for state dependents to drop to zero
4931 i_ensureNoStateDependencies(alock);
4932
4933 if (!mData->mAccessible)
4934 {
4935 // inaccessible machines can only be unregistered; uninitialize ourselves
4936 // here because currently there may be no unregistered that are inaccessible
4937 // (this state combination is not supported). Note releasing the caller and
4938 // leaving the lock before calling uninit()
4939 alock.release();
4940 autoCaller.release();
4941
4942 uninit();
4943
4944 mParent->i_unregisterMachine(this, id);
4945 // calls VirtualBox::i_saveSettings()
4946
4947 return S_OK;
4948 }
4949
4950 HRESULT rc = S_OK;
4951 mData->llFilesToDelete.clear();
4952
4953 if (!mSSData->strStateFilePath.isEmpty())
4954 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4955
4956 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4957 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4958 mData->llFilesToDelete.push_back(strNVRAMFile);
4959
4960 // This list collects the medium objects from all medium attachments
4961 // which we will detach from the machine and its snapshots, in a specific
4962 // order which allows for closing all media without getting "media in use"
4963 // errors, simply by going through the list from the front to the back:
4964 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4965 // and must be closed before the parent media from the snapshots, or closing the parents
4966 // will fail because they still have children);
4967 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4968 // the root ("first") snapshot of the machine.
4969 MediaList llMedia;
4970
4971 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4972 && mMediumAttachments->size()
4973 )
4974 {
4975 // we have media attachments: detach them all and add the Medium objects to our list
4976 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4977 }
4978
4979 if (mData->mFirstSnapshot)
4980 {
4981 // add the media from the medium attachments of the snapshots to llMedia
4982 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4983 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4984 // into the children first
4985
4986 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4987 MachineState_T oldState = mData->mMachineState;
4988 mData->mMachineState = MachineState_DeletingSnapshot;
4989
4990 // make a copy of the first snapshot reference so the refcount does not
4991 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4992 // (would hang due to the AutoCaller voodoo)
4993 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4994
4995 // GO!
4996 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4997
4998 mData->mMachineState = oldState;
4999 }
5000
5001 if (FAILED(rc))
5002 {
5003 i_rollbackMedia();
5004 return rc;
5005 }
5006
5007 // commit all the media changes made above
5008 i_commitMedia();
5009
5010 mData->mRegistered = false;
5011
5012 // machine lock no longer needed
5013 alock.release();
5014
5015 /* Make sure that the settings of the current VM are not saved, because
5016 * they are rather crippled at this point to meet the cleanup expectations
5017 * and there's no point destroying the VM config on disk just because. */
5018 mParent->i_unmarkRegistryModified(id);
5019
5020 // return media to caller
5021 aMedia.resize(llMedia.size());
5022 size_t i = 0;
5023 for (MediaList::const_iterator
5024 it = llMedia.begin();
5025 it != llMedia.end();
5026 ++it, ++i)
5027 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5028
5029 mParent->i_unregisterMachine(this, id);
5030 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5031
5032 return S_OK;
5033}
5034
5035/**
5036 * Task record for deleting a machine config.
5037 */
5038class Machine::DeleteConfigTask
5039 : public Machine::Task
5040{
5041public:
5042 DeleteConfigTask(Machine *m,
5043 Progress *p,
5044 const Utf8Str &t,
5045 const RTCList<ComPtr<IMedium> > &llMediums,
5046 const StringsList &llFilesToDelete)
5047 : Task(m, p, t),
5048 m_llMediums(llMediums),
5049 m_llFilesToDelete(llFilesToDelete)
5050 {}
5051
5052private:
5053 void handler()
5054 {
5055 try
5056 {
5057 m_pMachine->i_deleteConfigHandler(*this);
5058 }
5059 catch (...)
5060 {
5061 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5062 }
5063 }
5064
5065 RTCList<ComPtr<IMedium> > m_llMediums;
5066 StringsList m_llFilesToDelete;
5067
5068 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5069};
5070
5071/**
5072 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5073 * SessionMachine::taskHandler().
5074 *
5075 * @note Locks this object for writing.
5076 *
5077 * @param task
5078 * @return
5079 */
5080void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5081{
5082 LogFlowThisFuncEnter();
5083
5084 AutoCaller autoCaller(this);
5085 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5086 if (FAILED(autoCaller.rc()))
5087 {
5088 /* we might have been uninitialized because the session was accidentally
5089 * closed by the client, so don't assert */
5090 HRESULT rc = setError(E_FAIL,
5091 tr("The session has been accidentally closed"));
5092 task.m_pProgress->i_notifyComplete(rc);
5093 LogFlowThisFuncLeave();
5094 return;
5095 }
5096
5097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5098
5099 HRESULT rc = S_OK;
5100
5101 try
5102 {
5103 ULONG uLogHistoryCount = 3;
5104 ComPtr<ISystemProperties> systemProperties;
5105 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5106 if (FAILED(rc)) throw rc;
5107
5108 if (!systemProperties.isNull())
5109 {
5110 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5111 if (FAILED(rc)) throw rc;
5112 }
5113
5114 MachineState_T oldState = mData->mMachineState;
5115 i_setMachineState(MachineState_SettingUp);
5116 alock.release();
5117 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5118 {
5119 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5120 {
5121 AutoCaller mac(pMedium);
5122 if (FAILED(mac.rc())) throw mac.rc();
5123 Utf8Str strLocation = pMedium->i_getLocationFull();
5124 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5125 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5126 if (FAILED(rc)) throw rc;
5127 }
5128 if (pMedium->i_isMediumFormatFile())
5129 {
5130 ComPtr<IProgress> pProgress2;
5131 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5132 if (FAILED(rc)) throw rc;
5133 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5134 if (FAILED(rc)) throw rc;
5135 }
5136
5137 /* Close the medium, deliberately without checking the return
5138 * code, and without leaving any trace in the error info, as
5139 * a failure here is a very minor issue, which shouldn't happen
5140 * as above we even managed to delete the medium. */
5141 {
5142 ErrorInfoKeeper eik;
5143 pMedium->Close();
5144 }
5145 }
5146 i_setMachineState(oldState);
5147 alock.acquire();
5148
5149 // delete the files pushed on the task list by Machine::Delete()
5150 // (this includes saved states of the machine and snapshots and
5151 // medium storage files from the IMedium list passed in, and the
5152 // machine XML file)
5153 for (StringsList::const_iterator
5154 it = task.m_llFilesToDelete.begin();
5155 it != task.m_llFilesToDelete.end();
5156 ++it)
5157 {
5158 const Utf8Str &strFile = *it;
5159 LogFunc(("Deleting file %s\n", strFile.c_str()));
5160 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5161 if (FAILED(rc)) throw rc;
5162
5163 int vrc = RTFileDelete(strFile.c_str());
5164 if (RT_FAILURE(vrc))
5165 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5166 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5167 }
5168
5169 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5170 if (FAILED(rc)) throw rc;
5171
5172 /* delete the settings only when the file actually exists */
5173 if (mData->pMachineConfigFile->fileExists())
5174 {
5175 /* Delete any backup or uncommitted XML files. Ignore failures.
5176 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5177 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5178 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5179 RTFileDelete(otherXml.c_str());
5180 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5181 RTFileDelete(otherXml.c_str());
5182
5183 /* delete the Logs folder, nothing important should be left
5184 * there (we don't check for errors because the user might have
5185 * some private files there that we don't want to delete) */
5186 Utf8Str logFolder;
5187 getLogFolder(logFolder);
5188 Assert(logFolder.length());
5189 if (RTDirExists(logFolder.c_str()))
5190 {
5191 /* Delete all VBox.log[.N] files from the Logs folder
5192 * (this must be in sync with the rotation logic in
5193 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5194 * files that may have been created by the GUI. */
5195 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5196 RTFileDelete(log.c_str());
5197 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5198 RTFileDelete(log.c_str());
5199 for (ULONG i = uLogHistoryCount; i > 0; i--)
5200 {
5201 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5202 RTFileDelete(log.c_str());
5203 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5204 RTFileDelete(log.c_str());
5205 }
5206 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5207 RTFileDelete(log.c_str());
5208#if defined(RT_OS_WINDOWS)
5209 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5210 RTFileDelete(log.c_str());
5211 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5212 RTFileDelete(log.c_str());
5213#endif
5214
5215 RTDirRemove(logFolder.c_str());
5216 }
5217
5218 /* delete the Snapshots folder, nothing important should be left
5219 * there (we don't check for errors because the user might have
5220 * some private files there that we don't want to delete) */
5221 Utf8Str strFullSnapshotFolder;
5222 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5223 Assert(!strFullSnapshotFolder.isEmpty());
5224 if (RTDirExists(strFullSnapshotFolder.c_str()))
5225 RTDirRemove(strFullSnapshotFolder.c_str());
5226
5227 // delete the directory that contains the settings file, but only
5228 // if it matches the VM name
5229 Utf8Str settingsDir;
5230 if (i_isInOwnDir(&settingsDir))
5231 RTDirRemove(settingsDir.c_str());
5232 }
5233
5234 alock.release();
5235
5236 mParent->i_saveModifiedRegistries();
5237 }
5238 catch (HRESULT aRC) { rc = aRC; }
5239
5240 task.m_pProgress->i_notifyComplete(rc);
5241
5242 LogFlowThisFuncLeave();
5243}
5244
5245HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5246{
5247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5248
5249 HRESULT rc = i_checkStateDependency(MutableStateDep);
5250 if (FAILED(rc)) return rc;
5251
5252 if (mData->mRegistered)
5253 return setError(VBOX_E_INVALID_VM_STATE,
5254 tr("Cannot delete settings of a registered machine"));
5255
5256 // collect files to delete
5257 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5258 // machine config file
5259 if (mData->pMachineConfigFile->fileExists())
5260 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5261 // backup of machine config file
5262 Utf8Str strTmp(mData->m_strConfigFileFull);
5263 strTmp.append("-prev");
5264 if (RTFileExists(strTmp.c_str()))
5265 llFilesToDelete.push_back(strTmp);
5266
5267 RTCList<ComPtr<IMedium> > llMediums;
5268 for (size_t i = 0; i < aMedia.size(); ++i)
5269 {
5270 IMedium *pIMedium(aMedia[i]);
5271 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5272 if (pMedium.isNull())
5273 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5274 SafeArray<BSTR> ids;
5275 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5276 if (FAILED(rc)) return rc;
5277 /* At this point the medium should not have any back references
5278 * anymore. If it has it is attached to another VM and *must* not
5279 * deleted. */
5280 if (ids.size() < 1)
5281 llMediums.append(pMedium);
5282 }
5283
5284 ComObjPtr<Progress> pProgress;
5285 pProgress.createObject();
5286 rc = pProgress->init(i_getVirtualBox(),
5287 static_cast<IMachine*>(this) /* aInitiator */,
5288 tr("Deleting files"),
5289 true /* fCancellable */,
5290 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5291 tr("Collecting file inventory"));
5292 if (FAILED(rc))
5293 return rc;
5294
5295 /* create and start the task on a separate thread (note that it will not
5296 * start working until we release alock) */
5297 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5298 rc = pTask->createThread();
5299 pTask = NULL;
5300 if (FAILED(rc))
5301 return rc;
5302
5303 pProgress.queryInterfaceTo(aProgress.asOutParam());
5304
5305 LogFlowFuncLeave();
5306
5307 return S_OK;
5308}
5309
5310HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5311{
5312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5313
5314 ComObjPtr<Snapshot> pSnapshot;
5315 HRESULT rc;
5316
5317 if (aNameOrId.isEmpty())
5318 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5319 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5320 else
5321 {
5322 Guid uuid(aNameOrId);
5323 if (uuid.isValid())
5324 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5325 else
5326 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5327 }
5328 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5329
5330 return rc;
5331}
5332
5333HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5334 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5335{
5336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5337
5338 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5339 if (FAILED(rc)) return rc;
5340
5341 ComObjPtr<SharedFolder> sharedFolder;
5342 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5343 if (SUCCEEDED(rc))
5344 return setError(VBOX_E_OBJECT_IN_USE,
5345 tr("Shared folder named '%s' already exists"),
5346 aName.c_str());
5347
5348 sharedFolder.createObject();
5349 rc = sharedFolder->init(i_getMachine(),
5350 aName,
5351 aHostPath,
5352 !!aWritable,
5353 !!aAutomount,
5354 aAutoMountPoint,
5355 true /* fFailOnError */);
5356 if (FAILED(rc)) return rc;
5357
5358 i_setModified(IsModified_SharedFolders);
5359 mHWData.backup();
5360 mHWData->mSharedFolders.push_back(sharedFolder);
5361
5362 /* inform the direct session if any */
5363 alock.release();
5364 i_onSharedFolderChange();
5365
5366 return S_OK;
5367}
5368
5369HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5370{
5371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5372
5373 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5374 if (FAILED(rc)) return rc;
5375
5376 ComObjPtr<SharedFolder> sharedFolder;
5377 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5378 if (FAILED(rc)) return rc;
5379
5380 i_setModified(IsModified_SharedFolders);
5381 mHWData.backup();
5382 mHWData->mSharedFolders.remove(sharedFolder);
5383
5384 /* inform the direct session if any */
5385 alock.release();
5386 i_onSharedFolderChange();
5387
5388 return S_OK;
5389}
5390
5391HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5392{
5393 /* start with No */
5394 *aCanShow = FALSE;
5395
5396 ComPtr<IInternalSessionControl> directControl;
5397 {
5398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5399
5400 if (mData->mSession.mState != SessionState_Locked)
5401 return setError(VBOX_E_INVALID_VM_STATE,
5402 tr("Machine is not locked for session (session state: %s)"),
5403 Global::stringifySessionState(mData->mSession.mState));
5404
5405 if (mData->mSession.mLockType == LockType_VM)
5406 directControl = mData->mSession.mDirectControl;
5407 }
5408
5409 /* ignore calls made after #OnSessionEnd() is called */
5410 if (!directControl)
5411 return S_OK;
5412
5413 LONG64 dummy;
5414 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5415}
5416
5417HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5418{
5419 ComPtr<IInternalSessionControl> directControl;
5420 {
5421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5422
5423 if (mData->mSession.mState != SessionState_Locked)
5424 return setError(E_FAIL,
5425 tr("Machine is not locked for session (session state: %s)"),
5426 Global::stringifySessionState(mData->mSession.mState));
5427
5428 if (mData->mSession.mLockType == LockType_VM)
5429 directControl = mData->mSession.mDirectControl;
5430 }
5431
5432 /* ignore calls made after #OnSessionEnd() is called */
5433 if (!directControl)
5434 return S_OK;
5435
5436 BOOL dummy;
5437 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5438}
5439
5440#ifdef VBOX_WITH_GUEST_PROPS
5441/**
5442 * Look up a guest property in VBoxSVC's internal structures.
5443 */
5444HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5445 com::Utf8Str &aValue,
5446 LONG64 *aTimestamp,
5447 com::Utf8Str &aFlags) const
5448{
5449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5450
5451 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5452 if (it != mHWData->mGuestProperties.end())
5453 {
5454 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5455 aValue = it->second.strValue;
5456 *aTimestamp = it->second.mTimestamp;
5457 GuestPropWriteFlags(it->second.mFlags, szFlags);
5458 aFlags = Utf8Str(szFlags);
5459 }
5460
5461 return S_OK;
5462}
5463
5464/**
5465 * Query the VM that a guest property belongs to for the property.
5466 * @returns E_ACCESSDENIED if the VM process is not available or not
5467 * currently handling queries and the lookup should then be done in
5468 * VBoxSVC.
5469 */
5470HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5471 com::Utf8Str &aValue,
5472 LONG64 *aTimestamp,
5473 com::Utf8Str &aFlags) const
5474{
5475 HRESULT rc = S_OK;
5476 Bstr bstrValue;
5477 Bstr bstrFlags;
5478
5479 ComPtr<IInternalSessionControl> directControl;
5480 {
5481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5482 if (mData->mSession.mLockType == LockType_VM)
5483 directControl = mData->mSession.mDirectControl;
5484 }
5485
5486 /* ignore calls made after #OnSessionEnd() is called */
5487 if (!directControl)
5488 rc = E_ACCESSDENIED;
5489 else
5490 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5491 0 /* accessMode */,
5492 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5493
5494 aValue = bstrValue;
5495 aFlags = bstrFlags;
5496
5497 return rc;
5498}
5499#endif // VBOX_WITH_GUEST_PROPS
5500
5501HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5502 com::Utf8Str &aValue,
5503 LONG64 *aTimestamp,
5504 com::Utf8Str &aFlags)
5505{
5506#ifndef VBOX_WITH_GUEST_PROPS
5507 ReturnComNotImplemented();
5508#else // VBOX_WITH_GUEST_PROPS
5509
5510 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5511
5512 if (rc == E_ACCESSDENIED)
5513 /* The VM is not running or the service is not (yet) accessible */
5514 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5515 return rc;
5516#endif // VBOX_WITH_GUEST_PROPS
5517}
5518
5519HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5520{
5521 LONG64 dummyTimestamp;
5522 com::Utf8Str dummyFlags;
5523 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5524 return rc;
5525
5526}
5527HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5528{
5529 com::Utf8Str dummyFlags;
5530 com::Utf8Str dummyValue;
5531 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5532 return rc;
5533}
5534
5535#ifdef VBOX_WITH_GUEST_PROPS
5536/**
5537 * Set a guest property in VBoxSVC's internal structures.
5538 */
5539HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5540 const com::Utf8Str &aFlags, bool fDelete)
5541{
5542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5543 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5544 if (FAILED(rc)) return rc;
5545
5546 try
5547 {
5548 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5549 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5550 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5551
5552 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5553 if (it == mHWData->mGuestProperties.end())
5554 {
5555 if (!fDelete)
5556 {
5557 i_setModified(IsModified_MachineData);
5558 mHWData.backupEx();
5559
5560 RTTIMESPEC time;
5561 HWData::GuestProperty prop;
5562 prop.strValue = aValue;
5563 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5564 prop.mFlags = fFlags;
5565 mHWData->mGuestProperties[aName] = prop;
5566 }
5567 }
5568 else
5569 {
5570 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5571 {
5572 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5573 }
5574 else
5575 {
5576 i_setModified(IsModified_MachineData);
5577 mHWData.backupEx();
5578
5579 /* The backupEx() operation invalidates our iterator,
5580 * so get a new one. */
5581 it = mHWData->mGuestProperties.find(aName);
5582 Assert(it != mHWData->mGuestProperties.end());
5583
5584 if (!fDelete)
5585 {
5586 RTTIMESPEC time;
5587 it->second.strValue = aValue;
5588 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5589 it->second.mFlags = fFlags;
5590 }
5591 else
5592 mHWData->mGuestProperties.erase(it);
5593 }
5594 }
5595
5596 if (SUCCEEDED(rc))
5597 {
5598 alock.release();
5599
5600 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5601 }
5602 }
5603 catch (std::bad_alloc &)
5604 {
5605 rc = E_OUTOFMEMORY;
5606 }
5607
5608 return rc;
5609}
5610
5611/**
5612 * Set a property on the VM that that property belongs to.
5613 * @returns E_ACCESSDENIED if the VM process is not available or not
5614 * currently handling queries and the setting should then be done in
5615 * VBoxSVC.
5616 */
5617HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5618 const com::Utf8Str &aFlags, bool fDelete)
5619{
5620 HRESULT rc;
5621
5622 try
5623 {
5624 ComPtr<IInternalSessionControl> directControl;
5625 {
5626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5627 if (mData->mSession.mLockType == LockType_VM)
5628 directControl = mData->mSession.mDirectControl;
5629 }
5630
5631 Bstr dummy1; /* will not be changed (setter) */
5632 Bstr dummy2; /* will not be changed (setter) */
5633 LONG64 dummy64;
5634 if (!directControl)
5635 rc = E_ACCESSDENIED;
5636 else
5637 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5638 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5639 fDelete ? 2 : 1 /* accessMode */,
5640 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5641 }
5642 catch (std::bad_alloc &)
5643 {
5644 rc = E_OUTOFMEMORY;
5645 }
5646
5647 return rc;
5648}
5649#endif // VBOX_WITH_GUEST_PROPS
5650
5651HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5652 const com::Utf8Str &aFlags)
5653{
5654#ifndef VBOX_WITH_GUEST_PROPS
5655 ReturnComNotImplemented();
5656#else // VBOX_WITH_GUEST_PROPS
5657 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5658 if (rc == E_ACCESSDENIED)
5659 /* The VM is not running or the service is not (yet) accessible */
5660 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5661 return rc;
5662#endif // VBOX_WITH_GUEST_PROPS
5663}
5664
5665HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5666{
5667 return setGuestProperty(aProperty, aValue, "");
5668}
5669
5670HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5671{
5672#ifndef VBOX_WITH_GUEST_PROPS
5673 ReturnComNotImplemented();
5674#else // VBOX_WITH_GUEST_PROPS
5675 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5676 if (rc == E_ACCESSDENIED)
5677 /* The VM is not running or the service is not (yet) accessible */
5678 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5679 return rc;
5680#endif // VBOX_WITH_GUEST_PROPS
5681}
5682
5683#ifdef VBOX_WITH_GUEST_PROPS
5684/**
5685 * Enumerate the guest properties in VBoxSVC's internal structures.
5686 */
5687HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5688 std::vector<com::Utf8Str> &aNames,
5689 std::vector<com::Utf8Str> &aValues,
5690 std::vector<LONG64> &aTimestamps,
5691 std::vector<com::Utf8Str> &aFlags)
5692{
5693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5694 Utf8Str strPatterns(aPatterns);
5695
5696 /*
5697 * Look for matching patterns and build up a list.
5698 */
5699 HWData::GuestPropertyMap propMap;
5700 for (HWData::GuestPropertyMap::const_iterator
5701 it = mHWData->mGuestProperties.begin();
5702 it != mHWData->mGuestProperties.end();
5703 ++it)
5704 {
5705 if ( strPatterns.isEmpty()
5706 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5707 RTSTR_MAX,
5708 it->first.c_str(),
5709 RTSTR_MAX,
5710 NULL)
5711 )
5712 propMap.insert(*it);
5713 }
5714
5715 alock.release();
5716
5717 /*
5718 * And build up the arrays for returning the property information.
5719 */
5720 size_t cEntries = propMap.size();
5721
5722 aNames.resize(cEntries);
5723 aValues.resize(cEntries);
5724 aTimestamps.resize(cEntries);
5725 aFlags.resize(cEntries);
5726
5727 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5728 size_t i = 0;
5729 for (HWData::GuestPropertyMap::const_iterator
5730 it = propMap.begin();
5731 it != propMap.end();
5732 ++it, ++i)
5733 {
5734 aNames[i] = it->first;
5735 aValues[i] = it->second.strValue;
5736 aTimestamps[i] = it->second.mTimestamp;
5737 GuestPropWriteFlags(it->second.mFlags, szFlags);
5738 aFlags[i] = Utf8Str(szFlags);
5739 }
5740
5741 return S_OK;
5742}
5743
5744/**
5745 * Enumerate the properties managed by a VM.
5746 * @returns E_ACCESSDENIED if the VM process is not available or not
5747 * currently handling queries and the setting should then be done in
5748 * VBoxSVC.
5749 */
5750HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5751 std::vector<com::Utf8Str> &aNames,
5752 std::vector<com::Utf8Str> &aValues,
5753 std::vector<LONG64> &aTimestamps,
5754 std::vector<com::Utf8Str> &aFlags)
5755{
5756 HRESULT rc;
5757 ComPtr<IInternalSessionControl> directControl;
5758 {
5759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5760 if (mData->mSession.mLockType == LockType_VM)
5761 directControl = mData->mSession.mDirectControl;
5762 }
5763
5764 com::SafeArray<BSTR> bNames;
5765 com::SafeArray<BSTR> bValues;
5766 com::SafeArray<LONG64> bTimestamps;
5767 com::SafeArray<BSTR> bFlags;
5768
5769 if (!directControl)
5770 rc = E_ACCESSDENIED;
5771 else
5772 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5773 ComSafeArrayAsOutParam(bNames),
5774 ComSafeArrayAsOutParam(bValues),
5775 ComSafeArrayAsOutParam(bTimestamps),
5776 ComSafeArrayAsOutParam(bFlags));
5777 size_t i;
5778 aNames.resize(bNames.size());
5779 for (i = 0; i < bNames.size(); ++i)
5780 aNames[i] = Utf8Str(bNames[i]);
5781 aValues.resize(bValues.size());
5782 for (i = 0; i < bValues.size(); ++i)
5783 aValues[i] = Utf8Str(bValues[i]);
5784 aTimestamps.resize(bTimestamps.size());
5785 for (i = 0; i < bTimestamps.size(); ++i)
5786 aTimestamps[i] = bTimestamps[i];
5787 aFlags.resize(bFlags.size());
5788 for (i = 0; i < bFlags.size(); ++i)
5789 aFlags[i] = Utf8Str(bFlags[i]);
5790
5791 return rc;
5792}
5793#endif // VBOX_WITH_GUEST_PROPS
5794HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5795 std::vector<com::Utf8Str> &aNames,
5796 std::vector<com::Utf8Str> &aValues,
5797 std::vector<LONG64> &aTimestamps,
5798 std::vector<com::Utf8Str> &aFlags)
5799{
5800#ifndef VBOX_WITH_GUEST_PROPS
5801 ReturnComNotImplemented();
5802#else // VBOX_WITH_GUEST_PROPS
5803
5804 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5805
5806 if (rc == E_ACCESSDENIED)
5807 /* The VM is not running or the service is not (yet) accessible */
5808 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5809 return rc;
5810#endif // VBOX_WITH_GUEST_PROPS
5811}
5812
5813HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5814 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5815{
5816 MediumAttachmentList atts;
5817
5818 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5819 if (FAILED(rc)) return rc;
5820
5821 aMediumAttachments.resize(atts.size());
5822 size_t i = 0;
5823 for (MediumAttachmentList::const_iterator
5824 it = atts.begin();
5825 it != atts.end();
5826 ++it, ++i)
5827 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5828
5829 return S_OK;
5830}
5831
5832HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5833 LONG aControllerPort,
5834 LONG aDevice,
5835 ComPtr<IMediumAttachment> &aAttachment)
5836{
5837 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5838 aName.c_str(), aControllerPort, aDevice));
5839
5840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5841
5842 aAttachment = NULL;
5843
5844 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5845 aName,
5846 aControllerPort,
5847 aDevice);
5848 if (pAttach.isNull())
5849 return setError(VBOX_E_OBJECT_NOT_FOUND,
5850 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5851 aDevice, aControllerPort, aName.c_str());
5852
5853 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5854
5855 return S_OK;
5856}
5857
5858
5859HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5860 StorageBus_T aConnectionType,
5861 ComPtr<IStorageController> &aController)
5862{
5863 if ( (aConnectionType <= StorageBus_Null)
5864 || (aConnectionType > StorageBus_VirtioSCSI))
5865 return setError(E_INVALIDARG,
5866 tr("Invalid connection type: %d"),
5867 aConnectionType);
5868
5869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5870
5871 HRESULT rc = i_checkStateDependency(MutableStateDep);
5872 if (FAILED(rc)) return rc;
5873
5874 /* try to find one with the name first. */
5875 ComObjPtr<StorageController> ctrl;
5876
5877 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5878 if (SUCCEEDED(rc))
5879 return setError(VBOX_E_OBJECT_IN_USE,
5880 tr("Storage controller named '%s' already exists"),
5881 aName.c_str());
5882
5883 ctrl.createObject();
5884
5885 /* get a new instance number for the storage controller */
5886 ULONG ulInstance = 0;
5887 bool fBootable = true;
5888 for (StorageControllerList::const_iterator
5889 it = mStorageControllers->begin();
5890 it != mStorageControllers->end();
5891 ++it)
5892 {
5893 if ((*it)->i_getStorageBus() == aConnectionType)
5894 {
5895 ULONG ulCurInst = (*it)->i_getInstance();
5896
5897 if (ulCurInst >= ulInstance)
5898 ulInstance = ulCurInst + 1;
5899
5900 /* Only one controller of each type can be marked as bootable. */
5901 if ((*it)->i_getBootable())
5902 fBootable = false;
5903 }
5904 }
5905
5906 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5907 if (FAILED(rc)) return rc;
5908
5909 i_setModified(IsModified_Storage);
5910 mStorageControllers.backup();
5911 mStorageControllers->push_back(ctrl);
5912
5913 ctrl.queryInterfaceTo(aController.asOutParam());
5914
5915 /* inform the direct session if any */
5916 alock.release();
5917 i_onStorageControllerChange(i_getId(), aName);
5918
5919 return S_OK;
5920}
5921
5922HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5923 ComPtr<IStorageController> &aStorageController)
5924{
5925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5926
5927 ComObjPtr<StorageController> ctrl;
5928
5929 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5930 if (SUCCEEDED(rc))
5931 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5932
5933 return rc;
5934}
5935
5936HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5937 ULONG aInstance,
5938 ComPtr<IStorageController> &aStorageController)
5939{
5940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5941
5942 for (StorageControllerList::const_iterator
5943 it = mStorageControllers->begin();
5944 it != mStorageControllers->end();
5945 ++it)
5946 {
5947 if ( (*it)->i_getStorageBus() == aConnectionType
5948 && (*it)->i_getInstance() == aInstance)
5949 {
5950 (*it).queryInterfaceTo(aStorageController.asOutParam());
5951 return S_OK;
5952 }
5953 }
5954
5955 return setError(VBOX_E_OBJECT_NOT_FOUND,
5956 tr("Could not find a storage controller with instance number '%lu'"),
5957 aInstance);
5958}
5959
5960HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5961{
5962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5963
5964 HRESULT rc = i_checkStateDependency(MutableStateDep);
5965 if (FAILED(rc)) return rc;
5966
5967 ComObjPtr<StorageController> ctrl;
5968
5969 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5970 if (SUCCEEDED(rc))
5971 {
5972 /* Ensure that only one controller of each type is marked as bootable. */
5973 if (aBootable == TRUE)
5974 {
5975 for (StorageControllerList::const_iterator
5976 it = mStorageControllers->begin();
5977 it != mStorageControllers->end();
5978 ++it)
5979 {
5980 ComObjPtr<StorageController> aCtrl = (*it);
5981
5982 if ( (aCtrl->i_getName() != aName)
5983 && aCtrl->i_getBootable() == TRUE
5984 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5985 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5986 {
5987 aCtrl->i_setBootable(FALSE);
5988 break;
5989 }
5990 }
5991 }
5992
5993 if (SUCCEEDED(rc))
5994 {
5995 ctrl->i_setBootable(aBootable);
5996 i_setModified(IsModified_Storage);
5997 }
5998 }
5999
6000 if (SUCCEEDED(rc))
6001 {
6002 /* inform the direct session if any */
6003 alock.release();
6004 i_onStorageControllerChange(i_getId(), aName);
6005 }
6006
6007 return rc;
6008}
6009
6010HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6011{
6012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6013
6014 HRESULT rc = i_checkStateDependency(MutableStateDep);
6015 if (FAILED(rc)) return rc;
6016
6017 ComObjPtr<StorageController> ctrl;
6018 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6019 if (FAILED(rc)) return rc;
6020
6021 MediumAttachmentList llDetachedAttachments;
6022 {
6023 /* find all attached devices to the appropriate storage controller and detach them all */
6024 // make a temporary list because detachDevice invalidates iterators into
6025 // mMediumAttachments
6026 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6027
6028 for (MediumAttachmentList::const_iterator
6029 it = llAttachments2.begin();
6030 it != llAttachments2.end();
6031 ++it)
6032 {
6033 MediumAttachment *pAttachTemp = *it;
6034
6035 AutoCaller localAutoCaller(pAttachTemp);
6036 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6037
6038 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6039
6040 if (pAttachTemp->i_getControllerName() == aName)
6041 {
6042 llDetachedAttachments.push_back(pAttachTemp);
6043 rc = i_detachDevice(pAttachTemp, alock, NULL);
6044 if (FAILED(rc)) return rc;
6045 }
6046 }
6047 }
6048
6049 /* send event about detached devices before removing parent controller */
6050 for (MediumAttachmentList::const_iterator
6051 it = llDetachedAttachments.begin();
6052 it != llDetachedAttachments.end();
6053 ++it)
6054 {
6055 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6056 }
6057
6058 /* We can remove it now. */
6059 i_setModified(IsModified_Storage);
6060 mStorageControllers.backup();
6061
6062 ctrl->i_unshare();
6063
6064 mStorageControllers->remove(ctrl);
6065
6066 /* inform the direct session if any */
6067 alock.release();
6068 i_onStorageControllerChange(i_getId(), aName);
6069
6070 return S_OK;
6071}
6072
6073HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6074 ComPtr<IUSBController> &aController)
6075{
6076 if ( (aType <= USBControllerType_Null)
6077 || (aType >= USBControllerType_Last))
6078 return setError(E_INVALIDARG,
6079 tr("Invalid USB controller type: %d"),
6080 aType);
6081
6082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 HRESULT rc = i_checkStateDependency(MutableStateDep);
6085 if (FAILED(rc)) return rc;
6086
6087 /* try to find one with the same type first. */
6088 ComObjPtr<USBController> ctrl;
6089
6090 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6091 if (SUCCEEDED(rc))
6092 return setError(VBOX_E_OBJECT_IN_USE,
6093 tr("USB controller named '%s' already exists"),
6094 aName.c_str());
6095
6096 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6097 ULONG maxInstances;
6098 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6099 if (FAILED(rc))
6100 return rc;
6101
6102 ULONG cInstances = i_getUSBControllerCountByType(aType);
6103 if (cInstances >= maxInstances)
6104 return setError(E_INVALIDARG,
6105 tr("Too many USB controllers of this type"));
6106
6107 ctrl.createObject();
6108
6109 rc = ctrl->init(this, aName, aType);
6110 if (FAILED(rc)) return rc;
6111
6112 i_setModified(IsModified_USB);
6113 mUSBControllers.backup();
6114 mUSBControllers->push_back(ctrl);
6115
6116 ctrl.queryInterfaceTo(aController.asOutParam());
6117
6118 /* inform the direct session if any */
6119 alock.release();
6120 i_onUSBControllerChange();
6121
6122 return S_OK;
6123}
6124
6125HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6126{
6127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6128
6129 ComObjPtr<USBController> ctrl;
6130
6131 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6132 if (SUCCEEDED(rc))
6133 ctrl.queryInterfaceTo(aController.asOutParam());
6134
6135 return rc;
6136}
6137
6138HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6139 ULONG *aControllers)
6140{
6141 if ( (aType <= USBControllerType_Null)
6142 || (aType >= USBControllerType_Last))
6143 return setError(E_INVALIDARG,
6144 tr("Invalid USB controller type: %d"),
6145 aType);
6146
6147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6148
6149 ComObjPtr<USBController> ctrl;
6150
6151 *aControllers = i_getUSBControllerCountByType(aType);
6152
6153 return S_OK;
6154}
6155
6156HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6157{
6158
6159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6160
6161 HRESULT rc = i_checkStateDependency(MutableStateDep);
6162 if (FAILED(rc)) return rc;
6163
6164 ComObjPtr<USBController> ctrl;
6165 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6166 if (FAILED(rc)) return rc;
6167
6168 i_setModified(IsModified_USB);
6169 mUSBControllers.backup();
6170
6171 ctrl->i_unshare();
6172
6173 mUSBControllers->remove(ctrl);
6174
6175 /* inform the direct session if any */
6176 alock.release();
6177 i_onUSBControllerChange();
6178
6179 return S_OK;
6180}
6181
6182HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6183 ULONG *aOriginX,
6184 ULONG *aOriginY,
6185 ULONG *aWidth,
6186 ULONG *aHeight,
6187 BOOL *aEnabled)
6188{
6189 uint32_t u32OriginX= 0;
6190 uint32_t u32OriginY= 0;
6191 uint32_t u32Width = 0;
6192 uint32_t u32Height = 0;
6193 uint16_t u16Flags = 0;
6194
6195 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6196 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6197 if (RT_FAILURE(vrc))
6198 {
6199#ifdef RT_OS_WINDOWS
6200 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6201 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6202 * So just assign fEnable to TRUE again.
6203 * The right fix would be to change GUI API wrappers to make sure that parameters
6204 * are changed only if API succeeds.
6205 */
6206 *aEnabled = TRUE;
6207#endif
6208 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6209 tr("Saved guest size is not available (%Rrc)"),
6210 vrc);
6211 }
6212
6213 *aOriginX = u32OriginX;
6214 *aOriginY = u32OriginY;
6215 *aWidth = u32Width;
6216 *aHeight = u32Height;
6217 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6218
6219 return S_OK;
6220}
6221
6222HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6223 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6224{
6225 if (aScreenId != 0)
6226 return E_NOTIMPL;
6227
6228 if ( aBitmapFormat != BitmapFormat_BGR0
6229 && aBitmapFormat != BitmapFormat_BGRA
6230 && aBitmapFormat != BitmapFormat_RGBA
6231 && aBitmapFormat != BitmapFormat_PNG)
6232 return setError(E_NOTIMPL,
6233 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6234
6235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6236
6237 uint8_t *pu8Data = NULL;
6238 uint32_t cbData = 0;
6239 uint32_t u32Width = 0;
6240 uint32_t u32Height = 0;
6241
6242 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6243
6244 if (RT_FAILURE(vrc))
6245 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6246 tr("Saved thumbnail data is not available (%Rrc)"),
6247 vrc);
6248
6249 HRESULT hr = S_OK;
6250
6251 *aWidth = u32Width;
6252 *aHeight = u32Height;
6253
6254 if (cbData > 0)
6255 {
6256 /* Convert pixels to the format expected by the API caller. */
6257 if (aBitmapFormat == BitmapFormat_BGR0)
6258 {
6259 /* [0] B, [1] G, [2] R, [3] 0. */
6260 aData.resize(cbData);
6261 memcpy(&aData.front(), pu8Data, cbData);
6262 }
6263 else if (aBitmapFormat == BitmapFormat_BGRA)
6264 {
6265 /* [0] B, [1] G, [2] R, [3] A. */
6266 aData.resize(cbData);
6267 for (uint32_t i = 0; i < cbData; i += 4)
6268 {
6269 aData[i] = pu8Data[i];
6270 aData[i + 1] = pu8Data[i + 1];
6271 aData[i + 2] = pu8Data[i + 2];
6272 aData[i + 3] = 0xff;
6273 }
6274 }
6275 else if (aBitmapFormat == BitmapFormat_RGBA)
6276 {
6277 /* [0] R, [1] G, [2] B, [3] A. */
6278 aData.resize(cbData);
6279 for (uint32_t i = 0; i < cbData; i += 4)
6280 {
6281 aData[i] = pu8Data[i + 2];
6282 aData[i + 1] = pu8Data[i + 1];
6283 aData[i + 2] = pu8Data[i];
6284 aData[i + 3] = 0xff;
6285 }
6286 }
6287 else if (aBitmapFormat == BitmapFormat_PNG)
6288 {
6289 uint8_t *pu8PNG = NULL;
6290 uint32_t cbPNG = 0;
6291 uint32_t cxPNG = 0;
6292 uint32_t cyPNG = 0;
6293
6294 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6295
6296 if (RT_SUCCESS(vrc))
6297 {
6298 aData.resize(cbPNG);
6299 if (cbPNG)
6300 memcpy(&aData.front(), pu8PNG, cbPNG);
6301 }
6302 else
6303 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6304 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6305 vrc);
6306
6307 RTMemFree(pu8PNG);
6308 }
6309 }
6310
6311 freeSavedDisplayScreenshot(pu8Data);
6312
6313 return hr;
6314}
6315
6316HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6317 ULONG *aWidth,
6318 ULONG *aHeight,
6319 std::vector<BitmapFormat_T> &aBitmapFormats)
6320{
6321 if (aScreenId != 0)
6322 return E_NOTIMPL;
6323
6324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6325
6326 uint8_t *pu8Data = NULL;
6327 uint32_t cbData = 0;
6328 uint32_t u32Width = 0;
6329 uint32_t u32Height = 0;
6330
6331 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6332
6333 if (RT_FAILURE(vrc))
6334 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6335 tr("Saved screenshot data is not available (%Rrc)"),
6336 vrc);
6337
6338 *aWidth = u32Width;
6339 *aHeight = u32Height;
6340 aBitmapFormats.resize(1);
6341 aBitmapFormats[0] = BitmapFormat_PNG;
6342
6343 freeSavedDisplayScreenshot(pu8Data);
6344
6345 return S_OK;
6346}
6347
6348HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6349 BitmapFormat_T aBitmapFormat,
6350 ULONG *aWidth,
6351 ULONG *aHeight,
6352 std::vector<BYTE> &aData)
6353{
6354 if (aScreenId != 0)
6355 return E_NOTIMPL;
6356
6357 if (aBitmapFormat != BitmapFormat_PNG)
6358 return E_NOTIMPL;
6359
6360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6361
6362 uint8_t *pu8Data = NULL;
6363 uint32_t cbData = 0;
6364 uint32_t u32Width = 0;
6365 uint32_t u32Height = 0;
6366
6367 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6368
6369 if (RT_FAILURE(vrc))
6370 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6371 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6372 vrc);
6373
6374 *aWidth = u32Width;
6375 *aHeight = u32Height;
6376
6377 aData.resize(cbData);
6378 if (cbData)
6379 memcpy(&aData.front(), pu8Data, cbData);
6380
6381 freeSavedDisplayScreenshot(pu8Data);
6382
6383 return S_OK;
6384}
6385
6386HRESULT Machine::hotPlugCPU(ULONG aCpu)
6387{
6388 HRESULT rc = S_OK;
6389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6390
6391 if (!mHWData->mCPUHotPlugEnabled)
6392 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6393
6394 if (aCpu >= mHWData->mCPUCount)
6395 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6396
6397 if (mHWData->mCPUAttached[aCpu])
6398 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6399
6400 alock.release();
6401 rc = i_onCPUChange(aCpu, false);
6402 alock.acquire();
6403 if (FAILED(rc)) return rc;
6404
6405 i_setModified(IsModified_MachineData);
6406 mHWData.backup();
6407 mHWData->mCPUAttached[aCpu] = true;
6408
6409 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6410 if (Global::IsOnline(mData->mMachineState))
6411 i_saveSettings(NULL, alock);
6412
6413 return S_OK;
6414}
6415
6416HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6417{
6418 HRESULT rc = S_OK;
6419
6420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6421
6422 if (!mHWData->mCPUHotPlugEnabled)
6423 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6424
6425 if (aCpu >= SchemaDefs::MaxCPUCount)
6426 return setError(E_INVALIDARG,
6427 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6428 SchemaDefs::MaxCPUCount);
6429
6430 if (!mHWData->mCPUAttached[aCpu])
6431 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6432
6433 /* CPU 0 can't be detached */
6434 if (aCpu == 0)
6435 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6436
6437 alock.release();
6438 rc = i_onCPUChange(aCpu, true);
6439 alock.acquire();
6440 if (FAILED(rc)) return rc;
6441
6442 i_setModified(IsModified_MachineData);
6443 mHWData.backup();
6444 mHWData->mCPUAttached[aCpu] = false;
6445
6446 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6447 if (Global::IsOnline(mData->mMachineState))
6448 i_saveSettings(NULL, alock);
6449
6450 return S_OK;
6451}
6452
6453HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6454{
6455 *aAttached = false;
6456
6457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 /* If hotplug is enabled the CPU is always enabled. */
6460 if (!mHWData->mCPUHotPlugEnabled)
6461 {
6462 if (aCpu < mHWData->mCPUCount)
6463 *aAttached = true;
6464 }
6465 else
6466 {
6467 if (aCpu < SchemaDefs::MaxCPUCount)
6468 *aAttached = mHWData->mCPUAttached[aCpu];
6469 }
6470
6471 return S_OK;
6472}
6473
6474HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6475{
6476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6477
6478 Utf8Str log = i_getLogFilename(aIdx);
6479 if (!RTFileExists(log.c_str()))
6480 log.setNull();
6481 aFilename = log;
6482
6483 return S_OK;
6484}
6485
6486HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6487{
6488 if (aSize < 0)
6489 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6490
6491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6492
6493 HRESULT rc = S_OK;
6494 Utf8Str log = i_getLogFilename(aIdx);
6495
6496 /* do not unnecessarily hold the lock while doing something which does
6497 * not need the lock and potentially takes a long time. */
6498 alock.release();
6499
6500 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6501 * keeps the SOAP reply size under 1M for the webservice (we're using
6502 * base64 encoded strings for binary data for years now, avoiding the
6503 * expansion of each byte array element to approx. 25 bytes of XML. */
6504 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6505 aData.resize(cbData);
6506
6507 RTFILE LogFile;
6508 int vrc = RTFileOpen(&LogFile, log.c_str(),
6509 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6510 if (RT_SUCCESS(vrc))
6511 {
6512 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6513 if (RT_SUCCESS(vrc))
6514 aData.resize(cbData);
6515 else
6516 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6517 tr("Could not read log file '%s' (%Rrc)"),
6518 log.c_str(), vrc);
6519 RTFileClose(LogFile);
6520 }
6521 else
6522 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6523 tr("Could not open log file '%s' (%Rrc)"),
6524 log.c_str(), vrc);
6525
6526 if (FAILED(rc))
6527 aData.resize(0);
6528
6529 return rc;
6530}
6531
6532
6533/**
6534 * Currently this method doesn't attach device to the running VM,
6535 * just makes sure it's plugged on next VM start.
6536 */
6537HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6538{
6539 // lock scope
6540 {
6541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6542
6543 HRESULT rc = i_checkStateDependency(MutableStateDep);
6544 if (FAILED(rc)) return rc;
6545
6546 ChipsetType_T aChipset = ChipsetType_PIIX3;
6547 COMGETTER(ChipsetType)(&aChipset);
6548
6549 if (aChipset != ChipsetType_ICH9)
6550 {
6551 return setError(E_INVALIDARG,
6552 tr("Host PCI attachment only supported with ICH9 chipset"));
6553 }
6554
6555 // check if device with this host PCI address already attached
6556 for (HWData::PCIDeviceAssignmentList::const_iterator
6557 it = mHWData->mPCIDeviceAssignments.begin();
6558 it != mHWData->mPCIDeviceAssignments.end();
6559 ++it)
6560 {
6561 LONG iHostAddress = -1;
6562 ComPtr<PCIDeviceAttachment> pAttach;
6563 pAttach = *it;
6564 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6565 if (iHostAddress == aHostAddress)
6566 return setError(E_INVALIDARG,
6567 tr("Device with host PCI address already attached to this VM"));
6568 }
6569
6570 ComObjPtr<PCIDeviceAttachment> pda;
6571 char name[32];
6572
6573 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6574 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6575 pda.createObject();
6576 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6577 i_setModified(IsModified_MachineData);
6578 mHWData.backup();
6579 mHWData->mPCIDeviceAssignments.push_back(pda);
6580 }
6581
6582 return S_OK;
6583}
6584
6585/**
6586 * Currently this method doesn't detach device from the running VM,
6587 * just makes sure it's not plugged on next VM start.
6588 */
6589HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6590{
6591 ComObjPtr<PCIDeviceAttachment> pAttach;
6592 bool fRemoved = false;
6593 HRESULT rc;
6594
6595 // lock scope
6596 {
6597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6598
6599 rc = i_checkStateDependency(MutableStateDep);
6600 if (FAILED(rc)) return rc;
6601
6602 for (HWData::PCIDeviceAssignmentList::const_iterator
6603 it = mHWData->mPCIDeviceAssignments.begin();
6604 it != mHWData->mPCIDeviceAssignments.end();
6605 ++it)
6606 {
6607 LONG iHostAddress = -1;
6608 pAttach = *it;
6609 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6610 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6611 {
6612 i_setModified(IsModified_MachineData);
6613 mHWData.backup();
6614 mHWData->mPCIDeviceAssignments.remove(pAttach);
6615 fRemoved = true;
6616 break;
6617 }
6618 }
6619 }
6620
6621
6622 /* Fire event outside of the lock */
6623 if (fRemoved)
6624 {
6625 Assert(!pAttach.isNull());
6626 ComPtr<IEventSource> es;
6627 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6628 Assert(SUCCEEDED(rc));
6629 Bstr mid;
6630 rc = this->COMGETTER(Id)(mid.asOutParam());
6631 Assert(SUCCEEDED(rc));
6632 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6633 }
6634
6635 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6636 tr("No host PCI device %08x attached"),
6637 aHostAddress
6638 );
6639}
6640
6641HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6642{
6643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6644
6645 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6646 size_t i = 0;
6647 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6648 it = mHWData->mPCIDeviceAssignments.begin();
6649 it != mHWData->mPCIDeviceAssignments.end();
6650 ++it, ++i)
6651 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6652
6653 return S_OK;
6654}
6655
6656HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6657{
6658 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6659
6660 return S_OK;
6661}
6662
6663HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6664{
6665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6666
6667 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6668
6669 return S_OK;
6670}
6671
6672HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6673{
6674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6675 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6676 if (SUCCEEDED(hrc))
6677 {
6678 hrc = mHWData.backupEx();
6679 if (SUCCEEDED(hrc))
6680 {
6681 i_setModified(IsModified_MachineData);
6682 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6683 }
6684 }
6685 return hrc;
6686}
6687
6688HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6689{
6690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6691 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6692 return S_OK;
6693}
6694
6695HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6696{
6697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6698 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6699 if (SUCCEEDED(hrc))
6700 {
6701 hrc = mHWData.backupEx();
6702 if (SUCCEEDED(hrc))
6703 {
6704 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6705 if (SUCCEEDED(hrc))
6706 i_setModified(IsModified_MachineData);
6707 }
6708 }
6709 return hrc;
6710}
6711
6712HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6713{
6714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6715
6716 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6717
6718 return S_OK;
6719}
6720
6721HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6722{
6723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6724 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6725 if (SUCCEEDED(hrc))
6726 {
6727 hrc = mHWData.backupEx();
6728 if (SUCCEEDED(hrc))
6729 {
6730 i_setModified(IsModified_MachineData);
6731 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6732 }
6733 }
6734 return hrc;
6735}
6736
6737HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6738{
6739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6740
6741 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6742
6743 return S_OK;
6744}
6745
6746HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6747{
6748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6749
6750 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6751 if ( SUCCEEDED(hrc)
6752 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6753 {
6754 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6755 int vrc;
6756
6757 if (aAutostartEnabled)
6758 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6759 else
6760 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6761
6762 if (RT_SUCCESS(vrc))
6763 {
6764 hrc = mHWData.backupEx();
6765 if (SUCCEEDED(hrc))
6766 {
6767 i_setModified(IsModified_MachineData);
6768 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6769 }
6770 }
6771 else if (vrc == VERR_NOT_SUPPORTED)
6772 hrc = setError(VBOX_E_NOT_SUPPORTED,
6773 tr("The VM autostart feature is not supported on this platform"));
6774 else if (vrc == VERR_PATH_NOT_FOUND)
6775 hrc = setError(E_FAIL,
6776 tr("The path to the autostart database is not set"));
6777 else
6778 hrc = setError(E_UNEXPECTED,
6779 aAutostartEnabled ?
6780 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6781 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6782 mUserData->s.strName.c_str(), vrc);
6783 }
6784 return hrc;
6785}
6786
6787HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6788{
6789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6790
6791 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6792
6793 return S_OK;
6794}
6795
6796HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6797{
6798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6800 if (SUCCEEDED(hrc))
6801 {
6802 hrc = mHWData.backupEx();
6803 if (SUCCEEDED(hrc))
6804 {
6805 i_setModified(IsModified_MachineData);
6806 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6807 }
6808 }
6809 return hrc;
6810}
6811
6812HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6813{
6814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6815
6816 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6817
6818 return S_OK;
6819}
6820
6821HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6822{
6823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6824 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6825 if ( SUCCEEDED(hrc)
6826 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6827 {
6828 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6829 int vrc;
6830
6831 if (aAutostopType != AutostopType_Disabled)
6832 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6833 else
6834 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6835
6836 if (RT_SUCCESS(vrc))
6837 {
6838 hrc = mHWData.backupEx();
6839 if (SUCCEEDED(hrc))
6840 {
6841 i_setModified(IsModified_MachineData);
6842 mHWData->mAutostart.enmAutostopType = aAutostopType;
6843 }
6844 }
6845 else if (vrc == VERR_NOT_SUPPORTED)
6846 hrc = setError(VBOX_E_NOT_SUPPORTED,
6847 tr("The VM autostop feature is not supported on this platform"));
6848 else if (vrc == VERR_PATH_NOT_FOUND)
6849 hrc = setError(E_FAIL,
6850 tr("The path to the autostart database is not set"));
6851 else
6852 hrc = setError(E_UNEXPECTED,
6853 aAutostopType != AutostopType_Disabled ?
6854 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6855 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6856 mUserData->s.strName.c_str(), vrc);
6857 }
6858 return hrc;
6859}
6860
6861HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6862{
6863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6864
6865 aDefaultFrontend = mHWData->mDefaultFrontend;
6866
6867 return S_OK;
6868}
6869
6870HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6871{
6872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6873 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6874 if (SUCCEEDED(hrc))
6875 {
6876 hrc = mHWData.backupEx();
6877 if (SUCCEEDED(hrc))
6878 {
6879 i_setModified(IsModified_MachineData);
6880 mHWData->mDefaultFrontend = aDefaultFrontend;
6881 }
6882 }
6883 return hrc;
6884}
6885
6886HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6887{
6888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6889 size_t cbIcon = mUserData->s.ovIcon.size();
6890 aIcon.resize(cbIcon);
6891 if (cbIcon)
6892 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6893 return S_OK;
6894}
6895
6896HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6897{
6898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6899 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6900 if (SUCCEEDED(hrc))
6901 {
6902 i_setModified(IsModified_MachineData);
6903 mUserData.backup();
6904 size_t cbIcon = aIcon.size();
6905 mUserData->s.ovIcon.resize(cbIcon);
6906 if (cbIcon)
6907 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6908 }
6909 return hrc;
6910}
6911
6912HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6913{
6914#ifdef VBOX_WITH_USB
6915 *aUSBProxyAvailable = true;
6916#else
6917 *aUSBProxyAvailable = false;
6918#endif
6919 return S_OK;
6920}
6921
6922HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6923{
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925
6926 *aVMProcessPriority = mUserData->s.enmVMPriority;
6927
6928 return S_OK;
6929}
6930
6931HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6932{
6933 RT_NOREF(aVMProcessPriority);
6934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6935 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6936 if (SUCCEEDED(hrc))
6937 {
6938 hrc = mUserData.backupEx();
6939 if (SUCCEEDED(hrc))
6940 {
6941 i_setModified(IsModified_MachineData);
6942 mUserData->s.enmVMPriority = aVMProcessPriority;
6943 }
6944 }
6945 alock.release();
6946 if (SUCCEEDED(hrc))
6947 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6948 return hrc;
6949}
6950
6951HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6952 ComPtr<IProgress> &aProgress)
6953{
6954 ComObjPtr<Progress> pP;
6955 Progress *ppP = pP;
6956 IProgress *iP = static_cast<IProgress *>(ppP);
6957 IProgress **pProgress = &iP;
6958
6959 IMachine *pTarget = aTarget;
6960
6961 /* Convert the options. */
6962 RTCList<CloneOptions_T> optList;
6963 if (aOptions.size())
6964 for (size_t i = 0; i < aOptions.size(); ++i)
6965 optList.append(aOptions[i]);
6966
6967 if (optList.contains(CloneOptions_Link))
6968 {
6969 if (!i_isSnapshotMachine())
6970 return setError(E_INVALIDARG,
6971 tr("Linked clone can only be created from a snapshot"));
6972 if (aMode != CloneMode_MachineState)
6973 return setError(E_INVALIDARG,
6974 tr("Linked clone can only be created for a single machine state"));
6975 }
6976 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6977
6978 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6979
6980 HRESULT rc = pWorker->start(pProgress);
6981
6982 pP = static_cast<Progress *>(*pProgress);
6983 pP.queryInterfaceTo(aProgress.asOutParam());
6984
6985 return rc;
6986
6987}
6988
6989HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6990 const com::Utf8Str &aType,
6991 ComPtr<IProgress> &aProgress)
6992{
6993 LogFlowThisFuncEnter();
6994
6995 ComObjPtr<Progress> ptrProgress;
6996 HRESULT hrc = ptrProgress.createObject();
6997 if (SUCCEEDED(hrc))
6998 {
6999 com::Utf8Str strDefaultPath;
7000 if (aTargetPath.isEmpty())
7001 i_calculateFullPath(".", strDefaultPath);
7002
7003 /* Initialize our worker task */
7004 MachineMoveVM *pTask = NULL;
7005 try
7006 {
7007 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7008 }
7009 catch (std::bad_alloc &)
7010 {
7011 return E_OUTOFMEMORY;
7012 }
7013
7014 hrc = pTask->init();//no exceptions are thrown
7015
7016 if (SUCCEEDED(hrc))
7017 {
7018 hrc = pTask->createThread();
7019 pTask = NULL; /* Consumed by createThread(). */
7020 if (SUCCEEDED(hrc))
7021 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7022 else
7023 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7024 }
7025 else
7026 delete pTask;
7027 }
7028
7029 LogFlowThisFuncLeave();
7030 return hrc;
7031
7032}
7033
7034HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7035{
7036 NOREF(aProgress);
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038
7039 // This check should always fail.
7040 HRESULT rc = i_checkStateDependency(MutableStateDep);
7041 if (FAILED(rc)) return rc;
7042
7043 AssertFailedReturn(E_NOTIMPL);
7044}
7045
7046HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7047{
7048 NOREF(aSavedStateFile);
7049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7050
7051 // This check should always fail.
7052 HRESULT rc = i_checkStateDependency(MutableStateDep);
7053 if (FAILED(rc)) return rc;
7054
7055 AssertFailedReturn(E_NOTIMPL);
7056}
7057
7058HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7059{
7060 NOREF(aFRemoveFile);
7061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7062
7063 // This check should always fail.
7064 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7065 if (FAILED(rc)) return rc;
7066
7067 AssertFailedReturn(E_NOTIMPL);
7068}
7069
7070// public methods for internal purposes
7071/////////////////////////////////////////////////////////////////////////////
7072
7073/**
7074 * Adds the given IsModified_* flag to the dirty flags of the machine.
7075 * This must be called either during i_loadSettings or under the machine write lock.
7076 * @param fl Flag
7077 * @param fAllowStateModification If state modifications are allowed.
7078 */
7079void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7080{
7081 mData->flModifications |= fl;
7082 if (fAllowStateModification && i_isStateModificationAllowed())
7083 mData->mCurrentStateModified = true;
7084}
7085
7086/**
7087 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7088 * care of the write locking.
7089 *
7090 * @param fModification The flag to add.
7091 * @param fAllowStateModification If state modifications are allowed.
7092 */
7093void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7094{
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096 i_setModified(fModification, fAllowStateModification);
7097}
7098
7099/**
7100 * Saves the registry entry of this machine to the given configuration node.
7101 *
7102 * @param data Machine registry data.
7103 *
7104 * @note locks this object for reading.
7105 */
7106HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7107{
7108 AutoLimitedCaller autoCaller(this);
7109 AssertComRCReturnRC(autoCaller.rc());
7110
7111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7112
7113 data.uuid = mData->mUuid;
7114 data.strSettingsFile = mData->m_strConfigFile;
7115
7116 return S_OK;
7117}
7118
7119/**
7120 * Calculates the absolute path of the given path taking the directory of the
7121 * machine settings file as the current directory.
7122 *
7123 * @param strPath Path to calculate the absolute path for.
7124 * @param aResult Where to put the result (used only on success, can be the
7125 * same Utf8Str instance as passed in @a aPath).
7126 * @return IPRT result.
7127 *
7128 * @note Locks this object for reading.
7129 */
7130int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7131{
7132 AutoCaller autoCaller(this);
7133 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7134
7135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7136
7137 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7138
7139 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7140
7141 strSettingsDir.stripFilename();
7142 char szFolder[RTPATH_MAX];
7143 size_t cbFolder = sizeof(szFolder);
7144 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7145 if (RT_SUCCESS(vrc))
7146 aResult = szFolder;
7147
7148 return vrc;
7149}
7150
7151/**
7152 * Copies strSource to strTarget, making it relative to the machine folder
7153 * if it is a subdirectory thereof, or simply copying it otherwise.
7154 *
7155 * @param strSource Path to evaluate and copy.
7156 * @param strTarget Buffer to receive target path.
7157 *
7158 * @note Locks this object for reading.
7159 */
7160void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7161 Utf8Str &strTarget)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturn(autoCaller.rc(), (void)0);
7165
7166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7167
7168 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7169 // use strTarget as a temporary buffer to hold the machine settings dir
7170 strTarget = mData->m_strConfigFileFull;
7171 strTarget.stripFilename();
7172 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7173 {
7174 // is relative: then append what's left
7175 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7176 // for empty paths (only possible for subdirs) use "." to avoid
7177 // triggering default settings for not present config attributes.
7178 if (strTarget.isEmpty())
7179 strTarget = ".";
7180 }
7181 else
7182 // is not relative: then overwrite
7183 strTarget = strSource;
7184}
7185
7186/**
7187 * Returns the full path to the machine's log folder in the
7188 * \a aLogFolder argument.
7189 */
7190void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7191{
7192 AutoCaller autoCaller(this);
7193 AssertComRCReturnVoid(autoCaller.rc());
7194
7195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 char szTmp[RTPATH_MAX];
7198 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7199 if (RT_SUCCESS(vrc))
7200 {
7201 if (szTmp[0] && !mUserData.isNull())
7202 {
7203 char szTmp2[RTPATH_MAX];
7204 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7205 if (RT_SUCCESS(vrc))
7206 aLogFolder.printf("%s%c%s",
7207 szTmp2,
7208 RTPATH_DELIMITER,
7209 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7210 }
7211 else
7212 vrc = VERR_PATH_IS_RELATIVE;
7213 }
7214
7215 if (RT_FAILURE(vrc))
7216 {
7217 // fallback if VBOX_USER_LOGHOME is not set or invalid
7218 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7219 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7220 aLogFolder.append(RTPATH_DELIMITER);
7221 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7222 }
7223}
7224
7225/**
7226 * Returns the full path to the machine's log file for an given index.
7227 */
7228Utf8Str Machine::i_getLogFilename(ULONG idx)
7229{
7230 Utf8Str logFolder;
7231 getLogFolder(logFolder);
7232 Assert(logFolder.length());
7233
7234 Utf8Str log;
7235 if (idx == 0)
7236 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7237#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7238 else if (idx == 1)
7239 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7240 else
7241 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7242#else
7243 else
7244 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7245#endif
7246 return log;
7247}
7248
7249/**
7250 * Returns the full path to the machine's hardened log file.
7251 */
7252Utf8Str Machine::i_getHardeningLogFilename(void)
7253{
7254 Utf8Str strFilename;
7255 getLogFolder(strFilename);
7256 Assert(strFilename.length());
7257 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7258 return strFilename;
7259}
7260
7261/**
7262 * Returns the default NVRAM filename based on the location of the VM config.
7263 * Note that this is a relative path.
7264 */
7265Utf8Str Machine::i_getDefaultNVRAMFilename()
7266{
7267 AutoCaller autoCaller(this);
7268 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7269
7270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7271
7272 if (i_isSnapshotMachine())
7273 return Utf8Str::Empty;
7274
7275 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7276 strNVRAMFilePath.stripPath();
7277 strNVRAMFilePath.stripSuffix();
7278 strNVRAMFilePath += ".nvram";
7279
7280 return strNVRAMFilePath;
7281}
7282
7283/**
7284 * Returns the NVRAM filename for a new snapshot. This intentionally works
7285 * similarly to the saved state file naming. Note that this is usually
7286 * a relative path, unless the snapshot folder is absolute.
7287 */
7288Utf8Str Machine::i_getSnapshotNVRAMFilename()
7289{
7290 AutoCaller autoCaller(this);
7291 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7292
7293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7294
7295 RTTIMESPEC ts;
7296 RTTimeNow(&ts);
7297 RTTIME time;
7298 RTTimeExplode(&time, &ts);
7299
7300 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7301 strNVRAMFilePath += RTPATH_DELIMITER;
7302 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7303 time.i32Year, time.u8Month, time.u8MonthDay,
7304 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7305
7306 return strNVRAMFilePath;
7307}
7308
7309/**
7310 * Returns the version of the settings file.
7311 */
7312SettingsVersion_T Machine::i_getSettingsVersion(void)
7313{
7314 AutoCaller autoCaller(this);
7315 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7316
7317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7318
7319 return mData->pMachineConfigFile->getSettingsVersion();
7320}
7321
7322/**
7323 * Composes a unique saved state filename based on the current system time. The filename is
7324 * granular to the second so this will work so long as no more than one snapshot is taken on
7325 * a machine per second.
7326 *
7327 * Before version 4.1, we used this formula for saved state files:
7328 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7329 * which no longer works because saved state files can now be shared between the saved state of the
7330 * "saved" machine and an online snapshot, and the following would cause problems:
7331 * 1) save machine
7332 * 2) create online snapshot from that machine state --> reusing saved state file
7333 * 3) save machine again --> filename would be reused, breaking the online snapshot
7334 *
7335 * So instead we now use a timestamp.
7336 *
7337 * @param strStateFilePath
7338 */
7339
7340void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7341{
7342 AutoCaller autoCaller(this);
7343 AssertComRCReturnVoid(autoCaller.rc());
7344
7345 {
7346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7347 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7348 }
7349
7350 RTTIMESPEC ts;
7351 RTTimeNow(&ts);
7352 RTTIME time;
7353 RTTimeExplode(&time, &ts);
7354
7355 strStateFilePath += RTPATH_DELIMITER;
7356 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7357 time.i32Year, time.u8Month, time.u8MonthDay,
7358 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7359}
7360
7361/**
7362 * Returns whether at least one USB controller is present for the VM.
7363 */
7364bool Machine::i_isUSBControllerPresent()
7365{
7366 AutoCaller autoCaller(this);
7367 AssertComRCReturn(autoCaller.rc(), false);
7368
7369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7370
7371 return (mUSBControllers->size() > 0);
7372}
7373
7374
7375/**
7376 * @note Locks this object for writing, calls the client process
7377 * (inside the lock).
7378 */
7379HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7380 const Utf8Str &strFrontend,
7381 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7382 ProgressProxy *aProgress)
7383{
7384 LogFlowThisFuncEnter();
7385
7386 AssertReturn(aControl, E_FAIL);
7387 AssertReturn(aProgress, E_FAIL);
7388 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7389
7390 AutoCaller autoCaller(this);
7391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7392
7393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7394
7395 if (!mData->mRegistered)
7396 return setError(E_UNEXPECTED,
7397 tr("The machine '%s' is not registered"),
7398 mUserData->s.strName.c_str());
7399
7400 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7401
7402 /* The process started when launching a VM with separate UI/VM processes is always
7403 * the UI process, i.e. needs special handling as it won't claim the session. */
7404 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7405
7406 if (fSeparate)
7407 {
7408 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7409 return setError(VBOX_E_INVALID_OBJECT_STATE,
7410 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7411 mUserData->s.strName.c_str());
7412 }
7413 else
7414 {
7415 if ( mData->mSession.mState == SessionState_Locked
7416 || mData->mSession.mState == SessionState_Spawning
7417 || mData->mSession.mState == SessionState_Unlocking)
7418 return setError(VBOX_E_INVALID_OBJECT_STATE,
7419 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7420 mUserData->s.strName.c_str());
7421
7422 /* may not be busy */
7423 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7424 }
7425
7426 /* Hardening logging */
7427#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7428 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7429 {
7430 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7431 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7432 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7433 {
7434 Utf8Str strStartupLogDir = strHardeningLogFile;
7435 strStartupLogDir.stripFilename();
7436 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7437 file without stripping the file. */
7438 }
7439 strSupHardeningLogArg.append(strHardeningLogFile);
7440
7441 /* Remove legacy log filename to avoid confusion. */
7442 Utf8Str strOldStartupLogFile;
7443 getLogFolder(strOldStartupLogFile);
7444 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7445 RTFileDelete(strOldStartupLogFile.c_str());
7446 }
7447#else
7448 Utf8Str strSupHardeningLogArg;
7449#endif
7450
7451 Utf8Str strAppOverride;
7452#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7453 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7454#endif
7455
7456 bool fUseVBoxSDS = false;
7457 Utf8Str strCanonicalName;
7458 if (false)
7459 { }
7460#ifdef VBOX_WITH_QTGUI
7461 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7462 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7463 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7464 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7465 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7466 {
7467 strCanonicalName = "GUI/Qt";
7468 fUseVBoxSDS = true;
7469 }
7470#endif
7471#ifdef VBOX_WITH_VBOXSDL
7472 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7473 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7474 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7475 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7476 {
7477 strCanonicalName = "GUI/SDL";
7478 fUseVBoxSDS = true;
7479 }
7480#endif
7481#ifdef VBOX_WITH_HEADLESS
7482 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7483 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7484 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7485 {
7486 strCanonicalName = "headless";
7487 }
7488#endif
7489 else
7490 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7491
7492 Utf8Str idStr = mData->mUuid.toString();
7493 Utf8Str const &strMachineName = mUserData->s.strName;
7494 RTPROCESS pid = NIL_RTPROCESS;
7495
7496#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7497 RT_NOREF(fUseVBoxSDS);
7498#else
7499 DWORD idCallerSession = ~(DWORD)0;
7500 if (fUseVBoxSDS)
7501 {
7502 /*
7503 * The VBoxSDS should be used for process launching the VM with
7504 * GUI only if the caller and the VBoxSDS are in different Windows
7505 * sessions and the caller in the interactive one.
7506 */
7507 fUseVBoxSDS = false;
7508
7509 /* Get windows session of the current process. The process token used
7510 due to several reasons:
7511 1. The token is absent for the current thread except someone set it
7512 for us.
7513 2. Needs to get the id of the session where the process is started.
7514 We only need to do this once, though. */
7515 static DWORD s_idCurrentSession = ~(DWORD)0;
7516 DWORD idCurrentSession = s_idCurrentSession;
7517 if (idCurrentSession == ~(DWORD)0)
7518 {
7519 HANDLE hCurrentProcessToken = NULL;
7520 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7521 {
7522 DWORD cbIgn = 0;
7523 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7524 s_idCurrentSession = idCurrentSession;
7525 else
7526 {
7527 idCurrentSession = ~(DWORD)0;
7528 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7529 }
7530 CloseHandle(hCurrentProcessToken);
7531 }
7532 else
7533 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7534 }
7535
7536 /* get the caller's session */
7537 HRESULT hrc = CoImpersonateClient();
7538 if (SUCCEEDED(hrc))
7539 {
7540 HANDLE hCallerThreadToken;
7541 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7542 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7543 &hCallerThreadToken))
7544 {
7545 SetLastError(NO_ERROR);
7546 DWORD cbIgn = 0;
7547 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7548 {
7549 /* Only need to use SDS if the session ID differs: */
7550 if (idCurrentSession != idCallerSession)
7551 {
7552 fUseVBoxSDS = false;
7553
7554 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7555 DWORD cbTokenGroups = 0;
7556 PTOKEN_GROUPS pTokenGroups = NULL;
7557 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7558 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7559 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7560 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7561 {
7562 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7563 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7564 PSID pInteractiveSid = NULL;
7565 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7566 {
7567 /* Iterate over the groups looking for the interactive SID: */
7568 fUseVBoxSDS = false;
7569 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7570 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7571 {
7572 fUseVBoxSDS = true;
7573 break;
7574 }
7575 FreeSid(pInteractiveSid);
7576 }
7577 }
7578 else
7579 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7580 RTMemTmpFree(pTokenGroups);
7581 }
7582 }
7583 else
7584 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7585 CloseHandle(hCallerThreadToken);
7586 }
7587 else
7588 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7589 CoRevertToSelf();
7590 }
7591 else
7592 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7593 }
7594 if (fUseVBoxSDS)
7595 {
7596 /* connect to VBoxSDS */
7597 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7598 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7599 if (FAILED(rc))
7600 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7601 strMachineName.c_str());
7602
7603 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7604 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7605 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7606 service to access the files. */
7607 rc = CoSetProxyBlanket(pVBoxSDS,
7608 RPC_C_AUTHN_DEFAULT,
7609 RPC_C_AUTHZ_DEFAULT,
7610 COLE_DEFAULT_PRINCIPAL,
7611 RPC_C_AUTHN_LEVEL_DEFAULT,
7612 RPC_C_IMP_LEVEL_IMPERSONATE,
7613 NULL,
7614 EOAC_DEFAULT);
7615 if (FAILED(rc))
7616 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7617
7618 size_t const cEnvVars = aEnvironmentChanges.size();
7619 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7620 for (size_t i = 0; i < cEnvVars; i++)
7621 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7622
7623 ULONG uPid = 0;
7624 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7625 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7626 idCallerSession, &uPid);
7627 if (FAILED(rc))
7628 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7629 pid = (RTPROCESS)uPid;
7630 }
7631 else
7632#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7633 {
7634 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7635 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7636 if (RT_FAILURE(vrc))
7637 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7638 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7639 }
7640
7641 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7642 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7643
7644 if (!fSeparate)
7645 {
7646 /*
7647 * Note that we don't release the lock here before calling the client,
7648 * because it doesn't need to call us back if called with a NULL argument.
7649 * Releasing the lock here is dangerous because we didn't prepare the
7650 * launch data yet, but the client we've just started may happen to be
7651 * too fast and call LockMachine() that will fail (because of PID, etc.),
7652 * so that the Machine will never get out of the Spawning session state.
7653 */
7654
7655 /* inform the session that it will be a remote one */
7656 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7657#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7658 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7659#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7660 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7661#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7662 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7663
7664 if (FAILED(rc))
7665 {
7666 /* restore the session state */
7667 mData->mSession.mState = SessionState_Unlocked;
7668 alock.release();
7669 mParent->i_addProcessToReap(pid);
7670 /* The failure may occur w/o any error info (from RPC), so provide one */
7671 return setError(VBOX_E_VM_ERROR,
7672 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7673 }
7674
7675 /* attach launch data to the machine */
7676 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7677 mData->mSession.mRemoteControls.push_back(aControl);
7678 mData->mSession.mProgress = aProgress;
7679 mData->mSession.mPID = pid;
7680 mData->mSession.mState = SessionState_Spawning;
7681 Assert(strCanonicalName.isNotEmpty());
7682 mData->mSession.mName = strCanonicalName;
7683 }
7684 else
7685 {
7686 /* For separate UI process we declare the launch as completed instantly, as the
7687 * actual headless VM start may or may not come. No point in remembering anything
7688 * yet, as what matters for us is when the headless VM gets started. */
7689 aProgress->i_notifyComplete(S_OK);
7690 }
7691
7692 alock.release();
7693 mParent->i_addProcessToReap(pid);
7694
7695 LogFlowThisFuncLeave();
7696 return S_OK;
7697}
7698
7699/**
7700 * Returns @c true if the given session machine instance has an open direct
7701 * session (and optionally also for direct sessions which are closing) and
7702 * returns the session control machine instance if so.
7703 *
7704 * Note that when the method returns @c false, the arguments remain unchanged.
7705 *
7706 * @param aMachine Session machine object.
7707 * @param aControl Direct session control object (optional).
7708 * @param aRequireVM If true then only allow VM sessions.
7709 * @param aAllowClosing If true then additionally a session which is currently
7710 * being closed will also be allowed.
7711 *
7712 * @note locks this object for reading.
7713 */
7714bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7715 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7716 bool aRequireVM /*= false*/,
7717 bool aAllowClosing /*= false*/)
7718{
7719 AutoLimitedCaller autoCaller(this);
7720 AssertComRCReturn(autoCaller.rc(), false);
7721
7722 /* just return false for inaccessible machines */
7723 if (getObjectState().getState() != ObjectState::Ready)
7724 return false;
7725
7726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7727
7728 if ( ( mData->mSession.mState == SessionState_Locked
7729 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7730 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7731 )
7732 {
7733 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7734
7735 aMachine = mData->mSession.mMachine;
7736
7737 if (aControl != NULL)
7738 *aControl = mData->mSession.mDirectControl;
7739
7740 return true;
7741 }
7742
7743 return false;
7744}
7745
7746/**
7747 * Returns @c true if the given machine has an spawning direct session.
7748 *
7749 * @note locks this object for reading.
7750 */
7751bool Machine::i_isSessionSpawning()
7752{
7753 AutoLimitedCaller autoCaller(this);
7754 AssertComRCReturn(autoCaller.rc(), false);
7755
7756 /* just return false for inaccessible machines */
7757 if (getObjectState().getState() != ObjectState::Ready)
7758 return false;
7759
7760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7761
7762 if (mData->mSession.mState == SessionState_Spawning)
7763 return true;
7764
7765 return false;
7766}
7767
7768/**
7769 * Called from the client watcher thread to check for unexpected client process
7770 * death during Session_Spawning state (e.g. before it successfully opened a
7771 * direct session).
7772 *
7773 * On Win32 and on OS/2, this method is called only when we've got the
7774 * direct client's process termination notification, so it always returns @c
7775 * true.
7776 *
7777 * On other platforms, this method returns @c true if the client process is
7778 * terminated and @c false if it's still alive.
7779 *
7780 * @note Locks this object for writing.
7781 */
7782bool Machine::i_checkForSpawnFailure()
7783{
7784 AutoCaller autoCaller(this);
7785 if (!autoCaller.isOk())
7786 {
7787 /* nothing to do */
7788 LogFlowThisFunc(("Already uninitialized!\n"));
7789 return true;
7790 }
7791
7792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7793
7794 if (mData->mSession.mState != SessionState_Spawning)
7795 {
7796 /* nothing to do */
7797 LogFlowThisFunc(("Not spawning any more!\n"));
7798 return true;
7799 }
7800
7801 HRESULT rc = S_OK;
7802
7803 /* PID not yet initialized, skip check. */
7804 if (mData->mSession.mPID == NIL_RTPROCESS)
7805 return false;
7806
7807 RTPROCSTATUS status;
7808 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7809
7810 if (vrc != VERR_PROCESS_RUNNING)
7811 {
7812 Utf8Str strExtraInfo;
7813
7814#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7815 /* If the startup logfile exists and is of non-zero length, tell the
7816 user to look there for more details to encourage them to attach it
7817 when reporting startup issues. */
7818 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7819 uint64_t cbStartupLogFile = 0;
7820 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7821 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7822 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7823#endif
7824
7825 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7826 rc = setError(E_FAIL,
7827 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7828 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7829 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7830 rc = setError(E_FAIL,
7831 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7832 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7833 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7834 rc = setError(E_FAIL,
7835 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7836 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7837 else
7838 rc = setErrorBoth(E_FAIL, vrc,
7839 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7840 i_getName().c_str(), vrc, strExtraInfo.c_str());
7841 }
7842
7843 if (FAILED(rc))
7844 {
7845 /* Close the remote session, remove the remote control from the list
7846 * and reset session state to Closed (@note keep the code in sync with
7847 * the relevant part in LockMachine()). */
7848
7849 Assert(mData->mSession.mRemoteControls.size() == 1);
7850 if (mData->mSession.mRemoteControls.size() == 1)
7851 {
7852 ErrorInfoKeeper eik;
7853 mData->mSession.mRemoteControls.front()->Uninitialize();
7854 }
7855
7856 mData->mSession.mRemoteControls.clear();
7857 mData->mSession.mState = SessionState_Unlocked;
7858
7859 /* finalize the progress after setting the state */
7860 if (!mData->mSession.mProgress.isNull())
7861 {
7862 mData->mSession.mProgress->notifyComplete(rc);
7863 mData->mSession.mProgress.setNull();
7864 }
7865
7866 mData->mSession.mPID = NIL_RTPROCESS;
7867
7868 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7869 return true;
7870 }
7871
7872 return false;
7873}
7874
7875/**
7876 * Checks whether the machine can be registered. If so, commits and saves
7877 * all settings.
7878 *
7879 * @note Must be called from mParent's write lock. Locks this object and
7880 * children for writing.
7881 */
7882HRESULT Machine::i_prepareRegister()
7883{
7884 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7885
7886 AutoLimitedCaller autoCaller(this);
7887 AssertComRCReturnRC(autoCaller.rc());
7888
7889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7890
7891 /* wait for state dependents to drop to zero */
7892 i_ensureNoStateDependencies(alock);
7893
7894 if (!mData->mAccessible)
7895 return setError(VBOX_E_INVALID_OBJECT_STATE,
7896 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7897 mUserData->s.strName.c_str(),
7898 mData->mUuid.toString().c_str());
7899
7900 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7901
7902 if (mData->mRegistered)
7903 return setError(VBOX_E_INVALID_OBJECT_STATE,
7904 tr("The machine '%s' with UUID {%s} is already registered"),
7905 mUserData->s.strName.c_str(),
7906 mData->mUuid.toString().c_str());
7907
7908 HRESULT rc = S_OK;
7909
7910 // Ensure the settings are saved. If we are going to be registered and
7911 // no config file exists yet, create it by calling i_saveSettings() too.
7912 if ( (mData->flModifications)
7913 || (!mData->pMachineConfigFile->fileExists())
7914 )
7915 {
7916 rc = i_saveSettings(NULL, alock);
7917 // no need to check whether VirtualBox.xml needs saving too since
7918 // we can't have a machine XML file rename pending
7919 if (FAILED(rc)) return rc;
7920 }
7921
7922 /* more config checking goes here */
7923
7924 if (SUCCEEDED(rc))
7925 {
7926 /* we may have had implicit modifications we want to fix on success */
7927 i_commit();
7928
7929 mData->mRegistered = true;
7930 }
7931 else
7932 {
7933 /* we may have had implicit modifications we want to cancel on failure*/
7934 i_rollback(false /* aNotify */);
7935 }
7936
7937 return rc;
7938}
7939
7940/**
7941 * Increases the number of objects dependent on the machine state or on the
7942 * registered state. Guarantees that these two states will not change at least
7943 * until #i_releaseStateDependency() is called.
7944 *
7945 * Depending on the @a aDepType value, additional state checks may be made.
7946 * These checks will set extended error info on failure. See
7947 * #i_checkStateDependency() for more info.
7948 *
7949 * If this method returns a failure, the dependency is not added and the caller
7950 * is not allowed to rely on any particular machine state or registration state
7951 * value and may return the failed result code to the upper level.
7952 *
7953 * @param aDepType Dependency type to add.
7954 * @param aState Current machine state (NULL if not interested).
7955 * @param aRegistered Current registered state (NULL if not interested).
7956 *
7957 * @note Locks this object for writing.
7958 */
7959HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7960 MachineState_T *aState /* = NULL */,
7961 BOOL *aRegistered /* = NULL */)
7962{
7963 AutoCaller autoCaller(this);
7964 AssertComRCReturnRC(autoCaller.rc());
7965
7966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7967
7968 HRESULT rc = i_checkStateDependency(aDepType);
7969 if (FAILED(rc)) return rc;
7970
7971 {
7972 if (mData->mMachineStateChangePending != 0)
7973 {
7974 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7975 * drop to zero so don't add more. It may make sense to wait a bit
7976 * and retry before reporting an error (since the pending state
7977 * transition should be really quick) but let's just assert for
7978 * now to see if it ever happens on practice. */
7979
7980 AssertFailed();
7981
7982 return setError(E_ACCESSDENIED,
7983 tr("Machine state change is in progress. Please retry the operation later."));
7984 }
7985
7986 ++mData->mMachineStateDeps;
7987 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7988 }
7989
7990 if (aState)
7991 *aState = mData->mMachineState;
7992 if (aRegistered)
7993 *aRegistered = mData->mRegistered;
7994
7995 return S_OK;
7996}
7997
7998/**
7999 * Decreases the number of objects dependent on the machine state.
8000 * Must always complete the #i_addStateDependency() call after the state
8001 * dependency is no more necessary.
8002 */
8003void Machine::i_releaseStateDependency()
8004{
8005 AutoCaller autoCaller(this);
8006 AssertComRCReturnVoid(autoCaller.rc());
8007
8008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8009
8010 /* releaseStateDependency() w/o addStateDependency()? */
8011 AssertReturnVoid(mData->mMachineStateDeps != 0);
8012 -- mData->mMachineStateDeps;
8013
8014 if (mData->mMachineStateDeps == 0)
8015 {
8016 /* inform i_ensureNoStateDependencies() that there are no more deps */
8017 if (mData->mMachineStateChangePending != 0)
8018 {
8019 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8020 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8021 }
8022 }
8023}
8024
8025Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8026{
8027 /* start with nothing found */
8028 Utf8Str strResult("");
8029
8030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8031
8032 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8033 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8034 // found:
8035 strResult = it->second; // source is a Utf8Str
8036
8037 return strResult;
8038}
8039
8040// protected methods
8041/////////////////////////////////////////////////////////////////////////////
8042
8043/**
8044 * Performs machine state checks based on the @a aDepType value. If a check
8045 * fails, this method will set extended error info, otherwise it will return
8046 * S_OK. It is supposed, that on failure, the caller will immediately return
8047 * the return value of this method to the upper level.
8048 *
8049 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8050 *
8051 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8052 * current state of this machine object allows to change settings of the
8053 * machine (i.e. the machine is not registered, or registered but not running
8054 * and not saved). It is useful to call this method from Machine setters
8055 * before performing any change.
8056 *
8057 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8058 * as for MutableStateDep except that if the machine is saved, S_OK is also
8059 * returned. This is useful in setters which allow changing machine
8060 * properties when it is in the saved state.
8061 *
8062 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8063 * if the current state of this machine object allows to change runtime
8064 * changeable settings of the machine (i.e. the machine is not registered, or
8065 * registered but either running or not running and not saved). It is useful
8066 * to call this method from Machine setters before performing any changes to
8067 * runtime changeable settings.
8068 *
8069 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8070 * the same as for MutableOrRunningStateDep except that if the machine is
8071 * saved, S_OK is also returned. This is useful in setters which allow
8072 * changing runtime and saved state changeable machine properties.
8073 *
8074 * @param aDepType Dependency type to check.
8075 *
8076 * @note Non Machine based classes should use #i_addStateDependency() and
8077 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8078 * template.
8079 *
8080 * @note This method must be called from under this object's read or write
8081 * lock.
8082 */
8083HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8084{
8085 switch (aDepType)
8086 {
8087 case AnyStateDep:
8088 {
8089 break;
8090 }
8091 case MutableStateDep:
8092 {
8093 if ( mData->mRegistered
8094 && ( !i_isSessionMachine()
8095 || ( mData->mMachineState != MachineState_Aborted
8096 && mData->mMachineState != MachineState_Teleported
8097 && mData->mMachineState != MachineState_PoweredOff
8098 )
8099 )
8100 )
8101 return setError(VBOX_E_INVALID_VM_STATE,
8102 tr("The machine is not mutable (state is %s)"),
8103 Global::stringifyMachineState(mData->mMachineState));
8104 break;
8105 }
8106 case MutableOrSavedStateDep:
8107 {
8108 if ( mData->mRegistered
8109 && ( !i_isSessionMachine()
8110 || ( mData->mMachineState != MachineState_Aborted
8111 && mData->mMachineState != MachineState_Teleported
8112 && mData->mMachineState != MachineState_Saved
8113 && mData->mMachineState != MachineState_AbortedSaved
8114 && mData->mMachineState != MachineState_PoweredOff
8115 )
8116 )
8117 )
8118 return setError(VBOX_E_INVALID_VM_STATE,
8119 tr("The machine is not mutable or saved (state is %s)"),
8120 Global::stringifyMachineState(mData->mMachineState));
8121 break;
8122 }
8123 case MutableOrRunningStateDep:
8124 {
8125 if ( mData->mRegistered
8126 && ( !i_isSessionMachine()
8127 || ( mData->mMachineState != MachineState_Aborted
8128 && mData->mMachineState != MachineState_Teleported
8129 && mData->mMachineState != MachineState_PoweredOff
8130 && !Global::IsOnline(mData->mMachineState)
8131 )
8132 )
8133 )
8134 return setError(VBOX_E_INVALID_VM_STATE,
8135 tr("The machine is not mutable or running (state is %s)"),
8136 Global::stringifyMachineState(mData->mMachineState));
8137 break;
8138 }
8139 case MutableOrSavedOrRunningStateDep:
8140 {
8141 if ( mData->mRegistered
8142 && ( !i_isSessionMachine()
8143 || ( mData->mMachineState != MachineState_Aborted
8144 && mData->mMachineState != MachineState_Teleported
8145 && mData->mMachineState != MachineState_Saved
8146 && mData->mMachineState != MachineState_AbortedSaved
8147 && mData->mMachineState != MachineState_PoweredOff
8148 && !Global::IsOnline(mData->mMachineState)
8149 )
8150 )
8151 )
8152 return setError(VBOX_E_INVALID_VM_STATE,
8153 tr("The machine is not mutable, saved or running (state is %s)"),
8154 Global::stringifyMachineState(mData->mMachineState));
8155 break;
8156 }
8157 }
8158
8159 return S_OK;
8160}
8161
8162/**
8163 * Helper to initialize all associated child objects and allocate data
8164 * structures.
8165 *
8166 * This method must be called as a part of the object's initialization procedure
8167 * (usually done in the #init() method).
8168 *
8169 * @note Must be called only from #init() or from #i_registeredInit().
8170 */
8171HRESULT Machine::initDataAndChildObjects()
8172{
8173 AutoCaller autoCaller(this);
8174 AssertComRCReturnRC(autoCaller.rc());
8175 AssertReturn( getObjectState().getState() == ObjectState::InInit
8176 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8177
8178 AssertReturn(!mData->mAccessible, E_FAIL);
8179
8180 /* allocate data structures */
8181 mSSData.allocate();
8182 mUserData.allocate();
8183 mHWData.allocate();
8184 mMediumAttachments.allocate();
8185 mStorageControllers.allocate();
8186 mUSBControllers.allocate();
8187
8188 /* initialize mOSTypeId */
8189 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8190
8191/** @todo r=bird: init() methods never fails, right? Why don't we make them
8192 * return void then! */
8193
8194 /* create associated BIOS settings object */
8195 unconst(mBIOSSettings).createObject();
8196 mBIOSSettings->init(this);
8197
8198 /* create associated trusted platform module object */
8199 unconst(mTrustedPlatformModule).createObject();
8200 mTrustedPlatformModule->init(this);
8201
8202 /* create associated NVRAM store object */
8203 unconst(mNvramStore).createObject();
8204 mNvramStore->init(this);
8205
8206 /* create associated record settings object */
8207 unconst(mRecordingSettings).createObject();
8208 mRecordingSettings->init(this);
8209
8210 /* create the graphics adapter object (always present) */
8211 unconst(mGraphicsAdapter).createObject();
8212 mGraphicsAdapter->init(this);
8213
8214 /* create an associated VRDE object (default is disabled) */
8215 unconst(mVRDEServer).createObject();
8216 mVRDEServer->init(this);
8217
8218 /* create associated serial port objects */
8219 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8220 {
8221 unconst(mSerialPorts[slot]).createObject();
8222 mSerialPorts[slot]->init(this, slot);
8223 }
8224
8225 /* create associated parallel port objects */
8226 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8227 {
8228 unconst(mParallelPorts[slot]).createObject();
8229 mParallelPorts[slot]->init(this, slot);
8230 }
8231
8232 /* create the audio adapter object (always present, default is disabled) */
8233 unconst(mAudioAdapter).createObject();
8234 mAudioAdapter->init(this);
8235
8236 /* create the USB device filters object (always present) */
8237 unconst(mUSBDeviceFilters).createObject();
8238 mUSBDeviceFilters->init(this);
8239
8240 /* create associated network adapter objects */
8241 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8242 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8243 {
8244 unconst(mNetworkAdapters[slot]).createObject();
8245 mNetworkAdapters[slot]->init(this, slot);
8246 }
8247
8248 /* create the bandwidth control */
8249 unconst(mBandwidthControl).createObject();
8250 mBandwidthControl->init(this);
8251
8252 return S_OK;
8253}
8254
8255/**
8256 * Helper to uninitialize all associated child objects and to free all data
8257 * structures.
8258 *
8259 * This method must be called as a part of the object's uninitialization
8260 * procedure (usually done in the #uninit() method).
8261 *
8262 * @note Must be called only from #uninit() or from #i_registeredInit().
8263 */
8264void Machine::uninitDataAndChildObjects()
8265{
8266 AutoCaller autoCaller(this);
8267 AssertComRCReturnVoid(autoCaller.rc());
8268 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8269 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8270 || getObjectState().getState() == ObjectState::InUninit
8271 || getObjectState().getState() == ObjectState::Limited);
8272
8273 /* tell all our other child objects we've been uninitialized */
8274 if (mBandwidthControl)
8275 {
8276 mBandwidthControl->uninit();
8277 unconst(mBandwidthControl).setNull();
8278 }
8279
8280 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8281 {
8282 if (mNetworkAdapters[slot])
8283 {
8284 mNetworkAdapters[slot]->uninit();
8285 unconst(mNetworkAdapters[slot]).setNull();
8286 }
8287 }
8288
8289 if (mUSBDeviceFilters)
8290 {
8291 mUSBDeviceFilters->uninit();
8292 unconst(mUSBDeviceFilters).setNull();
8293 }
8294
8295 if (mAudioAdapter)
8296 {
8297 mAudioAdapter->uninit();
8298 unconst(mAudioAdapter).setNull();
8299 }
8300
8301 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8302 {
8303 if (mParallelPorts[slot])
8304 {
8305 mParallelPorts[slot]->uninit();
8306 unconst(mParallelPorts[slot]).setNull();
8307 }
8308 }
8309
8310 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8311 {
8312 if (mSerialPorts[slot])
8313 {
8314 mSerialPorts[slot]->uninit();
8315 unconst(mSerialPorts[slot]).setNull();
8316 }
8317 }
8318
8319 if (mVRDEServer)
8320 {
8321 mVRDEServer->uninit();
8322 unconst(mVRDEServer).setNull();
8323 }
8324
8325 if (mGraphicsAdapter)
8326 {
8327 mGraphicsAdapter->uninit();
8328 unconst(mGraphicsAdapter).setNull();
8329 }
8330
8331 if (mBIOSSettings)
8332 {
8333 mBIOSSettings->uninit();
8334 unconst(mBIOSSettings).setNull();
8335 }
8336
8337 if (mTrustedPlatformModule)
8338 {
8339 mTrustedPlatformModule->uninit();
8340 unconst(mTrustedPlatformModule).setNull();
8341 }
8342
8343 if (mNvramStore)
8344 {
8345 mNvramStore->uninit();
8346 unconst(mNvramStore).setNull();
8347 }
8348
8349 if (mRecordingSettings)
8350 {
8351 mRecordingSettings->uninit();
8352 unconst(mRecordingSettings).setNull();
8353 }
8354
8355 /* Deassociate media (only when a real Machine or a SnapshotMachine
8356 * instance is uninitialized; SessionMachine instances refer to real
8357 * Machine media). This is necessary for a clean re-initialization of
8358 * the VM after successfully re-checking the accessibility state. Note
8359 * that in case of normal Machine or SnapshotMachine uninitialization (as
8360 * a result of unregistering or deleting the snapshot), outdated media
8361 * attachments will already be uninitialized and deleted, so this
8362 * code will not affect them. */
8363 if ( !mMediumAttachments.isNull()
8364 && !i_isSessionMachine()
8365 )
8366 {
8367 for (MediumAttachmentList::const_iterator
8368 it = mMediumAttachments->begin();
8369 it != mMediumAttachments->end();
8370 ++it)
8371 {
8372 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8373 if (pMedium.isNull())
8374 continue;
8375 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8376 AssertComRC(rc);
8377 }
8378 }
8379
8380 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8381 {
8382 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8383 if (mData->mFirstSnapshot)
8384 {
8385 // snapshots tree is protected by machine write lock; strictly
8386 // this isn't necessary here since we're deleting the entire
8387 // machine, but otherwise we assert in Snapshot::uninit()
8388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8389 mData->mFirstSnapshot->uninit();
8390 mData->mFirstSnapshot.setNull();
8391 }
8392
8393 mData->mCurrentSnapshot.setNull();
8394 }
8395
8396 /* free data structures (the essential mData structure is not freed here
8397 * since it may be still in use) */
8398 mMediumAttachments.free();
8399 mStorageControllers.free();
8400 mUSBControllers.free();
8401 mHWData.free();
8402 mUserData.free();
8403 mSSData.free();
8404}
8405
8406/**
8407 * Returns a pointer to the Machine object for this machine that acts like a
8408 * parent for complex machine data objects such as shared folders, etc.
8409 *
8410 * For primary Machine objects and for SnapshotMachine objects, returns this
8411 * object's pointer itself. For SessionMachine objects, returns the peer
8412 * (primary) machine pointer.
8413 */
8414Machine *Machine::i_getMachine()
8415{
8416 if (i_isSessionMachine())
8417 return (Machine*)mPeer;
8418 return this;
8419}
8420
8421/**
8422 * Makes sure that there are no machine state dependents. If necessary, waits
8423 * for the number of dependents to drop to zero.
8424 *
8425 * Make sure this method is called from under this object's write lock to
8426 * guarantee that no new dependents may be added when this method returns
8427 * control to the caller.
8428 *
8429 * @note Receives a lock to this object for writing. The lock will be released
8430 * while waiting (if necessary).
8431 *
8432 * @warning To be used only in methods that change the machine state!
8433 */
8434void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8435{
8436 AssertReturnVoid(isWriteLockOnCurrentThread());
8437
8438 /* Wait for all state dependents if necessary */
8439 if (mData->mMachineStateDeps != 0)
8440 {
8441 /* lazy semaphore creation */
8442 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8443 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8444
8445 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8446 mData->mMachineStateDeps));
8447
8448 ++mData->mMachineStateChangePending;
8449
8450 /* reset the semaphore before waiting, the last dependent will signal
8451 * it */
8452 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8453
8454 alock.release();
8455
8456 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8457
8458 alock.acquire();
8459
8460 -- mData->mMachineStateChangePending;
8461 }
8462}
8463
8464/**
8465 * Changes the machine state and informs callbacks.
8466 *
8467 * This method is not intended to fail so it either returns S_OK or asserts (and
8468 * returns a failure).
8469 *
8470 * @note Locks this object for writing.
8471 */
8472HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8473{
8474 LogFlowThisFuncEnter();
8475 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8476 Assert(aMachineState != MachineState_Null);
8477
8478 AutoCaller autoCaller(this);
8479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8480
8481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8482
8483 /* wait for state dependents to drop to zero */
8484 i_ensureNoStateDependencies(alock);
8485
8486 MachineState_T const enmOldState = mData->mMachineState;
8487 if (enmOldState != aMachineState)
8488 {
8489 mData->mMachineState = aMachineState;
8490 RTTimeNow(&mData->mLastStateChange);
8491
8492#ifdef VBOX_WITH_DTRACE_R3_MAIN
8493 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8494#endif
8495 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8496 }
8497
8498 LogFlowThisFuncLeave();
8499 return S_OK;
8500}
8501
8502/**
8503 * Searches for a shared folder with the given logical name
8504 * in the collection of shared folders.
8505 *
8506 * @param aName logical name of the shared folder
8507 * @param aSharedFolder where to return the found object
8508 * @param aSetError whether to set the error info if the folder is
8509 * not found
8510 * @return
8511 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8512 *
8513 * @note
8514 * must be called from under the object's lock!
8515 */
8516HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8517 ComObjPtr<SharedFolder> &aSharedFolder,
8518 bool aSetError /* = false */)
8519{
8520 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8521 for (HWData::SharedFolderList::const_iterator
8522 it = mHWData->mSharedFolders.begin();
8523 it != mHWData->mSharedFolders.end();
8524 ++it)
8525 {
8526 SharedFolder *pSF = *it;
8527 AutoCaller autoCaller(pSF);
8528 if (pSF->i_getName() == aName)
8529 {
8530 aSharedFolder = pSF;
8531 rc = S_OK;
8532 break;
8533 }
8534 }
8535
8536 if (aSetError && FAILED(rc))
8537 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8538
8539 return rc;
8540}
8541
8542/**
8543 * Initializes all machine instance data from the given settings structures
8544 * from XML. The exception is the machine UUID which needs special handling
8545 * depending on the caller's use case, so the caller needs to set that herself.
8546 *
8547 * This gets called in several contexts during machine initialization:
8548 *
8549 * -- When machine XML exists on disk already and needs to be loaded into memory,
8550 * for example, from #i_registeredInit() to load all registered machines on
8551 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8552 * attached to the machine should be part of some media registry already.
8553 *
8554 * -- During OVF import, when a machine config has been constructed from an
8555 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8556 * ensure that the media listed as attachments in the config (which have
8557 * been imported from the OVF) receive the correct registry ID.
8558 *
8559 * -- During VM cloning.
8560 *
8561 * @param config Machine settings from XML.
8562 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8563 * for each attached medium in the config.
8564 * @return
8565 */
8566HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8567 const Guid *puuidRegistry)
8568{
8569 // copy name, description, OS type, teleporter, UTC etc.
8570 mUserData->s = config.machineUserData;
8571
8572 // look up the object by Id to check it is valid
8573 ComObjPtr<GuestOSType> pGuestOSType;
8574 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8575 if (!pGuestOSType.isNull())
8576 mUserData->s.strOsType = pGuestOSType->i_id();
8577
8578 // stateFile (optional)
8579 if (config.strStateFile.isEmpty())
8580 mSSData->strStateFilePath.setNull();
8581 else
8582 {
8583 Utf8Str stateFilePathFull(config.strStateFile);
8584 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8585 if (RT_FAILURE(vrc))
8586 return setErrorBoth(E_FAIL, vrc,
8587 tr("Invalid saved state file path '%s' (%Rrc)"),
8588 config.strStateFile.c_str(),
8589 vrc);
8590 mSSData->strStateFilePath = stateFilePathFull;
8591 }
8592
8593 // snapshot folder needs special processing so set it again
8594 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8595 if (FAILED(rc)) return rc;
8596
8597 /* Copy the extra data items (config may or may not be the same as
8598 * mData->pMachineConfigFile) if necessary. When loading the XML files
8599 * from disk they are the same, but not for OVF import. */
8600 if (mData->pMachineConfigFile != &config)
8601 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8602
8603 /* currentStateModified (optional, default is true) */
8604 mData->mCurrentStateModified = config.fCurrentStateModified;
8605
8606 mData->mLastStateChange = config.timeLastStateChange;
8607
8608 /*
8609 * note: all mUserData members must be assigned prior this point because
8610 * we need to commit changes in order to let mUserData be shared by all
8611 * snapshot machine instances.
8612 */
8613 mUserData.commitCopy();
8614
8615 // machine registry, if present (must be loaded before snapshots)
8616 if (config.canHaveOwnMediaRegistry())
8617 {
8618 // determine machine folder
8619 Utf8Str strMachineFolder = i_getSettingsFileFull();
8620 strMachineFolder.stripFilename();
8621 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8622 config.mediaRegistry,
8623 strMachineFolder);
8624 if (FAILED(rc)) return rc;
8625 }
8626
8627 /* Snapshot node (optional) */
8628 size_t cRootSnapshots;
8629 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8630 {
8631 // there must be only one root snapshot
8632 Assert(cRootSnapshots == 1);
8633
8634 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8635
8636 rc = i_loadSnapshot(snap,
8637 config.uuidCurrentSnapshot,
8638 NULL); // no parent == first snapshot
8639 if (FAILED(rc)) return rc;
8640 }
8641
8642 // hardware data
8643 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8644 if (FAILED(rc)) return rc;
8645
8646 /*
8647 * NOTE: the assignment below must be the last thing to do,
8648 * otherwise it will be not possible to change the settings
8649 * somewhere in the code above because all setters will be
8650 * blocked by i_checkStateDependency(MutableStateDep).
8651 */
8652
8653 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8654 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8655 {
8656 /* no need to use i_setMachineState() during init() */
8657 mData->mMachineState = MachineState_AbortedSaved;
8658 }
8659 else if (config.fAborted)
8660 {
8661 mSSData->strStateFilePath.setNull();
8662
8663 /* no need to use i_setMachineState() during init() */
8664 mData->mMachineState = MachineState_Aborted;
8665 }
8666 else if (!mSSData->strStateFilePath.isEmpty())
8667 {
8668 /* no need to use i_setMachineState() during init() */
8669 mData->mMachineState = MachineState_Saved;
8670 }
8671
8672 // after loading settings, we are no longer different from the XML on disk
8673 mData->flModifications = 0;
8674
8675 return S_OK;
8676}
8677
8678/**
8679 * Recursively loads all snapshots starting from the given.
8680 *
8681 * @param data snapshot settings.
8682 * @param aCurSnapshotId Current snapshot ID from the settings file.
8683 * @param aParentSnapshot Parent snapshot.
8684 */
8685HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8686 const Guid &aCurSnapshotId,
8687 Snapshot *aParentSnapshot)
8688{
8689 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8690 AssertReturn(!i_isSessionMachine(), E_FAIL);
8691
8692 HRESULT rc = S_OK;
8693
8694 Utf8Str strStateFile;
8695 if (!data.strStateFile.isEmpty())
8696 {
8697 /* optional */
8698 strStateFile = data.strStateFile;
8699 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8700 if (RT_FAILURE(vrc))
8701 return setErrorBoth(E_FAIL, vrc,
8702 tr("Invalid saved state file path '%s' (%Rrc)"),
8703 strStateFile.c_str(),
8704 vrc);
8705 }
8706
8707 /* create a snapshot machine object */
8708 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8709 pSnapshotMachine.createObject();
8710 rc = pSnapshotMachine->initFromSettings(this,
8711 data.hardware,
8712 &data.debugging,
8713 &data.autostart,
8714 data.uuid.ref(),
8715 strStateFile);
8716 if (FAILED(rc)) return rc;
8717
8718 /* create a snapshot object */
8719 ComObjPtr<Snapshot> pSnapshot;
8720 pSnapshot.createObject();
8721 /* initialize the snapshot */
8722 rc = pSnapshot->init(mParent, // VirtualBox object
8723 data.uuid,
8724 data.strName,
8725 data.strDescription,
8726 data.timestamp,
8727 pSnapshotMachine,
8728 aParentSnapshot);
8729 if (FAILED(rc)) return rc;
8730
8731 /* memorize the first snapshot if necessary */
8732 if (!mData->mFirstSnapshot)
8733 mData->mFirstSnapshot = pSnapshot;
8734
8735 /* memorize the current snapshot when appropriate */
8736 if ( !mData->mCurrentSnapshot
8737 && pSnapshot->i_getId() == aCurSnapshotId
8738 )
8739 mData->mCurrentSnapshot = pSnapshot;
8740
8741 // now create the children
8742 for (settings::SnapshotsList::const_iterator
8743 it = data.llChildSnapshots.begin();
8744 it != data.llChildSnapshots.end();
8745 ++it)
8746 {
8747 const settings::Snapshot &childData = *it;
8748 // recurse
8749 rc = i_loadSnapshot(childData,
8750 aCurSnapshotId,
8751 pSnapshot); // parent = the one we created above
8752 if (FAILED(rc)) return rc;
8753 }
8754
8755 return rc;
8756}
8757
8758/**
8759 * Loads settings into mHWData.
8760 *
8761 * @param puuidRegistry Registry ID.
8762 * @param puuidSnapshot Snapshot ID
8763 * @param data Reference to the hardware settings.
8764 * @param pDbg Pointer to the debugging settings.
8765 * @param pAutostart Pointer to the autostart settings.
8766 */
8767HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8768 const Guid *puuidSnapshot,
8769 const settings::Hardware &data,
8770 const settings::Debugging *pDbg,
8771 const settings::Autostart *pAutostart)
8772{
8773 AssertReturn(!i_isSessionMachine(), E_FAIL);
8774
8775 HRESULT rc = S_OK;
8776
8777 try
8778 {
8779 ComObjPtr<GuestOSType> pGuestOSType;
8780 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8781
8782 /* The hardware version attribute (optional). */
8783 mHWData->mHWVersion = data.strVersion;
8784 mHWData->mHardwareUUID = data.uuid;
8785
8786 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8787 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8788 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8789 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8790 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8791 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8792 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8793 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8794 mHWData->mPAEEnabled = data.fPAE;
8795 mHWData->mLongMode = data.enmLongMode;
8796 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8797 mHWData->mAPIC = data.fAPIC;
8798 mHWData->mX2APIC = data.fX2APIC;
8799 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8800 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8801 mHWData->mSpecCtrl = data.fSpecCtrl;
8802 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8803 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8804 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8805 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8806 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8807 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8808 mHWData->mCPUCount = data.cCPUs;
8809 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8810 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8811 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8812 mHWData->mCpuProfile = data.strCpuProfile;
8813
8814 // cpu
8815 if (mHWData->mCPUHotPlugEnabled)
8816 {
8817 for (settings::CpuList::const_iterator
8818 it = data.llCpus.begin();
8819 it != data.llCpus.end();
8820 ++it)
8821 {
8822 const settings::Cpu &cpu = *it;
8823
8824 mHWData->mCPUAttached[cpu.ulId] = true;
8825 }
8826 }
8827
8828 // cpuid leafs
8829 for (settings::CpuIdLeafsList::const_iterator
8830 it = data.llCpuIdLeafs.begin();
8831 it != data.llCpuIdLeafs.end();
8832 ++it)
8833 {
8834 const settings::CpuIdLeaf &rLeaf= *it;
8835 if ( rLeaf.idx < UINT32_C(0x20)
8836 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8837 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8838 mHWData->mCpuIdLeafList.push_back(rLeaf);
8839 /* else: just ignore */
8840 }
8841
8842 mHWData->mMemorySize = data.ulMemorySizeMB;
8843 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8844
8845 // boot order
8846 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8847 {
8848 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8849 if (it == data.mapBootOrder.end())
8850 mHWData->mBootOrder[i] = DeviceType_Null;
8851 else
8852 mHWData->mBootOrder[i] = it->second;
8853 }
8854
8855 mHWData->mFirmwareType = data.firmwareType;
8856 mHWData->mPointingHIDType = data.pointingHIDType;
8857 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8858 mHWData->mChipsetType = data.chipsetType;
8859 mHWData->mIommuType = data.iommuType;
8860 mHWData->mParavirtProvider = data.paravirtProvider;
8861 mHWData->mParavirtDebug = data.strParavirtDebug;
8862 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8863 mHWData->mHPETEnabled = data.fHPETEnabled;
8864
8865 /* GraphicsAdapter */
8866 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8867 if (FAILED(rc)) return rc;
8868
8869 /* VRDEServer */
8870 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8871 if (FAILED(rc)) return rc;
8872
8873 /* BIOS */
8874 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8875 if (FAILED(rc)) return rc;
8876
8877 /* Trusted Platform Module */
8878 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8879 if (FAILED(rc)) return rc;
8880
8881 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8882 if (FAILED(rc)) return rc;
8883
8884 /* Recording settings */
8885 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8886 if (FAILED(rc)) return rc;
8887
8888 // Bandwidth control (must come before network adapters)
8889 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8890 if (FAILED(rc)) return rc;
8891
8892 /* USB controllers */
8893 for (settings::USBControllerList::const_iterator
8894 it = data.usbSettings.llUSBControllers.begin();
8895 it != data.usbSettings.llUSBControllers.end();
8896 ++it)
8897 {
8898 const settings::USBController &settingsCtrl = *it;
8899 ComObjPtr<USBController> newCtrl;
8900
8901 newCtrl.createObject();
8902 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8903 mUSBControllers->push_back(newCtrl);
8904 }
8905
8906 /* USB device filters */
8907 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8908 if (FAILED(rc)) return rc;
8909
8910 // network adapters (establish array size first and apply defaults, to
8911 // ensure reading the same settings as we saved, since the list skips
8912 // adapters having defaults)
8913 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8914 size_t oldCount = mNetworkAdapters.size();
8915 if (newCount > oldCount)
8916 {
8917 mNetworkAdapters.resize(newCount);
8918 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8919 {
8920 unconst(mNetworkAdapters[slot]).createObject();
8921 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8922 }
8923 }
8924 else if (newCount < oldCount)
8925 mNetworkAdapters.resize(newCount);
8926 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8927 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8928 for (settings::NetworkAdaptersList::const_iterator
8929 it = data.llNetworkAdapters.begin();
8930 it != data.llNetworkAdapters.end();
8931 ++it)
8932 {
8933 const settings::NetworkAdapter &nic = *it;
8934
8935 /* slot uniqueness is guaranteed by XML Schema */
8936 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8937 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8938 if (FAILED(rc)) return rc;
8939 }
8940
8941 // serial ports (establish defaults first, to ensure reading the same
8942 // settings as we saved, since the list skips ports having defaults)
8943 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8944 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8945 for (settings::SerialPortsList::const_iterator
8946 it = data.llSerialPorts.begin();
8947 it != data.llSerialPorts.end();
8948 ++it)
8949 {
8950 const settings::SerialPort &s = *it;
8951
8952 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8953 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8954 if (FAILED(rc)) return rc;
8955 }
8956
8957 // parallel ports (establish defaults first, to ensure reading the same
8958 // settings as we saved, since the list skips ports having defaults)
8959 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8960 mParallelPorts[i]->i_applyDefaults();
8961 for (settings::ParallelPortsList::const_iterator
8962 it = data.llParallelPorts.begin();
8963 it != data.llParallelPorts.end();
8964 ++it)
8965 {
8966 const settings::ParallelPort &p = *it;
8967
8968 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8969 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8970 if (FAILED(rc)) return rc;
8971 }
8972
8973 /* AudioAdapter */
8974 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8975 if (FAILED(rc)) return rc;
8976
8977 /* storage controllers */
8978 rc = i_loadStorageControllers(data.storage,
8979 puuidRegistry,
8980 puuidSnapshot);
8981 if (FAILED(rc)) return rc;
8982
8983 /* Shared folders */
8984 for (settings::SharedFoldersList::const_iterator
8985 it = data.llSharedFolders.begin();
8986 it != data.llSharedFolders.end();
8987 ++it)
8988 {
8989 const settings::SharedFolder &sf = *it;
8990
8991 ComObjPtr<SharedFolder> sharedFolder;
8992 /* Check for double entries. Not allowed! */
8993 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8994 if (SUCCEEDED(rc))
8995 return setError(VBOX_E_OBJECT_IN_USE,
8996 tr("Shared folder named '%s' already exists"),
8997 sf.strName.c_str());
8998
8999 /* Create the new shared folder. Don't break on error. This will be
9000 * reported when the machine starts. */
9001 sharedFolder.createObject();
9002 rc = sharedFolder->init(i_getMachine(),
9003 sf.strName,
9004 sf.strHostPath,
9005 RT_BOOL(sf.fWritable),
9006 RT_BOOL(sf.fAutoMount),
9007 sf.strAutoMountPoint,
9008 false /* fFailOnError */);
9009 if (FAILED(rc)) return rc;
9010 mHWData->mSharedFolders.push_back(sharedFolder);
9011 }
9012
9013 // Clipboard
9014 mHWData->mClipboardMode = data.clipboardMode;
9015 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9016
9017 // drag'n'drop
9018 mHWData->mDnDMode = data.dndMode;
9019
9020 // guest settings
9021 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9022
9023 // IO settings
9024 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9025 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9026
9027 // Host PCI devices
9028 for (settings::HostPCIDeviceAttachmentList::const_iterator
9029 it = data.pciAttachments.begin();
9030 it != data.pciAttachments.end();
9031 ++it)
9032 {
9033 const settings::HostPCIDeviceAttachment &hpda = *it;
9034 ComObjPtr<PCIDeviceAttachment> pda;
9035
9036 pda.createObject();
9037 pda->i_loadSettings(this, hpda);
9038 mHWData->mPCIDeviceAssignments.push_back(pda);
9039 }
9040
9041 /*
9042 * (The following isn't really real hardware, but it lives in HWData
9043 * for reasons of convenience.)
9044 */
9045
9046#ifdef VBOX_WITH_GUEST_PROPS
9047 /* Guest properties (optional) */
9048
9049 /* Only load transient guest properties for configs which have saved
9050 * state, because there shouldn't be any for powered off VMs. The same
9051 * logic applies for snapshots, as offline snapshots shouldn't have
9052 * any such properties. They confuse the code in various places.
9053 * Note: can't rely on the machine state, as it isn't set yet. */
9054 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9055 /* apologies for the hacky unconst() usage, but this needs hacking
9056 * actually inconsistent settings into consistency, otherwise there
9057 * will be some corner cases where the inconsistency survives
9058 * surprisingly long without getting fixed, especially for snapshots
9059 * as there are no config changes. */
9060 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9061 for (settings::GuestPropertiesList::iterator
9062 it = llGuestProperties.begin();
9063 it != llGuestProperties.end();
9064 /*nothing*/)
9065 {
9066 const settings::GuestProperty &prop = *it;
9067 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9068 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9069 if ( fSkipTransientGuestProperties
9070 && ( fFlags & GUEST_PROP_F_TRANSIENT
9071 || fFlags & GUEST_PROP_F_TRANSRESET))
9072 {
9073 it = llGuestProperties.erase(it);
9074 continue;
9075 }
9076 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9077 mHWData->mGuestProperties[prop.strName] = property;
9078 ++it;
9079 }
9080#endif /* VBOX_WITH_GUEST_PROPS defined */
9081
9082 rc = i_loadDebugging(pDbg);
9083 if (FAILED(rc))
9084 return rc;
9085
9086 mHWData->mAutostart = *pAutostart;
9087
9088 /* default frontend */
9089 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9090 }
9091 catch (std::bad_alloc &)
9092 {
9093 return E_OUTOFMEMORY;
9094 }
9095
9096 AssertComRC(rc);
9097 return rc;
9098}
9099
9100/**
9101 * Called from i_loadHardware() to load the debugging settings of the
9102 * machine.
9103 *
9104 * @param pDbg Pointer to the settings.
9105 */
9106HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9107{
9108 mHWData->mDebugging = *pDbg;
9109 /* no more processing currently required, this will probably change. */
9110 return S_OK;
9111}
9112
9113/**
9114 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9115 *
9116 * @param data storage settings.
9117 * @param puuidRegistry media registry ID to set media to or NULL;
9118 * see Machine::i_loadMachineDataFromSettings()
9119 * @param puuidSnapshot snapshot ID
9120 * @return
9121 */
9122HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9123 const Guid *puuidRegistry,
9124 const Guid *puuidSnapshot)
9125{
9126 AssertReturn(!i_isSessionMachine(), E_FAIL);
9127
9128 HRESULT rc = S_OK;
9129
9130 for (settings::StorageControllersList::const_iterator
9131 it = data.llStorageControllers.begin();
9132 it != data.llStorageControllers.end();
9133 ++it)
9134 {
9135 const settings::StorageController &ctlData = *it;
9136
9137 ComObjPtr<StorageController> pCtl;
9138 /* Try to find one with the name first. */
9139 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9140 if (SUCCEEDED(rc))
9141 return setError(VBOX_E_OBJECT_IN_USE,
9142 tr("Storage controller named '%s' already exists"),
9143 ctlData.strName.c_str());
9144
9145 pCtl.createObject();
9146 rc = pCtl->init(this,
9147 ctlData.strName,
9148 ctlData.storageBus,
9149 ctlData.ulInstance,
9150 ctlData.fBootable);
9151 if (FAILED(rc)) return rc;
9152
9153 mStorageControllers->push_back(pCtl);
9154
9155 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9156 if (FAILED(rc)) return rc;
9157
9158 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9159 if (FAILED(rc)) return rc;
9160
9161 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9162 if (FAILED(rc)) return rc;
9163
9164 /* Load the attached devices now. */
9165 rc = i_loadStorageDevices(pCtl,
9166 ctlData,
9167 puuidRegistry,
9168 puuidSnapshot);
9169 if (FAILED(rc)) return rc;
9170 }
9171
9172 return S_OK;
9173}
9174
9175/**
9176 * Called from i_loadStorageControllers for a controller's devices.
9177 *
9178 * @param aStorageController
9179 * @param data
9180 * @param puuidRegistry media registry ID to set media to or NULL; see
9181 * Machine::i_loadMachineDataFromSettings()
9182 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9183 * @return
9184 */
9185HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9186 const settings::StorageController &data,
9187 const Guid *puuidRegistry,
9188 const Guid *puuidSnapshot)
9189{
9190 HRESULT rc = S_OK;
9191
9192 /* paranoia: detect duplicate attachments */
9193 for (settings::AttachedDevicesList::const_iterator
9194 it = data.llAttachedDevices.begin();
9195 it != data.llAttachedDevices.end();
9196 ++it)
9197 {
9198 const settings::AttachedDevice &ad = *it;
9199
9200 for (settings::AttachedDevicesList::const_iterator it2 = it;
9201 it2 != data.llAttachedDevices.end();
9202 ++it2)
9203 {
9204 if (it == it2)
9205 continue;
9206
9207 const settings::AttachedDevice &ad2 = *it2;
9208
9209 if ( ad.lPort == ad2.lPort
9210 && ad.lDevice == ad2.lDevice)
9211 {
9212 return setError(E_FAIL,
9213 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9214 aStorageController->i_getName().c_str(),
9215 ad.lPort,
9216 ad.lDevice,
9217 mUserData->s.strName.c_str());
9218 }
9219 }
9220 }
9221
9222 for (settings::AttachedDevicesList::const_iterator
9223 it = data.llAttachedDevices.begin();
9224 it != data.llAttachedDevices.end();
9225 ++it)
9226 {
9227 const settings::AttachedDevice &dev = *it;
9228 ComObjPtr<Medium> medium;
9229
9230 switch (dev.deviceType)
9231 {
9232 case DeviceType_Floppy:
9233 case DeviceType_DVD:
9234 if (dev.strHostDriveSrc.isNotEmpty())
9235 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9236 false /* fRefresh */, medium);
9237 else
9238 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9239 dev.uuid,
9240 false /* fRefresh */,
9241 false /* aSetError */,
9242 medium);
9243 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9244 // This is not an error. The host drive or UUID might have vanished, so just go
9245 // ahead without this removeable medium attachment
9246 rc = S_OK;
9247 break;
9248
9249 case DeviceType_HardDisk:
9250 {
9251 /* find a hard disk by UUID */
9252 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9253 if (FAILED(rc))
9254 {
9255 if (i_isSnapshotMachine())
9256 {
9257 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9258 // so the user knows that the bad disk is in a snapshot somewhere
9259 com::ErrorInfo info;
9260 return setError(E_FAIL,
9261 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9262 puuidSnapshot->raw(),
9263 info.getText().raw());
9264 }
9265 else
9266 return rc;
9267 }
9268
9269 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9270
9271 if (medium->i_getType() == MediumType_Immutable)
9272 {
9273 if (i_isSnapshotMachine())
9274 return setError(E_FAIL,
9275 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9276 "of the virtual machine '%s' ('%s')"),
9277 medium->i_getLocationFull().c_str(),
9278 dev.uuid.raw(),
9279 puuidSnapshot->raw(),
9280 mUserData->s.strName.c_str(),
9281 mData->m_strConfigFileFull.c_str());
9282
9283 return setError(E_FAIL,
9284 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9285 medium->i_getLocationFull().c_str(),
9286 dev.uuid.raw(),
9287 mUserData->s.strName.c_str(),
9288 mData->m_strConfigFileFull.c_str());
9289 }
9290
9291 if (medium->i_getType() == MediumType_MultiAttach)
9292 {
9293 if (i_isSnapshotMachine())
9294 return setError(E_FAIL,
9295 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9296 "of the virtual machine '%s' ('%s')"),
9297 medium->i_getLocationFull().c_str(),
9298 dev.uuid.raw(),
9299 puuidSnapshot->raw(),
9300 mUserData->s.strName.c_str(),
9301 mData->m_strConfigFileFull.c_str());
9302
9303 return setError(E_FAIL,
9304 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9305 medium->i_getLocationFull().c_str(),
9306 dev.uuid.raw(),
9307 mUserData->s.strName.c_str(),
9308 mData->m_strConfigFileFull.c_str());
9309 }
9310
9311 if ( !i_isSnapshotMachine()
9312 && medium->i_getChildren().size() != 0
9313 )
9314 return setError(E_FAIL,
9315 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9316 "because it has %d differencing child hard disks"),
9317 medium->i_getLocationFull().c_str(),
9318 dev.uuid.raw(),
9319 mUserData->s.strName.c_str(),
9320 mData->m_strConfigFileFull.c_str(),
9321 medium->i_getChildren().size());
9322
9323 if (i_findAttachment(*mMediumAttachments.data(),
9324 medium))
9325 return setError(E_FAIL,
9326 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9327 medium->i_getLocationFull().c_str(),
9328 dev.uuid.raw(),
9329 mUserData->s.strName.c_str(),
9330 mData->m_strConfigFileFull.c_str());
9331
9332 break;
9333 }
9334
9335 default:
9336 return setError(E_FAIL,
9337 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9338 medium->i_getLocationFull().c_str(),
9339 mUserData->s.strName.c_str(),
9340 mData->m_strConfigFileFull.c_str());
9341 }
9342
9343 if (FAILED(rc))
9344 break;
9345
9346 /* Bandwidth groups are loaded at this point. */
9347 ComObjPtr<BandwidthGroup> pBwGroup;
9348
9349 if (!dev.strBwGroup.isEmpty())
9350 {
9351 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9352 if (FAILED(rc))
9353 return setError(E_FAIL,
9354 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9355 medium->i_getLocationFull().c_str(),
9356 dev.strBwGroup.c_str(),
9357 mUserData->s.strName.c_str(),
9358 mData->m_strConfigFileFull.c_str());
9359 pBwGroup->i_reference();
9360 }
9361
9362 const Utf8Str controllerName = aStorageController->i_getName();
9363 ComObjPtr<MediumAttachment> pAttachment;
9364 pAttachment.createObject();
9365 rc = pAttachment->init(this,
9366 medium,
9367 controllerName,
9368 dev.lPort,
9369 dev.lDevice,
9370 dev.deviceType,
9371 false,
9372 dev.fPassThrough,
9373 dev.fTempEject,
9374 dev.fNonRotational,
9375 dev.fDiscard,
9376 dev.fHotPluggable,
9377 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9378 if (FAILED(rc)) break;
9379
9380 /* associate the medium with this machine and snapshot */
9381 if (!medium.isNull())
9382 {
9383 AutoCaller medCaller(medium);
9384 if (FAILED(medCaller.rc())) return medCaller.rc();
9385 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9386
9387 if (i_isSnapshotMachine())
9388 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9389 else
9390 rc = medium->i_addBackReference(mData->mUuid);
9391 /* If the medium->addBackReference fails it sets an appropriate
9392 * error message, so no need to do any guesswork here. */
9393
9394 if (puuidRegistry)
9395 // caller wants registry ID to be set on all attached media (OVF import case)
9396 medium->i_addRegistry(*puuidRegistry);
9397 }
9398
9399 if (FAILED(rc))
9400 break;
9401
9402 /* back up mMediumAttachments to let registeredInit() properly rollback
9403 * on failure (= limited accessibility) */
9404 i_setModified(IsModified_Storage);
9405 mMediumAttachments.backup();
9406 mMediumAttachments->push_back(pAttachment);
9407 }
9408
9409 return rc;
9410}
9411
9412/**
9413 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9414 *
9415 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9416 * @param aSnapshot where to return the found snapshot
9417 * @param aSetError true to set extended error info on failure
9418 */
9419HRESULT Machine::i_findSnapshotById(const Guid &aId,
9420 ComObjPtr<Snapshot> &aSnapshot,
9421 bool aSetError /* = false */)
9422{
9423 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9424
9425 if (!mData->mFirstSnapshot)
9426 {
9427 if (aSetError)
9428 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9429 return E_FAIL;
9430 }
9431
9432 if (aId.isZero())
9433 aSnapshot = mData->mFirstSnapshot;
9434 else
9435 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9436
9437 if (!aSnapshot)
9438 {
9439 if (aSetError)
9440 return setError(E_FAIL,
9441 tr("Could not find a snapshot with UUID {%s}"),
9442 aId.toString().c_str());
9443 return E_FAIL;
9444 }
9445
9446 return S_OK;
9447}
9448
9449/**
9450 * Returns the snapshot with the given name or fails of no such snapshot.
9451 *
9452 * @param strName snapshot name to find
9453 * @param aSnapshot where to return the found snapshot
9454 * @param aSetError true to set extended error info on failure
9455 */
9456HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9457 ComObjPtr<Snapshot> &aSnapshot,
9458 bool aSetError /* = false */)
9459{
9460 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9461
9462 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9463
9464 if (!mData->mFirstSnapshot)
9465 {
9466 if (aSetError)
9467 return setError(VBOX_E_OBJECT_NOT_FOUND,
9468 tr("This machine does not have any snapshots"));
9469 return VBOX_E_OBJECT_NOT_FOUND;
9470 }
9471
9472 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9473
9474 if (!aSnapshot)
9475 {
9476 if (aSetError)
9477 return setError(VBOX_E_OBJECT_NOT_FOUND,
9478 tr("Could not find a snapshot named '%s'"), strName.c_str());
9479 return VBOX_E_OBJECT_NOT_FOUND;
9480 }
9481
9482 return S_OK;
9483}
9484
9485/**
9486 * Returns a storage controller object with the given name.
9487 *
9488 * @param aName storage controller name to find
9489 * @param aStorageController where to return the found storage controller
9490 * @param aSetError true to set extended error info on failure
9491 */
9492HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9493 ComObjPtr<StorageController> &aStorageController,
9494 bool aSetError /* = false */)
9495{
9496 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9497
9498 for (StorageControllerList::const_iterator
9499 it = mStorageControllers->begin();
9500 it != mStorageControllers->end();
9501 ++it)
9502 {
9503 if ((*it)->i_getName() == aName)
9504 {
9505 aStorageController = (*it);
9506 return S_OK;
9507 }
9508 }
9509
9510 if (aSetError)
9511 return setError(VBOX_E_OBJECT_NOT_FOUND,
9512 tr("Could not find a storage controller named '%s'"),
9513 aName.c_str());
9514 return VBOX_E_OBJECT_NOT_FOUND;
9515}
9516
9517/**
9518 * Returns a USB controller object with the given name.
9519 *
9520 * @param aName USB controller name to find
9521 * @param aUSBController where to return the found USB controller
9522 * @param aSetError true to set extended error info on failure
9523 */
9524HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9525 ComObjPtr<USBController> &aUSBController,
9526 bool aSetError /* = false */)
9527{
9528 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9529
9530 for (USBControllerList::const_iterator
9531 it = mUSBControllers->begin();
9532 it != mUSBControllers->end();
9533 ++it)
9534 {
9535 if ((*it)->i_getName() == aName)
9536 {
9537 aUSBController = (*it);
9538 return S_OK;
9539 }
9540 }
9541
9542 if (aSetError)
9543 return setError(VBOX_E_OBJECT_NOT_FOUND,
9544 tr("Could not find a storage controller named '%s'"),
9545 aName.c_str());
9546 return VBOX_E_OBJECT_NOT_FOUND;
9547}
9548
9549/**
9550 * Returns the number of USB controller instance of the given type.
9551 *
9552 * @param enmType USB controller type.
9553 */
9554ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9555{
9556 ULONG cCtrls = 0;
9557
9558 for (USBControllerList::const_iterator
9559 it = mUSBControllers->begin();
9560 it != mUSBControllers->end();
9561 ++it)
9562 {
9563 if ((*it)->i_getControllerType() == enmType)
9564 cCtrls++;
9565 }
9566
9567 return cCtrls;
9568}
9569
9570HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9571 MediumAttachmentList &atts)
9572{
9573 AutoCaller autoCaller(this);
9574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9575
9576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9577
9578 for (MediumAttachmentList::const_iterator
9579 it = mMediumAttachments->begin();
9580 it != mMediumAttachments->end();
9581 ++it)
9582 {
9583 const ComObjPtr<MediumAttachment> &pAtt = *it;
9584 // should never happen, but deal with NULL pointers in the list.
9585 AssertContinue(!pAtt.isNull());
9586
9587 // getControllerName() needs caller+read lock
9588 AutoCaller autoAttCaller(pAtt);
9589 if (FAILED(autoAttCaller.rc()))
9590 {
9591 atts.clear();
9592 return autoAttCaller.rc();
9593 }
9594 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9595
9596 if (pAtt->i_getControllerName() == aName)
9597 atts.push_back(pAtt);
9598 }
9599
9600 return S_OK;
9601}
9602
9603
9604/**
9605 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9606 * file if the machine name was changed and about creating a new settings file
9607 * if this is a new machine.
9608 *
9609 * @note Must be never called directly but only from #saveSettings().
9610 */
9611HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9612 bool *pfSettingsFileIsNew)
9613{
9614 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9615
9616 HRESULT rc = S_OK;
9617
9618 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9619 /// @todo need to handle primary group change, too
9620
9621 /* attempt to rename the settings file if machine name is changed */
9622 if ( mUserData->s.fNameSync
9623 && mUserData.isBackedUp()
9624 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9625 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9626 )
9627 {
9628 bool dirRenamed = false;
9629 bool fileRenamed = false;
9630
9631 Utf8Str configFile, newConfigFile;
9632 Utf8Str configFilePrev, newConfigFilePrev;
9633 Utf8Str NVRAMFile, newNVRAMFile;
9634 Utf8Str configDir, newConfigDir;
9635
9636 do
9637 {
9638 int vrc = VINF_SUCCESS;
9639
9640 Utf8Str name = mUserData.backedUpData()->s.strName;
9641 Utf8Str newName = mUserData->s.strName;
9642 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9643 if (group == "/")
9644 group.setNull();
9645 Utf8Str newGroup = mUserData->s.llGroups.front();
9646 if (newGroup == "/")
9647 newGroup.setNull();
9648
9649 configFile = mData->m_strConfigFileFull;
9650
9651 /* first, rename the directory if it matches the group and machine name */
9652 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9653 /** @todo hack, make somehow use of ComposeMachineFilename */
9654 if (mUserData->s.fDirectoryIncludesUUID)
9655 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9656 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9657 /** @todo hack, make somehow use of ComposeMachineFilename */
9658 if (mUserData->s.fDirectoryIncludesUUID)
9659 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9660 configDir = configFile;
9661 configDir.stripFilename();
9662 newConfigDir = configDir;
9663 if ( configDir.length() >= groupPlusName.length()
9664 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9665 groupPlusName.c_str()))
9666 {
9667 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9668 Utf8Str newConfigBaseDir(newConfigDir);
9669 newConfigDir.append(newGroupPlusName);
9670 /* consistency: use \ if appropriate on the platform */
9671 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9672 /* new dir and old dir cannot be equal here because of 'if'
9673 * above and because name != newName */
9674 Assert(configDir != newConfigDir);
9675 if (!fSettingsFileIsNew)
9676 {
9677 /* perform real rename only if the machine is not new */
9678 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9679 if ( vrc == VERR_FILE_NOT_FOUND
9680 || vrc == VERR_PATH_NOT_FOUND)
9681 {
9682 /* create the parent directory, then retry renaming */
9683 Utf8Str parent(newConfigDir);
9684 parent.stripFilename();
9685 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9686 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9687 }
9688 if (RT_FAILURE(vrc))
9689 {
9690 rc = setErrorBoth(E_FAIL, vrc,
9691 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9692 configDir.c_str(),
9693 newConfigDir.c_str(),
9694 vrc);
9695 break;
9696 }
9697 /* delete subdirectories which are no longer needed */
9698 Utf8Str dir(configDir);
9699 dir.stripFilename();
9700 while (dir != newConfigBaseDir && dir != ".")
9701 {
9702 vrc = RTDirRemove(dir.c_str());
9703 if (RT_FAILURE(vrc))
9704 break;
9705 dir.stripFilename();
9706 }
9707 dirRenamed = true;
9708 }
9709 }
9710
9711 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9712
9713 /* then try to rename the settings file itself */
9714 if (newConfigFile != configFile)
9715 {
9716 /* get the path to old settings file in renamed directory */
9717 Assert(mData->m_strConfigFileFull == configFile);
9718 configFile.printf("%s%c%s",
9719 newConfigDir.c_str(),
9720 RTPATH_DELIMITER,
9721 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9722 if (!fSettingsFileIsNew)
9723 {
9724 /* perform real rename only if the machine is not new */
9725 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9726 if (RT_FAILURE(vrc))
9727 {
9728 rc = setErrorBoth(E_FAIL, vrc,
9729 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9730 configFile.c_str(),
9731 newConfigFile.c_str(),
9732 vrc);
9733 break;
9734 }
9735 fileRenamed = true;
9736 configFilePrev = configFile;
9737 configFilePrev += "-prev";
9738 newConfigFilePrev = newConfigFile;
9739 newConfigFilePrev += "-prev";
9740 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9741 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9742 if (NVRAMFile.isNotEmpty())
9743 {
9744 // in the NVRAM file path, replace the old directory with the new directory
9745 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9746 {
9747 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9748 NVRAMFile = newConfigDir + strNVRAMFile;
9749 }
9750 newNVRAMFile = newConfigFile;
9751 newNVRAMFile.stripSuffix();
9752 newNVRAMFile += ".nvram";
9753 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9754 }
9755 }
9756 }
9757
9758 // update m_strConfigFileFull amd mConfigFile
9759 mData->m_strConfigFileFull = newConfigFile;
9760 // compute the relative path too
9761 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9762
9763 // store the old and new so that VirtualBox::i_saveSettings() can update
9764 // the media registry
9765 if ( mData->mRegistered
9766 && (configDir != newConfigDir || configFile != newConfigFile))
9767 {
9768 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9769
9770 if (pfNeedsGlobalSaveSettings)
9771 *pfNeedsGlobalSaveSettings = true;
9772 }
9773
9774 // in the saved state file path, replace the old directory with the new directory
9775 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9776 {
9777 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9778 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9779 }
9780 if (newNVRAMFile.isNotEmpty())
9781 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9782
9783 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9784 if (mData->mFirstSnapshot)
9785 {
9786 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9787 newConfigDir.c_str());
9788 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9789 newConfigDir.c_str());
9790 }
9791 }
9792 while (0);
9793
9794 if (FAILED(rc))
9795 {
9796 /* silently try to rename everything back */
9797 if (fileRenamed)
9798 {
9799 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9800 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9801 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9802 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9803 }
9804 if (dirRenamed)
9805 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9806 }
9807
9808 if (FAILED(rc)) return rc;
9809 }
9810
9811 if (fSettingsFileIsNew)
9812 {
9813 /* create a virgin config file */
9814 int vrc = VINF_SUCCESS;
9815
9816 /* ensure the settings directory exists */
9817 Utf8Str path(mData->m_strConfigFileFull);
9818 path.stripFilename();
9819 if (!RTDirExists(path.c_str()))
9820 {
9821 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9822 if (RT_FAILURE(vrc))
9823 {
9824 return setErrorBoth(E_FAIL, vrc,
9825 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9826 path.c_str(),
9827 vrc);
9828 }
9829 }
9830
9831 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9832 path = mData->m_strConfigFileFull;
9833 RTFILE f = NIL_RTFILE;
9834 vrc = RTFileOpen(&f, path.c_str(),
9835 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9836 if (RT_FAILURE(vrc))
9837 return setErrorBoth(E_FAIL, vrc,
9838 tr("Could not create the settings file '%s' (%Rrc)"),
9839 path.c_str(),
9840 vrc);
9841 RTFileClose(f);
9842 }
9843 if (pfSettingsFileIsNew)
9844 *pfSettingsFileIsNew = fSettingsFileIsNew;
9845
9846 return rc;
9847}
9848
9849/**
9850 * Saves and commits machine data, user data and hardware data.
9851 *
9852 * Note that on failure, the data remains uncommitted.
9853 *
9854 * @a aFlags may combine the following flags:
9855 *
9856 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9857 * Used when saving settings after an operation that makes them 100%
9858 * correspond to the settings from the current snapshot.
9859 * - SaveS_Force: settings will be saved without doing a deep compare of the
9860 * settings structures. This is used when this is called because snapshots
9861 * have changed to avoid the overhead of the deep compare.
9862 *
9863 * @note Must be called from under this object's write lock. Locks children for
9864 * writing.
9865 *
9866 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9867 * initialized to false and that will be set to true by this function if
9868 * the caller must invoke VirtualBox::i_saveSettings() because the global
9869 * settings have changed. This will happen if a machine rename has been
9870 * saved and the global machine and media registries will therefore need
9871 * updating.
9872 * @param alock Reference to the lock for this machine object.
9873 * @param aFlags Flags.
9874 */
9875HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9876 AutoWriteLock &alock,
9877 int aFlags /*= 0*/)
9878{
9879 LogFlowThisFuncEnter();
9880
9881 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9882
9883 /* make sure child objects are unable to modify the settings while we are
9884 * saving them */
9885 i_ensureNoStateDependencies(alock);
9886
9887 AssertReturn(!i_isSnapshotMachine(),
9888 E_FAIL);
9889
9890 if (!mData->mAccessible)
9891 return setError(VBOX_E_INVALID_VM_STATE,
9892 tr("The machine is not accessible, so cannot save settings"));
9893
9894 HRESULT rc = S_OK;
9895 bool fNeedsWrite = false;
9896 bool fSettingsFileIsNew = false;
9897
9898 /* First, prepare to save settings. It will care about renaming the
9899 * settings directory and file if the machine name was changed and about
9900 * creating a new settings file if this is a new machine. */
9901 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
9902 &fSettingsFileIsNew);
9903 if (FAILED(rc)) return rc;
9904
9905 // keep a pointer to the current settings structures
9906 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9907 settings::MachineConfigFile *pNewConfig = NULL;
9908
9909 try
9910 {
9911 // make a fresh one to have everyone write stuff into
9912 pNewConfig = new settings::MachineConfigFile(NULL);
9913 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9914
9915 // now go and copy all the settings data from COM to the settings structures
9916 // (this calls i_saveSettings() on all the COM objects in the machine)
9917 i_copyMachineDataToSettings(*pNewConfig);
9918
9919 if (aFlags & SaveS_ResetCurStateModified)
9920 {
9921 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9922 mData->mCurrentStateModified = FALSE;
9923 fNeedsWrite = true; // always, no need to compare
9924 }
9925 else if (aFlags & SaveS_Force)
9926 {
9927 fNeedsWrite = true; // always, no need to compare
9928 }
9929 else
9930 {
9931 if (!mData->mCurrentStateModified)
9932 {
9933 // do a deep compare of the settings that we just saved with the settings
9934 // previously stored in the config file; this invokes MachineConfigFile::operator==
9935 // which does a deep compare of all the settings, which is expensive but less expensive
9936 // than writing out XML in vain
9937 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9938
9939 // could still be modified if any settings changed
9940 mData->mCurrentStateModified = fAnySettingsChanged;
9941
9942 fNeedsWrite = fAnySettingsChanged;
9943 }
9944 else
9945 fNeedsWrite = true;
9946 }
9947
9948 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9949
9950 if (fNeedsWrite)
9951 // now spit it all out!
9952 pNewConfig->write(mData->m_strConfigFileFull);
9953
9954 mData->pMachineConfigFile = pNewConfig;
9955 delete pOldConfig;
9956 i_commit();
9957
9958 // after saving settings, we are no longer different from the XML on disk
9959 mData->flModifications = 0;
9960 }
9961 catch (HRESULT err)
9962 {
9963 // we assume that error info is set by the thrower
9964 rc = err;
9965
9966 // delete any newly created settings file
9967 if (fSettingsFileIsNew)
9968 RTFileDelete(mData->m_strConfigFileFull.c_str());
9969
9970 // restore old config
9971 delete pNewConfig;
9972 mData->pMachineConfigFile = pOldConfig;
9973 }
9974 catch (...)
9975 {
9976 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9977 }
9978
9979 if (fNeedsWrite)
9980 {
9981 /* Fire the data change event, even on failure (since we've already
9982 * committed all data). This is done only for SessionMachines because
9983 * mutable Machine instances are always not registered (i.e. private
9984 * to the client process that creates them) and thus don't need to
9985 * inform callbacks. */
9986 if (i_isSessionMachine())
9987 mParent->i_onMachineDataChanged(mData->mUuid);
9988 }
9989
9990 LogFlowThisFunc(("rc=%08X\n", rc));
9991 LogFlowThisFuncLeave();
9992 return rc;
9993}
9994
9995/**
9996 * Implementation for saving the machine settings into the given
9997 * settings::MachineConfigFile instance. This copies machine extradata
9998 * from the previous machine config file in the instance data, if any.
9999 *
10000 * This gets called from two locations:
10001 *
10002 * -- Machine::i_saveSettings(), during the regular XML writing;
10003 *
10004 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10005 * exported to OVF and we write the VirtualBox proprietary XML
10006 * into a <vbox:Machine> tag.
10007 *
10008 * This routine fills all the fields in there, including snapshots, *except*
10009 * for the following:
10010 *
10011 * -- fCurrentStateModified. There is some special logic associated with that.
10012 *
10013 * The caller can then call MachineConfigFile::write() or do something else
10014 * with it.
10015 *
10016 * Caller must hold the machine lock!
10017 *
10018 * This throws XML errors and HRESULT, so the caller must have a catch block!
10019 */
10020void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10021{
10022 // deep copy extradata, being extra careful with self assignment (the STL
10023 // map assignment on Mac OS X clang based Xcode isn't checking)
10024 if (&config != mData->pMachineConfigFile)
10025 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10026
10027 config.uuid = mData->mUuid;
10028
10029 // copy name, description, OS type, teleport, UTC etc.
10030 config.machineUserData = mUserData->s;
10031
10032 if ( mData->mMachineState == MachineState_Saved
10033 || mData->mMachineState == MachineState_AbortedSaved
10034 || mData->mMachineState == MachineState_Restoring
10035 // when doing certain snapshot operations we may or may not have
10036 // a saved state in the current state, so keep everything as is
10037 || ( ( mData->mMachineState == MachineState_Snapshotting
10038 || mData->mMachineState == MachineState_DeletingSnapshot
10039 || mData->mMachineState == MachineState_RestoringSnapshot)
10040 && (!mSSData->strStateFilePath.isEmpty())
10041 )
10042 )
10043 {
10044 Assert(!mSSData->strStateFilePath.isEmpty());
10045 /* try to make the file name relative to the settings file dir */
10046 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10047 }
10048 else
10049 {
10050 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10051 config.strStateFile.setNull();
10052 }
10053
10054 if (mData->mCurrentSnapshot)
10055 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10056 else
10057 config.uuidCurrentSnapshot.clear();
10058
10059 config.timeLastStateChange = mData->mLastStateChange;
10060 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10061 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10062
10063 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10064 if (FAILED(rc)) throw rc;
10065
10066 // save machine's media registry if this is VirtualBox 4.0 or later
10067 if (config.canHaveOwnMediaRegistry())
10068 {
10069 // determine machine folder
10070 Utf8Str strMachineFolder = i_getSettingsFileFull();
10071 strMachineFolder.stripFilename();
10072 mParent->i_saveMediaRegistry(config.mediaRegistry,
10073 i_getId(), // only media with registry ID == machine UUID
10074 strMachineFolder);
10075 // this throws HRESULT
10076 }
10077
10078 // save snapshots
10079 rc = i_saveAllSnapshots(config);
10080 if (FAILED(rc)) throw rc;
10081}
10082
10083/**
10084 * Saves all snapshots of the machine into the given machine config file. Called
10085 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10086 * @param config
10087 * @return
10088 */
10089HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10090{
10091 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10092
10093 HRESULT rc = S_OK;
10094
10095 try
10096 {
10097 config.llFirstSnapshot.clear();
10098
10099 if (mData->mFirstSnapshot)
10100 {
10101 // the settings use a list for "the first snapshot"
10102 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10103
10104 // get reference to the snapshot on the list and work on that
10105 // element straight in the list to avoid excessive copying later
10106 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10107 if (FAILED(rc)) throw rc;
10108 }
10109
10110// if (mType == IsSessionMachine)
10111// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10112
10113 }
10114 catch (HRESULT err)
10115 {
10116 /* we assume that error info is set by the thrower */
10117 rc = err;
10118 }
10119 catch (...)
10120 {
10121 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10122 }
10123
10124 return rc;
10125}
10126
10127/**
10128 * Saves the VM hardware configuration. It is assumed that the
10129 * given node is empty.
10130 *
10131 * @param data Reference to the settings object for the hardware config.
10132 * @param pDbg Pointer to the settings object for the debugging config
10133 * which happens to live in mHWData.
10134 * @param pAutostart Pointer to the settings object for the autostart config
10135 * which happens to live in mHWData.
10136 */
10137HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10138 settings::Autostart *pAutostart)
10139{
10140 HRESULT rc = S_OK;
10141
10142 try
10143 {
10144 /* The hardware version attribute (optional).
10145 Automatically upgrade from 1 to current default hardware version
10146 when there is no saved state. (ugly!) */
10147 if ( mHWData->mHWVersion == "1"
10148 && mSSData->strStateFilePath.isEmpty()
10149 )
10150 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10151
10152 data.strVersion = mHWData->mHWVersion;
10153 data.uuid = mHWData->mHardwareUUID;
10154
10155 // CPU
10156 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10157 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10158 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10159 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10160 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10161 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10162 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10163 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10164 data.fPAE = !!mHWData->mPAEEnabled;
10165 data.enmLongMode = mHWData->mLongMode;
10166 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10167 data.fAPIC = !!mHWData->mAPIC;
10168 data.fX2APIC = !!mHWData->mX2APIC;
10169 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10170 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10171 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10172 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10173 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10174 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10175 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10176 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10177 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10178 data.cCPUs = mHWData->mCPUCount;
10179 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10180 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10181 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10182 data.strCpuProfile = mHWData->mCpuProfile;
10183
10184 data.llCpus.clear();
10185 if (data.fCpuHotPlug)
10186 {
10187 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10188 {
10189 if (mHWData->mCPUAttached[idx])
10190 {
10191 settings::Cpu cpu;
10192 cpu.ulId = idx;
10193 data.llCpus.push_back(cpu);
10194 }
10195 }
10196 }
10197
10198 /* Standard and Extended CPUID leafs. */
10199 data.llCpuIdLeafs.clear();
10200 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10201
10202 // memory
10203 data.ulMemorySizeMB = mHWData->mMemorySize;
10204 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10205
10206 // firmware
10207 data.firmwareType = mHWData->mFirmwareType;
10208
10209 // HID
10210 data.pointingHIDType = mHWData->mPointingHIDType;
10211 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10212
10213 // chipset
10214 data.chipsetType = mHWData->mChipsetType;
10215
10216 // iommu
10217 data.iommuType = mHWData->mIommuType;
10218
10219 // paravirt
10220 data.paravirtProvider = mHWData->mParavirtProvider;
10221 data.strParavirtDebug = mHWData->mParavirtDebug;
10222
10223 // emulated USB card reader
10224 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10225
10226 // HPET
10227 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10228
10229 // boot order
10230 data.mapBootOrder.clear();
10231 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10232 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10233
10234 /* VRDEServer settings (optional) */
10235 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10236 if (FAILED(rc)) throw rc;
10237
10238 /* BIOS settings (required) */
10239 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10240 if (FAILED(rc)) throw rc;
10241
10242 /* Trusted Platform Module settings (required) */
10243 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10244 if (FAILED(rc)) throw rc;
10245
10246 /* NVRAM settings (required) */
10247 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10248 if (FAILED(rc)) throw rc;
10249
10250 /* Recording settings (required) */
10251 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10252 if (FAILED(rc)) throw rc;
10253
10254 /* GraphicsAdapter settings (required) */
10255 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10256 if (FAILED(rc)) throw rc;
10257
10258 /* USB Controller (required) */
10259 data.usbSettings.llUSBControllers.clear();
10260 for (USBControllerList::const_iterator
10261 it = mUSBControllers->begin();
10262 it != mUSBControllers->end();
10263 ++it)
10264 {
10265 ComObjPtr<USBController> ctrl = *it;
10266 settings::USBController settingsCtrl;
10267
10268 settingsCtrl.strName = ctrl->i_getName();
10269 settingsCtrl.enmType = ctrl->i_getControllerType();
10270
10271 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10272 }
10273
10274 /* USB device filters (required) */
10275 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10276 if (FAILED(rc)) throw rc;
10277
10278 /* Network adapters (required) */
10279 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10280 data.llNetworkAdapters.clear();
10281 /* Write out only the nominal number of network adapters for this
10282 * chipset type. Since Machine::commit() hasn't been called there
10283 * may be extra NIC settings in the vector. */
10284 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10285 {
10286 settings::NetworkAdapter nic;
10287 nic.ulSlot = (uint32_t)slot;
10288 /* paranoia check... must not be NULL, but must not crash either. */
10289 if (mNetworkAdapters[slot])
10290 {
10291 if (mNetworkAdapters[slot]->i_hasDefaults())
10292 continue;
10293
10294 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10295 if (FAILED(rc)) throw rc;
10296
10297 data.llNetworkAdapters.push_back(nic);
10298 }
10299 }
10300
10301 /* Serial ports */
10302 data.llSerialPorts.clear();
10303 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10304 {
10305 if (mSerialPorts[slot]->i_hasDefaults())
10306 continue;
10307
10308 settings::SerialPort s;
10309 s.ulSlot = slot;
10310 rc = mSerialPorts[slot]->i_saveSettings(s);
10311 if (FAILED(rc)) return rc;
10312
10313 data.llSerialPorts.push_back(s);
10314 }
10315
10316 /* Parallel ports */
10317 data.llParallelPorts.clear();
10318 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10319 {
10320 if (mParallelPorts[slot]->i_hasDefaults())
10321 continue;
10322
10323 settings::ParallelPort p;
10324 p.ulSlot = slot;
10325 rc = mParallelPorts[slot]->i_saveSettings(p);
10326 if (FAILED(rc)) return rc;
10327
10328 data.llParallelPorts.push_back(p);
10329 }
10330
10331 /* Audio adapter */
10332 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10333 if (FAILED(rc)) return rc;
10334
10335 rc = i_saveStorageControllers(data.storage);
10336 if (FAILED(rc)) return rc;
10337
10338 /* Shared folders */
10339 data.llSharedFolders.clear();
10340 for (HWData::SharedFolderList::const_iterator
10341 it = mHWData->mSharedFolders.begin();
10342 it != mHWData->mSharedFolders.end();
10343 ++it)
10344 {
10345 SharedFolder *pSF = *it;
10346 AutoCaller sfCaller(pSF);
10347 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10348 settings::SharedFolder sf;
10349 sf.strName = pSF->i_getName();
10350 sf.strHostPath = pSF->i_getHostPath();
10351 sf.fWritable = !!pSF->i_isWritable();
10352 sf.fAutoMount = !!pSF->i_isAutoMounted();
10353 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10354
10355 data.llSharedFolders.push_back(sf);
10356 }
10357
10358 // clipboard
10359 data.clipboardMode = mHWData->mClipboardMode;
10360 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10361
10362 // drag'n'drop
10363 data.dndMode = mHWData->mDnDMode;
10364
10365 /* Guest */
10366 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10367
10368 // IO settings
10369 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10370 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10371
10372 /* BandwidthControl (required) */
10373 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10374 if (FAILED(rc)) throw rc;
10375
10376 /* Host PCI devices */
10377 data.pciAttachments.clear();
10378 for (HWData::PCIDeviceAssignmentList::const_iterator
10379 it = mHWData->mPCIDeviceAssignments.begin();
10380 it != mHWData->mPCIDeviceAssignments.end();
10381 ++it)
10382 {
10383 ComObjPtr<PCIDeviceAttachment> pda = *it;
10384 settings::HostPCIDeviceAttachment hpda;
10385
10386 rc = pda->i_saveSettings(hpda);
10387 if (FAILED(rc)) throw rc;
10388
10389 data.pciAttachments.push_back(hpda);
10390 }
10391
10392 // guest properties
10393 data.llGuestProperties.clear();
10394#ifdef VBOX_WITH_GUEST_PROPS
10395 for (HWData::GuestPropertyMap::const_iterator
10396 it = mHWData->mGuestProperties.begin();
10397 it != mHWData->mGuestProperties.end();
10398 ++it)
10399 {
10400 HWData::GuestProperty property = it->second;
10401
10402 /* Remove transient guest properties at shutdown unless we
10403 * are saving state. Note that restoring snapshot intentionally
10404 * keeps them, they will be removed if appropriate once the final
10405 * machine state is set (as crashes etc. need to work). */
10406 if ( ( mData->mMachineState == MachineState_PoweredOff
10407 || mData->mMachineState == MachineState_Aborted
10408 || mData->mMachineState == MachineState_Teleported)
10409 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10410 continue;
10411 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10412 prop.strName = it->first;
10413 prop.strValue = property.strValue;
10414 prop.timestamp = (uint64_t)property.mTimestamp;
10415 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10416 GuestPropWriteFlags(property.mFlags, szFlags);
10417 prop.strFlags = szFlags;
10418
10419 data.llGuestProperties.push_back(prop);
10420 }
10421
10422 /* I presume this doesn't require a backup(). */
10423 mData->mGuestPropertiesModified = FALSE;
10424#endif /* VBOX_WITH_GUEST_PROPS defined */
10425
10426 *pDbg = mHWData->mDebugging;
10427 *pAutostart = mHWData->mAutostart;
10428
10429 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10430 }
10431 catch (std::bad_alloc &)
10432 {
10433 return E_OUTOFMEMORY;
10434 }
10435
10436 AssertComRC(rc);
10437 return rc;
10438}
10439
10440/**
10441 * Saves the storage controller configuration.
10442 *
10443 * @param data storage settings.
10444 */
10445HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10446{
10447 data.llStorageControllers.clear();
10448
10449 for (StorageControllerList::const_iterator
10450 it = mStorageControllers->begin();
10451 it != mStorageControllers->end();
10452 ++it)
10453 {
10454 HRESULT rc;
10455 ComObjPtr<StorageController> pCtl = *it;
10456
10457 settings::StorageController ctl;
10458 ctl.strName = pCtl->i_getName();
10459 ctl.controllerType = pCtl->i_getControllerType();
10460 ctl.storageBus = pCtl->i_getStorageBus();
10461 ctl.ulInstance = pCtl->i_getInstance();
10462 ctl.fBootable = pCtl->i_getBootable();
10463
10464 /* Save the port count. */
10465 ULONG portCount;
10466 rc = pCtl->COMGETTER(PortCount)(&portCount);
10467 ComAssertComRCRet(rc, rc);
10468 ctl.ulPortCount = portCount;
10469
10470 /* Save fUseHostIOCache */
10471 BOOL fUseHostIOCache;
10472 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10473 ComAssertComRCRet(rc, rc);
10474 ctl.fUseHostIOCache = !!fUseHostIOCache;
10475
10476 /* save the devices now. */
10477 rc = i_saveStorageDevices(pCtl, ctl);
10478 ComAssertComRCRet(rc, rc);
10479
10480 data.llStorageControllers.push_back(ctl);
10481 }
10482
10483 return S_OK;
10484}
10485
10486/**
10487 * Saves the hard disk configuration.
10488 */
10489HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10490 settings::StorageController &data)
10491{
10492 MediumAttachmentList atts;
10493
10494 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10495 if (FAILED(rc)) return rc;
10496
10497 data.llAttachedDevices.clear();
10498 for (MediumAttachmentList::const_iterator
10499 it = atts.begin();
10500 it != atts.end();
10501 ++it)
10502 {
10503 settings::AttachedDevice dev;
10504 IMediumAttachment *iA = *it;
10505 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10506 Medium *pMedium = pAttach->i_getMedium();
10507
10508 dev.deviceType = pAttach->i_getType();
10509 dev.lPort = pAttach->i_getPort();
10510 dev.lDevice = pAttach->i_getDevice();
10511 dev.fPassThrough = pAttach->i_getPassthrough();
10512 dev.fHotPluggable = pAttach->i_getHotPluggable();
10513 if (pMedium)
10514 {
10515 if (pMedium->i_isHostDrive())
10516 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10517 else
10518 dev.uuid = pMedium->i_getId();
10519 dev.fTempEject = pAttach->i_getTempEject();
10520 dev.fNonRotational = pAttach->i_getNonRotational();
10521 dev.fDiscard = pAttach->i_getDiscard();
10522 }
10523
10524 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10525
10526 data.llAttachedDevices.push_back(dev);
10527 }
10528
10529 return S_OK;
10530}
10531
10532/**
10533 * Saves machine state settings as defined by aFlags
10534 * (SaveSTS_* values).
10535 *
10536 * @param aFlags Combination of SaveSTS_* flags.
10537 *
10538 * @note Locks objects for writing.
10539 */
10540HRESULT Machine::i_saveStateSettings(int aFlags)
10541{
10542 if (aFlags == 0)
10543 return S_OK;
10544
10545 AutoCaller autoCaller(this);
10546 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10547
10548 /* This object's write lock is also necessary to serialize file access
10549 * (prevent concurrent reads and writes) */
10550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10551
10552 HRESULT rc = S_OK;
10553
10554 Assert(mData->pMachineConfigFile);
10555
10556 try
10557 {
10558 if (aFlags & SaveSTS_CurStateModified)
10559 mData->pMachineConfigFile->fCurrentStateModified = true;
10560
10561 if (aFlags & SaveSTS_StateFilePath)
10562 {
10563 if (!mSSData->strStateFilePath.isEmpty())
10564 /* try to make the file name relative to the settings file dir */
10565 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10566 else
10567 mData->pMachineConfigFile->strStateFile.setNull();
10568 }
10569
10570 if (aFlags & SaveSTS_StateTimeStamp)
10571 {
10572 Assert( mData->mMachineState != MachineState_Aborted
10573 || mSSData->strStateFilePath.isEmpty());
10574
10575 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10576
10577 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10578 || mData->mMachineState == MachineState_AbortedSaved);
10579/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10580 }
10581
10582 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10583 }
10584 catch (...)
10585 {
10586 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10587 }
10588
10589 return rc;
10590}
10591
10592/**
10593 * Ensures that the given medium is added to a media registry. If this machine
10594 * was created with 4.0 or later, then the machine registry is used. Otherwise
10595 * the global VirtualBox media registry is used.
10596 *
10597 * Caller must NOT hold machine lock, media tree or any medium locks!
10598 *
10599 * @param pMedium
10600 */
10601void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10602{
10603 /* Paranoia checks: do not hold machine or media tree locks. */
10604 AssertReturnVoid(!isWriteLockOnCurrentThread());
10605 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10606
10607 ComObjPtr<Medium> pBase;
10608 {
10609 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10610 pBase = pMedium->i_getBase();
10611 }
10612
10613 /* Paranoia checks: do not hold medium locks. */
10614 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10615 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10616
10617 // decide which medium registry to use now that the medium is attached:
10618 Guid uuid;
10619 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10620 if (fCanHaveOwnMediaRegistry)
10621 // machine XML is VirtualBox 4.0 or higher:
10622 uuid = i_getId(); // machine UUID
10623 else
10624 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10625
10626 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10627 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10628 if (pMedium->i_addRegistry(uuid))
10629 mParent->i_markRegistryModified(uuid);
10630
10631 /* For more complex hard disk structures it can happen that the base
10632 * medium isn't yet associated with any medium registry. Do that now. */
10633 if (pMedium != pBase)
10634 {
10635 /* Tree lock needed by Medium::addRegistry when recursing. */
10636 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10637 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10638 {
10639 treeLock.release();
10640 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10641 treeLock.acquire();
10642 }
10643 if (pBase->i_addRegistryRecursive(uuid))
10644 {
10645 treeLock.release();
10646 mParent->i_markRegistryModified(uuid);
10647 }
10648 }
10649}
10650
10651/**
10652 * Creates differencing hard disks for all normal hard disks attached to this
10653 * machine and a new set of attachments to refer to created disks.
10654 *
10655 * Used when taking a snapshot or when deleting the current state. Gets called
10656 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10657 *
10658 * This method assumes that mMediumAttachments contains the original hard disk
10659 * attachments it needs to create diffs for. On success, these attachments will
10660 * be replaced with the created diffs.
10661 *
10662 * Attachments with non-normal hard disks are left as is.
10663 *
10664 * If @a aOnline is @c false then the original hard disks that require implicit
10665 * diffs will be locked for reading. Otherwise it is assumed that they are
10666 * already locked for writing (when the VM was started). Note that in the latter
10667 * case it is responsibility of the caller to lock the newly created diffs for
10668 * writing if this method succeeds.
10669 *
10670 * @param aProgress Progress object to run (must contain at least as
10671 * many operations left as the number of hard disks
10672 * attached).
10673 * @param aWeight Weight of this operation.
10674 * @param aOnline Whether the VM was online prior to this operation.
10675 *
10676 * @note The progress object is not marked as completed, neither on success nor
10677 * on failure. This is a responsibility of the caller.
10678 *
10679 * @note Locks this object and the media tree for writing.
10680 */
10681HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10682 ULONG aWeight,
10683 bool aOnline)
10684{
10685 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10686
10687 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10688 AssertReturn(!!pProgressControl, E_INVALIDARG);
10689
10690 AutoCaller autoCaller(this);
10691 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10692
10693 AutoMultiWriteLock2 alock(this->lockHandle(),
10694 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10695
10696 /* must be in a protective state because we release the lock below */
10697 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10698 || mData->mMachineState == MachineState_OnlineSnapshotting
10699 || mData->mMachineState == MachineState_LiveSnapshotting
10700 || mData->mMachineState == MachineState_RestoringSnapshot
10701 || mData->mMachineState == MachineState_DeletingSnapshot
10702 , E_FAIL);
10703
10704 HRESULT rc = S_OK;
10705
10706 // use appropriate locked media map (online or offline)
10707 MediumLockListMap lockedMediaOffline;
10708 MediumLockListMap *lockedMediaMap;
10709 if (aOnline)
10710 lockedMediaMap = &mData->mSession.mLockedMedia;
10711 else
10712 lockedMediaMap = &lockedMediaOffline;
10713
10714 try
10715 {
10716 if (!aOnline)
10717 {
10718 /* lock all attached hard disks early to detect "in use"
10719 * situations before creating actual diffs */
10720 for (MediumAttachmentList::const_iterator
10721 it = mMediumAttachments->begin();
10722 it != mMediumAttachments->end();
10723 ++it)
10724 {
10725 MediumAttachment *pAtt = *it;
10726 if (pAtt->i_getType() == DeviceType_HardDisk)
10727 {
10728 Medium *pMedium = pAtt->i_getMedium();
10729 Assert(pMedium);
10730
10731 MediumLockList *pMediumLockList(new MediumLockList());
10732 alock.release();
10733 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10734 NULL /* pToLockWrite */,
10735 false /* fMediumLockWriteAll */,
10736 NULL,
10737 *pMediumLockList);
10738 alock.acquire();
10739 if (FAILED(rc))
10740 {
10741 delete pMediumLockList;
10742 throw rc;
10743 }
10744 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10745 if (FAILED(rc))
10746 {
10747 throw setError(rc,
10748 tr("Collecting locking information for all attached media failed"));
10749 }
10750 }
10751 }
10752
10753 /* Now lock all media. If this fails, nothing is locked. */
10754 alock.release();
10755 rc = lockedMediaMap->Lock();
10756 alock.acquire();
10757 if (FAILED(rc))
10758 {
10759 throw setError(rc,
10760 tr("Locking of attached media failed"));
10761 }
10762 }
10763
10764 /* remember the current list (note that we don't use backup() since
10765 * mMediumAttachments may be already backed up) */
10766 MediumAttachmentList atts = *mMediumAttachments.data();
10767
10768 /* start from scratch */
10769 mMediumAttachments->clear();
10770
10771 /* go through remembered attachments and create diffs for normal hard
10772 * disks and attach them */
10773 for (MediumAttachmentList::const_iterator
10774 it = atts.begin();
10775 it != atts.end();
10776 ++it)
10777 {
10778 MediumAttachment *pAtt = *it;
10779
10780 DeviceType_T devType = pAtt->i_getType();
10781 Medium *pMedium = pAtt->i_getMedium();
10782
10783 if ( devType != DeviceType_HardDisk
10784 || pMedium == NULL
10785 || pMedium->i_getType() != MediumType_Normal)
10786 {
10787 /* copy the attachment as is */
10788
10789 /** @todo the progress object created in SessionMachine::TakeSnaphot
10790 * only expects operations for hard disks. Later other
10791 * device types need to show up in the progress as well. */
10792 if (devType == DeviceType_HardDisk)
10793 {
10794 if (pMedium == NULL)
10795 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10796 aWeight); // weight
10797 else
10798 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10799 pMedium->i_getBase()->i_getName().c_str()).raw(),
10800 aWeight); // weight
10801 }
10802
10803 mMediumAttachments->push_back(pAtt);
10804 continue;
10805 }
10806
10807 /* need a diff */
10808 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10809 pMedium->i_getBase()->i_getName().c_str()).raw(),
10810 aWeight); // weight
10811
10812 Utf8Str strFullSnapshotFolder;
10813 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10814
10815 ComObjPtr<Medium> diff;
10816 diff.createObject();
10817 // store the diff in the same registry as the parent
10818 // (this cannot fail here because we can't create implicit diffs for
10819 // unregistered images)
10820 Guid uuidRegistryParent;
10821 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10822 Assert(fInRegistry); NOREF(fInRegistry);
10823 rc = diff->init(mParent,
10824 pMedium->i_getPreferredDiffFormat(),
10825 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10826 uuidRegistryParent,
10827 DeviceType_HardDisk);
10828 if (FAILED(rc)) throw rc;
10829
10830 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10831 * the push_back? Looks like we're going to release medium with the
10832 * wrong kind of lock (general issue with if we fail anywhere at all)
10833 * and an orphaned VDI in the snapshots folder. */
10834
10835 /* update the appropriate lock list */
10836 MediumLockList *pMediumLockList;
10837 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10838 AssertComRCThrowRC(rc);
10839 if (aOnline)
10840 {
10841 alock.release();
10842 /* The currently attached medium will be read-only, change
10843 * the lock type to read. */
10844 rc = pMediumLockList->Update(pMedium, false);
10845 alock.acquire();
10846 AssertComRCThrowRC(rc);
10847 }
10848
10849 /* release the locks before the potentially lengthy operation */
10850 alock.release();
10851 rc = pMedium->i_createDiffStorage(diff,
10852 pMedium->i_getPreferredDiffVariant(),
10853 pMediumLockList,
10854 NULL /* aProgress */,
10855 true /* aWait */,
10856 false /* aNotify */);
10857 alock.acquire();
10858 if (FAILED(rc)) throw rc;
10859
10860 /* actual lock list update is done in Machine::i_commitMedia */
10861
10862 rc = diff->i_addBackReference(mData->mUuid);
10863 AssertComRCThrowRC(rc);
10864
10865 /* add a new attachment */
10866 ComObjPtr<MediumAttachment> attachment;
10867 attachment.createObject();
10868 rc = attachment->init(this,
10869 diff,
10870 pAtt->i_getControllerName(),
10871 pAtt->i_getPort(),
10872 pAtt->i_getDevice(),
10873 DeviceType_HardDisk,
10874 true /* aImplicit */,
10875 false /* aPassthrough */,
10876 false /* aTempEject */,
10877 pAtt->i_getNonRotational(),
10878 pAtt->i_getDiscard(),
10879 pAtt->i_getHotPluggable(),
10880 pAtt->i_getBandwidthGroup());
10881 if (FAILED(rc)) throw rc;
10882
10883 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10884 AssertComRCThrowRC(rc);
10885 mMediumAttachments->push_back(attachment);
10886 }
10887 }
10888 catch (HRESULT aRC) { rc = aRC; }
10889
10890 /* unlock all hard disks we locked when there is no VM */
10891 if (!aOnline)
10892 {
10893 ErrorInfoKeeper eik;
10894
10895 HRESULT rc1 = lockedMediaMap->Clear();
10896 AssertComRC(rc1);
10897 }
10898
10899 return rc;
10900}
10901
10902/**
10903 * Deletes implicit differencing hard disks created either by
10904 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10905 * mMediumAttachments.
10906 *
10907 * Note that to delete hard disks created by #attachDevice() this method is
10908 * called from #i_rollbackMedia() when the changes are rolled back.
10909 *
10910 * @note Locks this object and the media tree for writing.
10911 */
10912HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10913{
10914 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10915
10916 AutoCaller autoCaller(this);
10917 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10918
10919 AutoMultiWriteLock2 alock(this->lockHandle(),
10920 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10921
10922 /* We absolutely must have backed up state. */
10923 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10924
10925 /* Check if there are any implicitly created diff images. */
10926 bool fImplicitDiffs = false;
10927 for (MediumAttachmentList::const_iterator
10928 it = mMediumAttachments->begin();
10929 it != mMediumAttachments->end();
10930 ++it)
10931 {
10932 const ComObjPtr<MediumAttachment> &pAtt = *it;
10933 if (pAtt->i_isImplicit())
10934 {
10935 fImplicitDiffs = true;
10936 break;
10937 }
10938 }
10939 /* If there is nothing to do, leave early. This saves lots of image locking
10940 * effort. It also avoids a MachineStateChanged event without real reason.
10941 * This is important e.g. when loading a VM config, because there should be
10942 * no events. Otherwise API clients can become thoroughly confused for
10943 * inaccessible VMs (the code for loading VM configs uses this method for
10944 * cleanup if the config makes no sense), as they take such events as an
10945 * indication that the VM is alive, and they would force the VM config to
10946 * be reread, leading to an endless loop. */
10947 if (!fImplicitDiffs)
10948 return S_OK;
10949
10950 HRESULT rc = S_OK;
10951 MachineState_T oldState = mData->mMachineState;
10952
10953 /* will release the lock before the potentially lengthy operation,
10954 * so protect with the special state (unless already protected) */
10955 if ( oldState != MachineState_Snapshotting
10956 && oldState != MachineState_OnlineSnapshotting
10957 && oldState != MachineState_LiveSnapshotting
10958 && oldState != MachineState_RestoringSnapshot
10959 && oldState != MachineState_DeletingSnapshot
10960 && oldState != MachineState_DeletingSnapshotOnline
10961 && oldState != MachineState_DeletingSnapshotPaused
10962 )
10963 i_setMachineState(MachineState_SettingUp);
10964
10965 // use appropriate locked media map (online or offline)
10966 MediumLockListMap lockedMediaOffline;
10967 MediumLockListMap *lockedMediaMap;
10968 if (aOnline)
10969 lockedMediaMap = &mData->mSession.mLockedMedia;
10970 else
10971 lockedMediaMap = &lockedMediaOffline;
10972
10973 try
10974 {
10975 if (!aOnline)
10976 {
10977 /* lock all attached hard disks early to detect "in use"
10978 * situations before deleting actual diffs */
10979 for (MediumAttachmentList::const_iterator
10980 it = mMediumAttachments->begin();
10981 it != mMediumAttachments->end();
10982 ++it)
10983 {
10984 MediumAttachment *pAtt = *it;
10985 if (pAtt->i_getType() == DeviceType_HardDisk)
10986 {
10987 Medium *pMedium = pAtt->i_getMedium();
10988 Assert(pMedium);
10989
10990 MediumLockList *pMediumLockList(new MediumLockList());
10991 alock.release();
10992 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10993 NULL /* pToLockWrite */,
10994 false /* fMediumLockWriteAll */,
10995 NULL,
10996 *pMediumLockList);
10997 alock.acquire();
10998
10999 if (FAILED(rc))
11000 {
11001 delete pMediumLockList;
11002 throw rc;
11003 }
11004
11005 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11006 if (FAILED(rc))
11007 throw rc;
11008 }
11009 }
11010
11011 if (FAILED(rc))
11012 throw rc;
11013 } // end of offline
11014
11015 /* Lock lists are now up to date and include implicitly created media */
11016
11017 /* Go through remembered attachments and delete all implicitly created
11018 * diffs and fix up the attachment information */
11019 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11020 MediumAttachmentList implicitAtts;
11021 for (MediumAttachmentList::const_iterator
11022 it = mMediumAttachments->begin();
11023 it != mMediumAttachments->end();
11024 ++it)
11025 {
11026 ComObjPtr<MediumAttachment> pAtt = *it;
11027 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11028 if (pMedium.isNull())
11029 continue;
11030
11031 // Implicit attachments go on the list for deletion and back references are removed.
11032 if (pAtt->i_isImplicit())
11033 {
11034 /* Deassociate and mark for deletion */
11035 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11036 rc = pMedium->i_removeBackReference(mData->mUuid);
11037 if (FAILED(rc))
11038 throw rc;
11039 implicitAtts.push_back(pAtt);
11040 continue;
11041 }
11042
11043 /* Was this medium attached before? */
11044 if (!i_findAttachment(oldAtts, pMedium))
11045 {
11046 /* no: de-associate */
11047 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11048 rc = pMedium->i_removeBackReference(mData->mUuid);
11049 if (FAILED(rc))
11050 throw rc;
11051 continue;
11052 }
11053 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11054 }
11055
11056 /* If there are implicit attachments to delete, throw away the lock
11057 * map contents (which will unlock all media) since the medium
11058 * attachments will be rolled back. Below we need to completely
11059 * recreate the lock map anyway since it is infinitely complex to
11060 * do this incrementally (would need reconstructing each attachment
11061 * change, which would be extremely hairy). */
11062 if (implicitAtts.size() != 0)
11063 {
11064 ErrorInfoKeeper eik;
11065
11066 HRESULT rc1 = lockedMediaMap->Clear();
11067 AssertComRC(rc1);
11068 }
11069
11070 /* rollback hard disk changes */
11071 mMediumAttachments.rollback();
11072
11073 MultiResult mrc(S_OK);
11074
11075 // Delete unused implicit diffs.
11076 if (implicitAtts.size() != 0)
11077 {
11078 alock.release();
11079
11080 for (MediumAttachmentList::const_iterator
11081 it = implicitAtts.begin();
11082 it != implicitAtts.end();
11083 ++it)
11084 {
11085 // Remove medium associated with this attachment.
11086 ComObjPtr<MediumAttachment> pAtt = *it;
11087 Assert(pAtt);
11088 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11089 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11090 Assert(pMedium);
11091
11092 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11093 // continue on delete failure, just collect error messages
11094 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11095 pMedium->i_getLocationFull().c_str() ));
11096 mrc = rc;
11097 }
11098 // Clear the list of deleted implicit attachments now, while not
11099 // holding the lock, as it will ultimately trigger Medium::uninit()
11100 // calls which assume that the media tree lock isn't held.
11101 implicitAtts.clear();
11102
11103 alock.acquire();
11104
11105 /* if there is a VM recreate media lock map as mentioned above,
11106 * otherwise it is a waste of time and we leave things unlocked */
11107 if (aOnline)
11108 {
11109 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11110 /* must never be NULL, but better safe than sorry */
11111 if (!pMachine.isNull())
11112 {
11113 alock.release();
11114 rc = mData->mSession.mMachine->i_lockMedia();
11115 alock.acquire();
11116 if (FAILED(rc))
11117 throw rc;
11118 }
11119 }
11120 }
11121 }
11122 catch (HRESULT aRC) {rc = aRC;}
11123
11124 if (mData->mMachineState == MachineState_SettingUp)
11125 i_setMachineState(oldState);
11126
11127 /* unlock all hard disks we locked when there is no VM */
11128 if (!aOnline)
11129 {
11130 ErrorInfoKeeper eik;
11131
11132 HRESULT rc1 = lockedMediaMap->Clear();
11133 AssertComRC(rc1);
11134 }
11135
11136 return rc;
11137}
11138
11139
11140/**
11141 * Looks through the given list of media attachments for one with the given parameters
11142 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11143 * can be searched as well if needed.
11144 *
11145 * @param ll
11146 * @param aControllerName
11147 * @param aControllerPort
11148 * @param aDevice
11149 * @return
11150 */
11151MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11152 const Utf8Str &aControllerName,
11153 LONG aControllerPort,
11154 LONG aDevice)
11155{
11156 for (MediumAttachmentList::const_iterator
11157 it = ll.begin();
11158 it != ll.end();
11159 ++it)
11160 {
11161 MediumAttachment *pAttach = *it;
11162 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11163 return pAttach;
11164 }
11165
11166 return NULL;
11167}
11168
11169/**
11170 * Looks through the given list of media attachments for one with the given parameters
11171 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11172 * can be searched as well if needed.
11173 *
11174 * @param ll
11175 * @param pMedium
11176 * @return
11177 */
11178MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11179 ComObjPtr<Medium> pMedium)
11180{
11181 for (MediumAttachmentList::const_iterator
11182 it = ll.begin();
11183 it != ll.end();
11184 ++it)
11185 {
11186 MediumAttachment *pAttach = *it;
11187 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11188 if (pMediumThis == pMedium)
11189 return pAttach;
11190 }
11191
11192 return NULL;
11193}
11194
11195/**
11196 * Looks through the given list of media attachments for one with the given parameters
11197 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11198 * can be searched as well if needed.
11199 *
11200 * @param ll
11201 * @param id
11202 * @return
11203 */
11204MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11205 Guid &id)
11206{
11207 for (MediumAttachmentList::const_iterator
11208 it = ll.begin();
11209 it != ll.end();
11210 ++it)
11211 {
11212 MediumAttachment *pAttach = *it;
11213 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11214 if (pMediumThis->i_getId() == id)
11215 return pAttach;
11216 }
11217
11218 return NULL;
11219}
11220
11221/**
11222 * Main implementation for Machine::DetachDevice. This also gets called
11223 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11224 *
11225 * @param pAttach Medium attachment to detach.
11226 * @param writeLock Machine write lock which the caller must have locked once.
11227 * This may be released temporarily in here.
11228 * @param pSnapshot If NULL, then the detachment is for the current machine.
11229 * Otherwise this is for a SnapshotMachine, and this must be
11230 * its snapshot.
11231 * @return
11232 */
11233HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11234 AutoWriteLock &writeLock,
11235 Snapshot *pSnapshot)
11236{
11237 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11238 DeviceType_T mediumType = pAttach->i_getType();
11239
11240 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11241
11242 if (pAttach->i_isImplicit())
11243 {
11244 /* attempt to implicitly delete the implicitly created diff */
11245
11246 /// @todo move the implicit flag from MediumAttachment to Medium
11247 /// and forbid any hard disk operation when it is implicit. Or maybe
11248 /// a special media state for it to make it even more simple.
11249
11250 Assert(mMediumAttachments.isBackedUp());
11251
11252 /* will release the lock before the potentially lengthy operation, so
11253 * protect with the special state */
11254 MachineState_T oldState = mData->mMachineState;
11255 i_setMachineState(MachineState_SettingUp);
11256
11257 writeLock.release();
11258
11259 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11260 true /*aWait*/,
11261 false /*aNotify*/);
11262
11263 writeLock.acquire();
11264
11265 i_setMachineState(oldState);
11266
11267 if (FAILED(rc)) return rc;
11268 }
11269
11270 i_setModified(IsModified_Storage);
11271 mMediumAttachments.backup();
11272 mMediumAttachments->remove(pAttach);
11273
11274 if (!oldmedium.isNull())
11275 {
11276 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11277 if (pSnapshot)
11278 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11279 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11280 else if (mediumType != DeviceType_HardDisk)
11281 oldmedium->i_removeBackReference(mData->mUuid);
11282 }
11283
11284 return S_OK;
11285}
11286
11287/**
11288 * Goes thru all media of the given list and
11289 *
11290 * 1) calls i_detachDevice() on each of them for this machine and
11291 * 2) adds all Medium objects found in the process to the given list,
11292 * depending on cleanupMode.
11293 *
11294 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11295 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11296 * media to the list.
11297 *
11298 * This gets called from Machine::Unregister, both for the actual Machine and
11299 * the SnapshotMachine objects that might be found in the snapshots.
11300 *
11301 * Requires caller and locking. The machine lock must be passed in because it
11302 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11303 *
11304 * @param writeLock Machine lock from top-level caller; this gets passed to
11305 * i_detachDevice.
11306 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11307 * object if called for a SnapshotMachine.
11308 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11309 * added to llMedia; if Full, then all media get added;
11310 * otherwise no media get added.
11311 * @param llMedia Caller's list to receive Medium objects which got detached so
11312 * caller can close() them, depending on cleanupMode.
11313 * @return
11314 */
11315HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11316 Snapshot *pSnapshot,
11317 CleanupMode_T cleanupMode,
11318 MediaList &llMedia)
11319{
11320 Assert(isWriteLockOnCurrentThread());
11321
11322 HRESULT rc;
11323
11324 // make a temporary list because i_detachDevice invalidates iterators into
11325 // mMediumAttachments
11326 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11327
11328 for (MediumAttachmentList::iterator
11329 it = llAttachments2.begin();
11330 it != llAttachments2.end();
11331 ++it)
11332 {
11333 ComObjPtr<MediumAttachment> &pAttach = *it;
11334 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11335
11336 if (!pMedium.isNull())
11337 {
11338 AutoCaller mac(pMedium);
11339 if (FAILED(mac.rc())) return mac.rc();
11340 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11341 DeviceType_T devType = pMedium->i_getDeviceType();
11342 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11343 && devType == DeviceType_HardDisk)
11344 || (cleanupMode == CleanupMode_Full)
11345 )
11346 {
11347 llMedia.push_back(pMedium);
11348 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11349 /* Not allowed to keep this lock as below we need the parent
11350 * medium lock, and the lock order is parent to child. */
11351 lock.release();
11352 /*
11353 * Search for medias which are not attached to any machine, but
11354 * in the chain to an attached disk. Mediums are only consided
11355 * if they are:
11356 * - have only one child
11357 * - no references to any machines
11358 * - are of normal medium type
11359 */
11360 while (!pParent.isNull())
11361 {
11362 AutoCaller mac1(pParent);
11363 if (FAILED(mac1.rc())) return mac1.rc();
11364 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11365 if (pParent->i_getChildren().size() == 1)
11366 {
11367 if ( pParent->i_getMachineBackRefCount() == 0
11368 && pParent->i_getType() == MediumType_Normal
11369 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11370 llMedia.push_back(pParent);
11371 }
11372 else
11373 break;
11374 pParent = pParent->i_getParent();
11375 }
11376 }
11377 }
11378
11379 // real machine: then we need to use the proper method
11380 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11381
11382 if (FAILED(rc))
11383 return rc;
11384 }
11385
11386 return S_OK;
11387}
11388
11389/**
11390 * Perform deferred hard disk detachments.
11391 *
11392 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11393 * changed (not backed up).
11394 *
11395 * If @a aOnline is @c true then this method will also unlock the old hard
11396 * disks for which the new implicit diffs were created and will lock these new
11397 * diffs for writing.
11398 *
11399 * @param aOnline Whether the VM was online prior to this operation.
11400 *
11401 * @note Locks this object for writing!
11402 */
11403void Machine::i_commitMedia(bool aOnline /*= false*/)
11404{
11405 AutoCaller autoCaller(this);
11406 AssertComRCReturnVoid(autoCaller.rc());
11407
11408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11409
11410 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11411
11412 HRESULT rc = S_OK;
11413
11414 /* no attach/detach operations -- nothing to do */
11415 if (!mMediumAttachments.isBackedUp())
11416 return;
11417
11418 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11419 bool fMediaNeedsLocking = false;
11420
11421 /* enumerate new attachments */
11422 for (MediumAttachmentList::const_iterator
11423 it = mMediumAttachments->begin();
11424 it != mMediumAttachments->end();
11425 ++it)
11426 {
11427 MediumAttachment *pAttach = *it;
11428
11429 pAttach->i_commit();
11430
11431 Medium *pMedium = pAttach->i_getMedium();
11432 bool fImplicit = pAttach->i_isImplicit();
11433
11434 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11435 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11436 fImplicit));
11437
11438 /** @todo convert all this Machine-based voodoo to MediumAttachment
11439 * based commit logic. */
11440 if (fImplicit)
11441 {
11442 /* convert implicit attachment to normal */
11443 pAttach->i_setImplicit(false);
11444
11445 if ( aOnline
11446 && pMedium
11447 && pAttach->i_getType() == DeviceType_HardDisk
11448 )
11449 {
11450 /* update the appropriate lock list */
11451 MediumLockList *pMediumLockList;
11452 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11453 AssertComRC(rc);
11454 if (pMediumLockList)
11455 {
11456 /* unlock if there's a need to change the locking */
11457 if (!fMediaNeedsLocking)
11458 {
11459 rc = mData->mSession.mLockedMedia.Unlock();
11460 AssertComRC(rc);
11461 fMediaNeedsLocking = true;
11462 }
11463 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11464 AssertComRC(rc);
11465 rc = pMediumLockList->Append(pMedium, true);
11466 AssertComRC(rc);
11467 }
11468 }
11469
11470 continue;
11471 }
11472
11473 if (pMedium)
11474 {
11475 /* was this medium attached before? */
11476 for (MediumAttachmentList::iterator
11477 oldIt = oldAtts.begin();
11478 oldIt != oldAtts.end();
11479 ++oldIt)
11480 {
11481 MediumAttachment *pOldAttach = *oldIt;
11482 if (pOldAttach->i_getMedium() == pMedium)
11483 {
11484 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11485
11486 /* yes: remove from old to avoid de-association */
11487 oldAtts.erase(oldIt);
11488 break;
11489 }
11490 }
11491 }
11492 }
11493
11494 /* enumerate remaining old attachments and de-associate from the
11495 * current machine state */
11496 for (MediumAttachmentList::const_iterator
11497 it = oldAtts.begin();
11498 it != oldAtts.end();
11499 ++it)
11500 {
11501 MediumAttachment *pAttach = *it;
11502 Medium *pMedium = pAttach->i_getMedium();
11503
11504 /* Detach only hard disks, since DVD/floppy media is detached
11505 * instantly in MountMedium. */
11506 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11507 {
11508 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11509
11510 /* now de-associate from the current machine state */
11511 rc = pMedium->i_removeBackReference(mData->mUuid);
11512 AssertComRC(rc);
11513
11514 if (aOnline)
11515 {
11516 /* unlock since medium is not used anymore */
11517 MediumLockList *pMediumLockList;
11518 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11519 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11520 {
11521 /* this happens for online snapshots, there the attachment
11522 * is changing, but only to a diff image created under
11523 * the old one, so there is no separate lock list */
11524 Assert(!pMediumLockList);
11525 }
11526 else
11527 {
11528 AssertComRC(rc);
11529 if (pMediumLockList)
11530 {
11531 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11532 AssertComRC(rc);
11533 }
11534 }
11535 }
11536 }
11537 }
11538
11539 /* take media locks again so that the locking state is consistent */
11540 if (fMediaNeedsLocking)
11541 {
11542 Assert(aOnline);
11543 rc = mData->mSession.mLockedMedia.Lock();
11544 AssertComRC(rc);
11545 }
11546
11547 /* commit the hard disk changes */
11548 mMediumAttachments.commit();
11549
11550 if (i_isSessionMachine())
11551 {
11552 /*
11553 * Update the parent machine to point to the new owner.
11554 * This is necessary because the stored parent will point to the
11555 * session machine otherwise and cause crashes or errors later
11556 * when the session machine gets invalid.
11557 */
11558 /** @todo Change the MediumAttachment class to behave like any other
11559 * class in this regard by creating peer MediumAttachment
11560 * objects for session machines and share the data with the peer
11561 * machine.
11562 */
11563 for (MediumAttachmentList::const_iterator
11564 it = mMediumAttachments->begin();
11565 it != mMediumAttachments->end();
11566 ++it)
11567 (*it)->i_updateParentMachine(mPeer);
11568
11569 /* attach new data to the primary machine and reshare it */
11570 mPeer->mMediumAttachments.attach(mMediumAttachments);
11571 }
11572
11573 return;
11574}
11575
11576/**
11577 * Perform deferred deletion of implicitly created diffs.
11578 *
11579 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11580 * changed (not backed up).
11581 *
11582 * @note Locks this object for writing!
11583 */
11584void Machine::i_rollbackMedia()
11585{
11586 AutoCaller autoCaller(this);
11587 AssertComRCReturnVoid(autoCaller.rc());
11588
11589 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11590 LogFlowThisFunc(("Entering rollbackMedia\n"));
11591
11592 HRESULT rc = S_OK;
11593
11594 /* no attach/detach operations -- nothing to do */
11595 if (!mMediumAttachments.isBackedUp())
11596 return;
11597
11598 /* enumerate new attachments */
11599 for (MediumAttachmentList::const_iterator
11600 it = mMediumAttachments->begin();
11601 it != mMediumAttachments->end();
11602 ++it)
11603 {
11604 MediumAttachment *pAttach = *it;
11605 /* Fix up the backrefs for DVD/floppy media. */
11606 if (pAttach->i_getType() != DeviceType_HardDisk)
11607 {
11608 Medium *pMedium = pAttach->i_getMedium();
11609 if (pMedium)
11610 {
11611 rc = pMedium->i_removeBackReference(mData->mUuid);
11612 AssertComRC(rc);
11613 }
11614 }
11615
11616 (*it)->i_rollback();
11617
11618 pAttach = *it;
11619 /* Fix up the backrefs for DVD/floppy media. */
11620 if (pAttach->i_getType() != DeviceType_HardDisk)
11621 {
11622 Medium *pMedium = pAttach->i_getMedium();
11623 if (pMedium)
11624 {
11625 rc = pMedium->i_addBackReference(mData->mUuid);
11626 AssertComRC(rc);
11627 }
11628 }
11629 }
11630
11631 /** @todo convert all this Machine-based voodoo to MediumAttachment
11632 * based rollback logic. */
11633 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11634
11635 return;
11636}
11637
11638/**
11639 * Returns true if the settings file is located in the directory named exactly
11640 * as the machine; this means, among other things, that the machine directory
11641 * should be auto-renamed.
11642 *
11643 * @param aSettingsDir if not NULL, the full machine settings file directory
11644 * name will be assigned there.
11645 *
11646 * @note Doesn't lock anything.
11647 * @note Not thread safe (must be called from this object's lock).
11648 */
11649bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11650{
11651 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11652 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11653 if (aSettingsDir)
11654 *aSettingsDir = strMachineDirName;
11655 strMachineDirName.stripPath(); // vmname
11656 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11657 strConfigFileOnly.stripPath() // vmname.vbox
11658 .stripSuffix(); // vmname
11659 /** @todo hack, make somehow use of ComposeMachineFilename */
11660 if (mUserData->s.fDirectoryIncludesUUID)
11661 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11662
11663 AssertReturn(!strMachineDirName.isEmpty(), false);
11664 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11665
11666 return strMachineDirName == strConfigFileOnly;
11667}
11668
11669/**
11670 * Discards all changes to machine settings.
11671 *
11672 * @param aNotify Whether to notify the direct session about changes or not.
11673 *
11674 * @note Locks objects for writing!
11675 */
11676void Machine::i_rollback(bool aNotify)
11677{
11678 AutoCaller autoCaller(this);
11679 AssertComRCReturn(autoCaller.rc(), (void)0);
11680
11681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11682
11683 if (!mStorageControllers.isNull())
11684 {
11685 if (mStorageControllers.isBackedUp())
11686 {
11687 /* unitialize all new devices (absent in the backed up list). */
11688 StorageControllerList *backedList = mStorageControllers.backedUpData();
11689 for (StorageControllerList::const_iterator
11690 it = mStorageControllers->begin();
11691 it != mStorageControllers->end();
11692 ++it)
11693 {
11694 if ( std::find(backedList->begin(), backedList->end(), *it)
11695 == backedList->end()
11696 )
11697 {
11698 (*it)->uninit();
11699 }
11700 }
11701
11702 /* restore the list */
11703 mStorageControllers.rollback();
11704 }
11705
11706 /* rollback any changes to devices after restoring the list */
11707 if (mData->flModifications & IsModified_Storage)
11708 {
11709 for (StorageControllerList::const_iterator
11710 it = mStorageControllers->begin();
11711 it != mStorageControllers->end();
11712 ++it)
11713 {
11714 (*it)->i_rollback();
11715 }
11716 }
11717 }
11718
11719 if (!mUSBControllers.isNull())
11720 {
11721 if (mUSBControllers.isBackedUp())
11722 {
11723 /* unitialize all new devices (absent in the backed up list). */
11724 USBControllerList *backedList = mUSBControllers.backedUpData();
11725 for (USBControllerList::const_iterator
11726 it = mUSBControllers->begin();
11727 it != mUSBControllers->end();
11728 ++it)
11729 {
11730 if ( std::find(backedList->begin(), backedList->end(), *it)
11731 == backedList->end()
11732 )
11733 {
11734 (*it)->uninit();
11735 }
11736 }
11737
11738 /* restore the list */
11739 mUSBControllers.rollback();
11740 }
11741
11742 /* rollback any changes to devices after restoring the list */
11743 if (mData->flModifications & IsModified_USB)
11744 {
11745 for (USBControllerList::const_iterator
11746 it = mUSBControllers->begin();
11747 it != mUSBControllers->end();
11748 ++it)
11749 {
11750 (*it)->i_rollback();
11751 }
11752 }
11753 }
11754
11755 mUserData.rollback();
11756
11757 mHWData.rollback();
11758
11759 if (mData->flModifications & IsModified_Storage)
11760 i_rollbackMedia();
11761
11762 if (mBIOSSettings)
11763 mBIOSSettings->i_rollback();
11764
11765 if (mTrustedPlatformModule)
11766 mTrustedPlatformModule->i_rollback();
11767
11768 if (mNvramStore)
11769 mNvramStore->i_rollback();
11770
11771 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11772 mRecordingSettings->i_rollback();
11773
11774 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11775 mGraphicsAdapter->i_rollback();
11776
11777 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11778 mVRDEServer->i_rollback();
11779
11780 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11781 mAudioAdapter->i_rollback();
11782
11783 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11784 mUSBDeviceFilters->i_rollback();
11785
11786 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11787 mBandwidthControl->i_rollback();
11788
11789 if (!mHWData.isNull())
11790 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11791 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11792 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11793 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11794
11795 if (mData->flModifications & IsModified_NetworkAdapters)
11796 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11797 if ( mNetworkAdapters[slot]
11798 && mNetworkAdapters[slot]->i_isModified())
11799 {
11800 mNetworkAdapters[slot]->i_rollback();
11801 networkAdapters[slot] = mNetworkAdapters[slot];
11802 }
11803
11804 if (mData->flModifications & IsModified_SerialPorts)
11805 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11806 if ( mSerialPorts[slot]
11807 && mSerialPorts[slot]->i_isModified())
11808 {
11809 mSerialPorts[slot]->i_rollback();
11810 serialPorts[slot] = mSerialPorts[slot];
11811 }
11812
11813 if (mData->flModifications & IsModified_ParallelPorts)
11814 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11815 if ( mParallelPorts[slot]
11816 && mParallelPorts[slot]->i_isModified())
11817 {
11818 mParallelPorts[slot]->i_rollback();
11819 parallelPorts[slot] = mParallelPorts[slot];
11820 }
11821
11822 if (aNotify)
11823 {
11824 /* inform the direct session about changes */
11825
11826 ComObjPtr<Machine> that = this;
11827 uint32_t flModifications = mData->flModifications;
11828 alock.release();
11829
11830 if (flModifications & IsModified_SharedFolders)
11831 that->i_onSharedFolderChange();
11832
11833 if (flModifications & IsModified_VRDEServer)
11834 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11835 if (flModifications & IsModified_USB)
11836 that->i_onUSBControllerChange();
11837
11838 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11839 if (networkAdapters[slot])
11840 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11841 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11842 if (serialPorts[slot])
11843 that->i_onSerialPortChange(serialPorts[slot]);
11844 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11845 if (parallelPorts[slot])
11846 that->i_onParallelPortChange(parallelPorts[slot]);
11847
11848 if (flModifications & IsModified_Storage)
11849 {
11850 for (StorageControllerList::const_iterator
11851 it = mStorageControllers->begin();
11852 it != mStorageControllers->end();
11853 ++it)
11854 {
11855 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11856 }
11857 }
11858
11859
11860#if 0
11861 if (flModifications & IsModified_BandwidthControl)
11862 that->onBandwidthControlChange();
11863#endif
11864 }
11865}
11866
11867/**
11868 * Commits all the changes to machine settings.
11869 *
11870 * Note that this operation is supposed to never fail.
11871 *
11872 * @note Locks this object and children for writing.
11873 */
11874void Machine::i_commit()
11875{
11876 AutoCaller autoCaller(this);
11877 AssertComRCReturnVoid(autoCaller.rc());
11878
11879 AutoCaller peerCaller(mPeer);
11880 AssertComRCReturnVoid(peerCaller.rc());
11881
11882 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11883
11884 /*
11885 * use safe commit to ensure Snapshot machines (that share mUserData)
11886 * will still refer to a valid memory location
11887 */
11888 mUserData.commitCopy();
11889
11890 mHWData.commit();
11891
11892 if (mMediumAttachments.isBackedUp())
11893 i_commitMedia(Global::IsOnline(mData->mMachineState));
11894
11895 mBIOSSettings->i_commit();
11896 mTrustedPlatformModule->i_commit();
11897 mNvramStore->i_commit();
11898 mRecordingSettings->i_commit();
11899 mGraphicsAdapter->i_commit();
11900 mVRDEServer->i_commit();
11901 mAudioAdapter->i_commit();
11902 mUSBDeviceFilters->i_commit();
11903 mBandwidthControl->i_commit();
11904
11905 /* Since mNetworkAdapters is a list which might have been changed (resized)
11906 * without using the Backupable<> template we need to handle the copying
11907 * of the list entries manually, including the creation of peers for the
11908 * new objects. */
11909 bool commitNetworkAdapters = false;
11910 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11911 if (mPeer)
11912 {
11913 /* commit everything, even the ones which will go away */
11914 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11915 mNetworkAdapters[slot]->i_commit();
11916 /* copy over the new entries, creating a peer and uninit the original */
11917 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11918 for (size_t slot = 0; slot < newSize; slot++)
11919 {
11920 /* look if this adapter has a peer device */
11921 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11922 if (!peer)
11923 {
11924 /* no peer means the adapter is a newly created one;
11925 * create a peer owning data this data share it with */
11926 peer.createObject();
11927 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11928 }
11929 mPeer->mNetworkAdapters[slot] = peer;
11930 }
11931 /* uninit any no longer needed network adapters */
11932 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11933 mNetworkAdapters[slot]->uninit();
11934 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11935 {
11936 if (mPeer->mNetworkAdapters[slot])
11937 mPeer->mNetworkAdapters[slot]->uninit();
11938 }
11939 /* Keep the original network adapter count until this point, so that
11940 * discarding a chipset type change will not lose settings. */
11941 mNetworkAdapters.resize(newSize);
11942 mPeer->mNetworkAdapters.resize(newSize);
11943 }
11944 else
11945 {
11946 /* we have no peer (our parent is the newly created machine);
11947 * just commit changes to the network adapters */
11948 commitNetworkAdapters = true;
11949 }
11950 if (commitNetworkAdapters)
11951 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11952 mNetworkAdapters[slot]->i_commit();
11953
11954 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11955 mSerialPorts[slot]->i_commit();
11956 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11957 mParallelPorts[slot]->i_commit();
11958
11959 bool commitStorageControllers = false;
11960
11961 if (mStorageControllers.isBackedUp())
11962 {
11963 mStorageControllers.commit();
11964
11965 if (mPeer)
11966 {
11967 /* Commit all changes to new controllers (this will reshare data with
11968 * peers for those who have peers) */
11969 StorageControllerList *newList = new StorageControllerList();
11970 for (StorageControllerList::const_iterator
11971 it = mStorageControllers->begin();
11972 it != mStorageControllers->end();
11973 ++it)
11974 {
11975 (*it)->i_commit();
11976
11977 /* look if this controller has a peer device */
11978 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11979 if (!peer)
11980 {
11981 /* no peer means the device is a newly created one;
11982 * create a peer owning data this device share it with */
11983 peer.createObject();
11984 peer->init(mPeer, *it, true /* aReshare */);
11985 }
11986 else
11987 {
11988 /* remove peer from the old list */
11989 mPeer->mStorageControllers->remove(peer);
11990 }
11991 /* and add it to the new list */
11992 newList->push_back(peer);
11993 }
11994
11995 /* uninit old peer's controllers that are left */
11996 for (StorageControllerList::const_iterator
11997 it = mPeer->mStorageControllers->begin();
11998 it != mPeer->mStorageControllers->end();
11999 ++it)
12000 {
12001 (*it)->uninit();
12002 }
12003
12004 /* attach new list of controllers to our peer */
12005 mPeer->mStorageControllers.attach(newList);
12006 }
12007 else
12008 {
12009 /* we have no peer (our parent is the newly created machine);
12010 * just commit changes to devices */
12011 commitStorageControllers = true;
12012 }
12013 }
12014 else
12015 {
12016 /* the list of controllers itself is not changed,
12017 * just commit changes to controllers themselves */
12018 commitStorageControllers = true;
12019 }
12020
12021 if (commitStorageControllers)
12022 {
12023 for (StorageControllerList::const_iterator
12024 it = mStorageControllers->begin();
12025 it != mStorageControllers->end();
12026 ++it)
12027 {
12028 (*it)->i_commit();
12029 }
12030 }
12031
12032 bool commitUSBControllers = false;
12033
12034 if (mUSBControllers.isBackedUp())
12035 {
12036 mUSBControllers.commit();
12037
12038 if (mPeer)
12039 {
12040 /* Commit all changes to new controllers (this will reshare data with
12041 * peers for those who have peers) */
12042 USBControllerList *newList = new USBControllerList();
12043 for (USBControllerList::const_iterator
12044 it = mUSBControllers->begin();
12045 it != mUSBControllers->end();
12046 ++it)
12047 {
12048 (*it)->i_commit();
12049
12050 /* look if this controller has a peer device */
12051 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12052 if (!peer)
12053 {
12054 /* no peer means the device is a newly created one;
12055 * create a peer owning data this device share it with */
12056 peer.createObject();
12057 peer->init(mPeer, *it, true /* aReshare */);
12058 }
12059 else
12060 {
12061 /* remove peer from the old list */
12062 mPeer->mUSBControllers->remove(peer);
12063 }
12064 /* and add it to the new list */
12065 newList->push_back(peer);
12066 }
12067
12068 /* uninit old peer's controllers that are left */
12069 for (USBControllerList::const_iterator
12070 it = mPeer->mUSBControllers->begin();
12071 it != mPeer->mUSBControllers->end();
12072 ++it)
12073 {
12074 (*it)->uninit();
12075 }
12076
12077 /* attach new list of controllers to our peer */
12078 mPeer->mUSBControllers.attach(newList);
12079 }
12080 else
12081 {
12082 /* we have no peer (our parent is the newly created machine);
12083 * just commit changes to devices */
12084 commitUSBControllers = true;
12085 }
12086 }
12087 else
12088 {
12089 /* the list of controllers itself is not changed,
12090 * just commit changes to controllers themselves */
12091 commitUSBControllers = true;
12092 }
12093
12094 if (commitUSBControllers)
12095 {
12096 for (USBControllerList::const_iterator
12097 it = mUSBControllers->begin();
12098 it != mUSBControllers->end();
12099 ++it)
12100 {
12101 (*it)->i_commit();
12102 }
12103 }
12104
12105 if (i_isSessionMachine())
12106 {
12107 /* attach new data to the primary machine and reshare it */
12108 mPeer->mUserData.attach(mUserData);
12109 mPeer->mHWData.attach(mHWData);
12110 /* mmMediumAttachments is reshared by fixupMedia */
12111 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12112 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12113 }
12114}
12115
12116/**
12117 * Copies all the hardware data from the given machine.
12118 *
12119 * Currently, only called when the VM is being restored from a snapshot. In
12120 * particular, this implies that the VM is not running during this method's
12121 * call.
12122 *
12123 * @note This method must be called from under this object's lock.
12124 *
12125 * @note This method doesn't call #i_commit(), so all data remains backed up and
12126 * unsaved.
12127 */
12128void Machine::i_copyFrom(Machine *aThat)
12129{
12130 AssertReturnVoid(!i_isSnapshotMachine());
12131 AssertReturnVoid(aThat->i_isSnapshotMachine());
12132
12133 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12134
12135 mHWData.assignCopy(aThat->mHWData);
12136
12137 // create copies of all shared folders (mHWData after attaching a copy
12138 // contains just references to original objects)
12139 for (HWData::SharedFolderList::iterator
12140 it = mHWData->mSharedFolders.begin();
12141 it != mHWData->mSharedFolders.end();
12142 ++it)
12143 {
12144 ComObjPtr<SharedFolder> folder;
12145 folder.createObject();
12146 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12147 AssertComRC(rc);
12148 *it = folder;
12149 }
12150
12151 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12152 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12153 mNvramStore->i_copyFrom(aThat->mNvramStore);
12154 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12155 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12156 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12157 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12158 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12159 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12160
12161 /* create private copies of all controllers */
12162 mStorageControllers.backup();
12163 mStorageControllers->clear();
12164 for (StorageControllerList::const_iterator
12165 it = aThat->mStorageControllers->begin();
12166 it != aThat->mStorageControllers->end();
12167 ++it)
12168 {
12169 ComObjPtr<StorageController> ctrl;
12170 ctrl.createObject();
12171 ctrl->initCopy(this, *it);
12172 mStorageControllers->push_back(ctrl);
12173 }
12174
12175 /* create private copies of all USB controllers */
12176 mUSBControllers.backup();
12177 mUSBControllers->clear();
12178 for (USBControllerList::const_iterator
12179 it = aThat->mUSBControllers->begin();
12180 it != aThat->mUSBControllers->end();
12181 ++it)
12182 {
12183 ComObjPtr<USBController> ctrl;
12184 ctrl.createObject();
12185 ctrl->initCopy(this, *it);
12186 mUSBControllers->push_back(ctrl);
12187 }
12188
12189 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12190 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12191 {
12192 if (mNetworkAdapters[slot].isNotNull())
12193 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12194 else
12195 {
12196 unconst(mNetworkAdapters[slot]).createObject();
12197 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12198 }
12199 }
12200 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12201 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12202 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12203 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12204}
12205
12206/**
12207 * Returns whether the given storage controller is hotplug capable.
12208 *
12209 * @returns true if the controller supports hotplugging
12210 * false otherwise.
12211 * @param enmCtrlType The controller type to check for.
12212 */
12213bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12214{
12215 ComPtr<ISystemProperties> systemProperties;
12216 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12217 if (FAILED(rc))
12218 return false;
12219
12220 BOOL aHotplugCapable = FALSE;
12221 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12222
12223 return RT_BOOL(aHotplugCapable);
12224}
12225
12226#ifdef VBOX_WITH_RESOURCE_USAGE_API
12227
12228void Machine::i_getDiskList(MediaList &list)
12229{
12230 for (MediumAttachmentList::const_iterator
12231 it = mMediumAttachments->begin();
12232 it != mMediumAttachments->end();
12233 ++it)
12234 {
12235 MediumAttachment *pAttach = *it;
12236 /* just in case */
12237 AssertContinue(pAttach);
12238
12239 AutoCaller localAutoCallerA(pAttach);
12240 if (FAILED(localAutoCallerA.rc())) continue;
12241
12242 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12243
12244 if (pAttach->i_getType() == DeviceType_HardDisk)
12245 list.push_back(pAttach->i_getMedium());
12246 }
12247}
12248
12249void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12250{
12251 AssertReturnVoid(isWriteLockOnCurrentThread());
12252 AssertPtrReturnVoid(aCollector);
12253
12254 pm::CollectorHAL *hal = aCollector->getHAL();
12255 /* Create sub metrics */
12256 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12257 "Percentage of processor time spent in user mode by the VM process.");
12258 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12259 "Percentage of processor time spent in kernel mode by the VM process.");
12260 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12261 "Size of resident portion of VM process in memory.");
12262 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12263 "Actual size of all VM disks combined.");
12264 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12265 "Network receive rate.");
12266 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12267 "Network transmit rate.");
12268 /* Create and register base metrics */
12269 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12270 cpuLoadUser, cpuLoadKernel);
12271 aCollector->registerBaseMetric(cpuLoad);
12272 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12273 ramUsageUsed);
12274 aCollector->registerBaseMetric(ramUsage);
12275 MediaList disks;
12276 i_getDiskList(disks);
12277 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12278 diskUsageUsed);
12279 aCollector->registerBaseMetric(diskUsage);
12280
12281 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12282 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12283 new pm::AggregateAvg()));
12284 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12285 new pm::AggregateMin()));
12286 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12287 new pm::AggregateMax()));
12288 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12289 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12290 new pm::AggregateAvg()));
12291 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12292 new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12294 new pm::AggregateMax()));
12295
12296 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12297 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12298 new pm::AggregateAvg()));
12299 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12300 new pm::AggregateMin()));
12301 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12302 new pm::AggregateMax()));
12303
12304 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12305 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12306 new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12308 new pm::AggregateMin()));
12309 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12310 new pm::AggregateMax()));
12311
12312
12313 /* Guest metrics collector */
12314 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12315 aCollector->registerGuest(mCollectorGuest);
12316 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12317
12318 /* Create sub metrics */
12319 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12320 "Percentage of processor time spent in user mode as seen by the guest.");
12321 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12322 "Percentage of processor time spent in kernel mode as seen by the guest.");
12323 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12324 "Percentage of processor time spent idling as seen by the guest.");
12325
12326 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12327 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12328 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12329 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12330 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12331 pm::SubMetric *guestMemCache = new pm::SubMetric(
12332 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12333
12334 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12335 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12336
12337 /* Create and register base metrics */
12338 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12339 machineNetRx, machineNetTx);
12340 aCollector->registerBaseMetric(machineNetRate);
12341
12342 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12343 guestLoadUser, guestLoadKernel, guestLoadIdle);
12344 aCollector->registerBaseMetric(guestCpuLoad);
12345
12346 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12347 guestMemTotal, guestMemFree,
12348 guestMemBalloon, guestMemShared,
12349 guestMemCache, guestPagedTotal);
12350 aCollector->registerBaseMetric(guestCpuMem);
12351
12352 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12353 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12354 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12355 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12356
12357 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12358 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12359 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12360 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12361
12362 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12366
12367 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12371
12372 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12373 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12376
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12381
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12386
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12391
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12396
12397 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12400 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12401
12402 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12403 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12404 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12405 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12406}
12407
12408void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12409{
12410 AssertReturnVoid(isWriteLockOnCurrentThread());
12411
12412 if (aCollector)
12413 {
12414 aCollector->unregisterMetricsFor(aMachine);
12415 aCollector->unregisterBaseMetricsFor(aMachine);
12416 }
12417}
12418
12419#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12420
12421
12422////////////////////////////////////////////////////////////////////////////////
12423
12424DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12425
12426HRESULT SessionMachine::FinalConstruct()
12427{
12428 LogFlowThisFunc(("\n"));
12429
12430 mClientToken = NULL;
12431
12432 return BaseFinalConstruct();
12433}
12434
12435void SessionMachine::FinalRelease()
12436{
12437 LogFlowThisFunc(("\n"));
12438
12439 Assert(!mClientToken);
12440 /* paranoia, should not hang around any more */
12441 if (mClientToken)
12442 {
12443 delete mClientToken;
12444 mClientToken = NULL;
12445 }
12446
12447 uninit(Uninit::Unexpected);
12448
12449 BaseFinalRelease();
12450}
12451
12452/**
12453 * @note Must be called only by Machine::LockMachine() from its own write lock.
12454 */
12455HRESULT SessionMachine::init(Machine *aMachine)
12456{
12457 LogFlowThisFuncEnter();
12458 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12459
12460 AssertReturn(aMachine, E_INVALIDARG);
12461
12462 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12463
12464 /* Enclose the state transition NotReady->InInit->Ready */
12465 AutoInitSpan autoInitSpan(this);
12466 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12467
12468 HRESULT rc = S_OK;
12469
12470 RT_ZERO(mAuthLibCtx);
12471
12472 /* create the machine client token */
12473 try
12474 {
12475 mClientToken = new ClientToken(aMachine, this);
12476 if (!mClientToken->isReady())
12477 {
12478 delete mClientToken;
12479 mClientToken = NULL;
12480 rc = E_FAIL;
12481 }
12482 }
12483 catch (std::bad_alloc &)
12484 {
12485 rc = E_OUTOFMEMORY;
12486 }
12487 if (FAILED(rc))
12488 return rc;
12489
12490 /* memorize the peer Machine */
12491 unconst(mPeer) = aMachine;
12492 /* share the parent pointer */
12493 unconst(mParent) = aMachine->mParent;
12494
12495 /* take the pointers to data to share */
12496 mData.share(aMachine->mData);
12497 mSSData.share(aMachine->mSSData);
12498
12499 mUserData.share(aMachine->mUserData);
12500 mHWData.share(aMachine->mHWData);
12501 mMediumAttachments.share(aMachine->mMediumAttachments);
12502
12503 mStorageControllers.allocate();
12504 for (StorageControllerList::const_iterator
12505 it = aMachine->mStorageControllers->begin();
12506 it != aMachine->mStorageControllers->end();
12507 ++it)
12508 {
12509 ComObjPtr<StorageController> ctl;
12510 ctl.createObject();
12511 ctl->init(this, *it);
12512 mStorageControllers->push_back(ctl);
12513 }
12514
12515 mUSBControllers.allocate();
12516 for (USBControllerList::const_iterator
12517 it = aMachine->mUSBControllers->begin();
12518 it != aMachine->mUSBControllers->end();
12519 ++it)
12520 {
12521 ComObjPtr<USBController> ctl;
12522 ctl.createObject();
12523 ctl->init(this, *it);
12524 mUSBControllers->push_back(ctl);
12525 }
12526
12527 unconst(mBIOSSettings).createObject();
12528 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12529
12530 unconst(mTrustedPlatformModule).createObject();
12531 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12532
12533 unconst(mNvramStore).createObject();
12534 mNvramStore->init(this, aMachine->mNvramStore);
12535
12536 unconst(mRecordingSettings).createObject();
12537 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12538 /* create another GraphicsAdapter object that will be mutable */
12539 unconst(mGraphicsAdapter).createObject();
12540 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12541 /* create another VRDEServer object that will be mutable */
12542 unconst(mVRDEServer).createObject();
12543 mVRDEServer->init(this, aMachine->mVRDEServer);
12544 /* create another audio adapter object that will be mutable */
12545 unconst(mAudioAdapter).createObject();
12546 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12547 /* create a list of serial ports that will be mutable */
12548 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12549 {
12550 unconst(mSerialPorts[slot]).createObject();
12551 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12552 }
12553 /* create a list of parallel ports that will be mutable */
12554 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12555 {
12556 unconst(mParallelPorts[slot]).createObject();
12557 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12558 }
12559
12560 /* create another USB device filters object that will be mutable */
12561 unconst(mUSBDeviceFilters).createObject();
12562 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12563
12564 /* create a list of network adapters that will be mutable */
12565 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12566 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12567 {
12568 unconst(mNetworkAdapters[slot]).createObject();
12569 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12570 }
12571
12572 /* create another bandwidth control object that will be mutable */
12573 unconst(mBandwidthControl).createObject();
12574 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12575
12576 /* default is to delete saved state on Saved -> PoweredOff transition */
12577 mRemoveSavedState = true;
12578
12579 /* Confirm a successful initialization when it's the case */
12580 autoInitSpan.setSucceeded();
12581
12582 miNATNetworksStarted = 0;
12583
12584 LogFlowThisFuncLeave();
12585 return rc;
12586}
12587
12588/**
12589 * Uninitializes this session object. If the reason is other than
12590 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12591 * or the client watcher code.
12592 *
12593 * @param aReason uninitialization reason
12594 *
12595 * @note Locks mParent + this object for writing.
12596 */
12597void SessionMachine::uninit(Uninit::Reason aReason)
12598{
12599 LogFlowThisFuncEnter();
12600 LogFlowThisFunc(("reason=%d\n", aReason));
12601
12602 /*
12603 * Strongly reference ourselves to prevent this object deletion after
12604 * mData->mSession.mMachine.setNull() below (which can release the last
12605 * reference and call the destructor). Important: this must be done before
12606 * accessing any members (and before AutoUninitSpan that does it as well).
12607 * This self reference will be released as the very last step on return.
12608 */
12609 ComObjPtr<SessionMachine> selfRef;
12610 if (aReason != Uninit::Unexpected)
12611 selfRef = this;
12612
12613 /* Enclose the state transition Ready->InUninit->NotReady */
12614 AutoUninitSpan autoUninitSpan(this);
12615 if (autoUninitSpan.uninitDone())
12616 {
12617 LogFlowThisFunc(("Already uninitialized\n"));
12618 LogFlowThisFuncLeave();
12619 return;
12620 }
12621
12622 if (autoUninitSpan.initFailed())
12623 {
12624 /* We've been called by init() because it's failed. It's not really
12625 * necessary (nor it's safe) to perform the regular uninit sequence
12626 * below, the following is enough.
12627 */
12628 LogFlowThisFunc(("Initialization failed.\n"));
12629 /* destroy the machine client token */
12630 if (mClientToken)
12631 {
12632 delete mClientToken;
12633 mClientToken = NULL;
12634 }
12635 uninitDataAndChildObjects();
12636 mData.free();
12637 unconst(mParent) = NULL;
12638 unconst(mPeer) = NULL;
12639 LogFlowThisFuncLeave();
12640 return;
12641 }
12642
12643 MachineState_T lastState;
12644 {
12645 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12646 lastState = mData->mMachineState;
12647 }
12648 NOREF(lastState);
12649
12650#ifdef VBOX_WITH_USB
12651 // release all captured USB devices, but do this before requesting the locks below
12652 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12653 {
12654 /* Console::captureUSBDevices() is called in the VM process only after
12655 * setting the machine state to Starting or Restoring.
12656 * Console::detachAllUSBDevices() will be called upon successful
12657 * termination. So, we need to release USB devices only if there was
12658 * an abnormal termination of a running VM.
12659 *
12660 * This is identical to SessionMachine::DetachAllUSBDevices except
12661 * for the aAbnormal argument. */
12662 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12663 AssertComRC(rc);
12664 NOREF(rc);
12665
12666 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12667 if (service)
12668 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12669 }
12670#endif /* VBOX_WITH_USB */
12671
12672 // we need to lock this object in uninit() because the lock is shared
12673 // with mPeer (as well as data we modify below). mParent lock is needed
12674 // by several calls to it.
12675 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12676
12677#ifdef VBOX_WITH_RESOURCE_USAGE_API
12678 /*
12679 * It is safe to call Machine::i_unregisterMetrics() here because
12680 * PerformanceCollector::samplerCallback no longer accesses guest methods
12681 * holding the lock.
12682 */
12683 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12684 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12685 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12686 if (mCollectorGuest)
12687 {
12688 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12689 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12690 mCollectorGuest = NULL;
12691 }
12692#endif
12693
12694 if (aReason == Uninit::Abnormal)
12695 {
12696 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12697
12698 /*
12699 * Move the VM to the 'Aborted' machine state unless we are restoring a
12700 * VM that was in the 'Saved' machine state. In that case, if the VM
12701 * fails before reaching either the 'Restoring' machine state or the
12702 * 'Running' machine state then we set the machine state to
12703 * 'AbortedSaved' in order to preserve the saved state file so that the
12704 * VM can be restored in the future.
12705 */
12706 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12707 i_setMachineState(MachineState_AbortedSaved);
12708 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12709 i_setMachineState(MachineState_Aborted);
12710 }
12711
12712 // any machine settings modified?
12713 if (mData->flModifications)
12714 {
12715 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12716 i_rollback(false /* aNotify */);
12717 }
12718
12719 mData->mSession.mPID = NIL_RTPROCESS;
12720
12721 if (aReason == Uninit::Unexpected)
12722 {
12723 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12724 * client watcher thread to update the set of machines that have open
12725 * sessions. */
12726 mParent->i_updateClientWatcher();
12727 }
12728
12729 /* uninitialize all remote controls */
12730 if (mData->mSession.mRemoteControls.size())
12731 {
12732 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12733 mData->mSession.mRemoteControls.size()));
12734
12735 /* Always restart a the beginning, since the iterator is invalidated
12736 * by using erase(). */
12737 for (Data::Session::RemoteControlList::iterator
12738 it = mData->mSession.mRemoteControls.begin();
12739 it != mData->mSession.mRemoteControls.end();
12740 it = mData->mSession.mRemoteControls.begin())
12741 {
12742 ComPtr<IInternalSessionControl> pControl = *it;
12743 mData->mSession.mRemoteControls.erase(it);
12744 multilock.release();
12745 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12746 HRESULT rc = pControl->Uninitialize();
12747 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12748 if (FAILED(rc))
12749 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12750 multilock.acquire();
12751 }
12752 mData->mSession.mRemoteControls.clear();
12753 }
12754
12755 /* Remove all references to the NAT network service. The service will stop
12756 * if all references (also from other VMs) are removed. */
12757 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12758 {
12759 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12760 {
12761 BOOL enabled;
12762 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12763 if ( FAILED(hrc)
12764 || !enabled)
12765 continue;
12766
12767 NetworkAttachmentType_T type;
12768 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12769 if ( SUCCEEDED(hrc)
12770 && type == NetworkAttachmentType_NATNetwork)
12771 {
12772 Bstr name;
12773 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12774 if (SUCCEEDED(hrc))
12775 {
12776 multilock.release();
12777 Utf8Str strName(name);
12778 LogRel(("VM '%s' stops using NAT network '%s'\n",
12779 mUserData->s.strName.c_str(), strName.c_str()));
12780 mParent->i_natNetworkRefDec(strName);
12781 multilock.acquire();
12782 }
12783 }
12784 }
12785 }
12786
12787 /*
12788 * An expected uninitialization can come only from #i_checkForDeath().
12789 * Otherwise it means that something's gone really wrong (for example,
12790 * the Session implementation has released the VirtualBox reference
12791 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12792 * etc). However, it's also possible, that the client releases the IPC
12793 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12794 * but the VirtualBox release event comes first to the server process.
12795 * This case is practically possible, so we should not assert on an
12796 * unexpected uninit, just log a warning.
12797 */
12798
12799 if (aReason == Uninit::Unexpected)
12800 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12801
12802 if (aReason != Uninit::Normal)
12803 {
12804 mData->mSession.mDirectControl.setNull();
12805 }
12806 else
12807 {
12808 /* this must be null here (see #OnSessionEnd()) */
12809 Assert(mData->mSession.mDirectControl.isNull());
12810 Assert(mData->mSession.mState == SessionState_Unlocking);
12811 Assert(!mData->mSession.mProgress.isNull());
12812 }
12813 if (mData->mSession.mProgress)
12814 {
12815 if (aReason == Uninit::Normal)
12816 mData->mSession.mProgress->i_notifyComplete(S_OK);
12817 else
12818 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12819 COM_IIDOF(ISession),
12820 getComponentName(),
12821 tr("The VM session was aborted"));
12822 mData->mSession.mProgress.setNull();
12823 }
12824
12825 if (mConsoleTaskData.mProgress)
12826 {
12827 Assert(aReason == Uninit::Abnormal);
12828 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12829 COM_IIDOF(ISession),
12830 getComponentName(),
12831 tr("The VM session was aborted"));
12832 mConsoleTaskData.mProgress.setNull();
12833 }
12834
12835 /* remove the association between the peer machine and this session machine */
12836 Assert( (SessionMachine*)mData->mSession.mMachine == this
12837 || aReason == Uninit::Unexpected);
12838
12839 /* reset the rest of session data */
12840 mData->mSession.mLockType = LockType_Null;
12841 mData->mSession.mMachine.setNull();
12842 mData->mSession.mState = SessionState_Unlocked;
12843 mData->mSession.mName.setNull();
12844
12845 /* destroy the machine client token before leaving the exclusive lock */
12846 if (mClientToken)
12847 {
12848 delete mClientToken;
12849 mClientToken = NULL;
12850 }
12851
12852 /* fire an event */
12853 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12854
12855 uninitDataAndChildObjects();
12856
12857 /* free the essential data structure last */
12858 mData.free();
12859
12860 /* release the exclusive lock before setting the below two to NULL */
12861 multilock.release();
12862
12863 unconst(mParent) = NULL;
12864 unconst(mPeer) = NULL;
12865
12866 AuthLibUnload(&mAuthLibCtx);
12867
12868 LogFlowThisFuncLeave();
12869}
12870
12871// util::Lockable interface
12872////////////////////////////////////////////////////////////////////////////////
12873
12874/**
12875 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12876 * with the primary Machine instance (mPeer).
12877 */
12878RWLockHandle *SessionMachine::lockHandle() const
12879{
12880 AssertReturn(mPeer != NULL, NULL);
12881 return mPeer->lockHandle();
12882}
12883
12884// IInternalMachineControl methods
12885////////////////////////////////////////////////////////////////////////////////
12886
12887/**
12888 * Passes collected guest statistics to performance collector object
12889 */
12890HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12891 ULONG aCpuKernel, ULONG aCpuIdle,
12892 ULONG aMemTotal, ULONG aMemFree,
12893 ULONG aMemBalloon, ULONG aMemShared,
12894 ULONG aMemCache, ULONG aPageTotal,
12895 ULONG aAllocVMM, ULONG aFreeVMM,
12896 ULONG aBalloonedVMM, ULONG aSharedVMM,
12897 ULONG aVmNetRx, ULONG aVmNetTx)
12898{
12899#ifdef VBOX_WITH_RESOURCE_USAGE_API
12900 if (mCollectorGuest)
12901 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12902 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12903 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12904 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12905
12906 return S_OK;
12907#else
12908 NOREF(aValidStats);
12909 NOREF(aCpuUser);
12910 NOREF(aCpuKernel);
12911 NOREF(aCpuIdle);
12912 NOREF(aMemTotal);
12913 NOREF(aMemFree);
12914 NOREF(aMemBalloon);
12915 NOREF(aMemShared);
12916 NOREF(aMemCache);
12917 NOREF(aPageTotal);
12918 NOREF(aAllocVMM);
12919 NOREF(aFreeVMM);
12920 NOREF(aBalloonedVMM);
12921 NOREF(aSharedVMM);
12922 NOREF(aVmNetRx);
12923 NOREF(aVmNetTx);
12924 return E_NOTIMPL;
12925#endif
12926}
12927
12928////////////////////////////////////////////////////////////////////////////////
12929//
12930// SessionMachine task records
12931//
12932////////////////////////////////////////////////////////////////////////////////
12933
12934/**
12935 * Task record for saving the machine state.
12936 */
12937class SessionMachine::SaveStateTask
12938 : public Machine::Task
12939{
12940public:
12941 SaveStateTask(SessionMachine *m,
12942 Progress *p,
12943 const Utf8Str &t,
12944 Reason_T enmReason,
12945 const Utf8Str &strStateFilePath)
12946 : Task(m, p, t),
12947 m_enmReason(enmReason),
12948 m_strStateFilePath(strStateFilePath)
12949 {}
12950
12951private:
12952 void handler()
12953 {
12954 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12955 }
12956
12957 Reason_T m_enmReason;
12958 Utf8Str m_strStateFilePath;
12959
12960 friend class SessionMachine;
12961};
12962
12963/**
12964 * Task thread implementation for SessionMachine::SaveState(), called from
12965 * SessionMachine::taskHandler().
12966 *
12967 * @note Locks this object for writing.
12968 *
12969 * @param task
12970 * @return
12971 */
12972void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12973{
12974 LogFlowThisFuncEnter();
12975
12976 AutoCaller autoCaller(this);
12977 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12978 if (FAILED(autoCaller.rc()))
12979 {
12980 /* we might have been uninitialized because the session was accidentally
12981 * closed by the client, so don't assert */
12982 HRESULT rc = setError(E_FAIL,
12983 tr("The session has been accidentally closed"));
12984 task.m_pProgress->i_notifyComplete(rc);
12985 LogFlowThisFuncLeave();
12986 return;
12987 }
12988
12989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12990
12991 HRESULT rc = S_OK;
12992
12993 try
12994 {
12995 ComPtr<IInternalSessionControl> directControl;
12996 if (mData->mSession.mLockType == LockType_VM)
12997 directControl = mData->mSession.mDirectControl;
12998 if (directControl.isNull())
12999 throw setError(VBOX_E_INVALID_VM_STATE,
13000 tr("Trying to save state without a running VM"));
13001 alock.release();
13002 BOOL fSuspendedBySave;
13003 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13004 Assert(!fSuspendedBySave);
13005 alock.acquire();
13006
13007 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13008 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13009 throw E_FAIL);
13010
13011 if (SUCCEEDED(rc))
13012 {
13013 mSSData->strStateFilePath = task.m_strStateFilePath;
13014
13015 /* save all VM settings */
13016 rc = i_saveSettings(NULL, alock);
13017 // no need to check whether VirtualBox.xml needs saving also since
13018 // we can't have a name change pending at this point
13019 }
13020 else
13021 {
13022 // On failure, set the state to the state we had at the beginning.
13023 i_setMachineState(task.m_machineStateBackup);
13024 i_updateMachineStateOnClient();
13025
13026 // Delete the saved state file (might have been already created).
13027 // No need to check whether this is shared with a snapshot here
13028 // because we certainly created a fresh saved state file here.
13029 RTFileDelete(task.m_strStateFilePath.c_str());
13030 }
13031 }
13032 catch (HRESULT aRC) { rc = aRC; }
13033
13034 task.m_pProgress->i_notifyComplete(rc);
13035
13036 LogFlowThisFuncLeave();
13037}
13038
13039/**
13040 * @note Locks this object for writing.
13041 */
13042HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13043{
13044 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13045}
13046
13047HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13048{
13049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13050
13051 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13052 if (FAILED(rc)) return rc;
13053
13054 if ( mData->mMachineState != MachineState_Running
13055 && mData->mMachineState != MachineState_Paused
13056 )
13057 return setError(VBOX_E_INVALID_VM_STATE,
13058 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13059 Global::stringifyMachineState(mData->mMachineState));
13060
13061 ComObjPtr<Progress> pProgress;
13062 pProgress.createObject();
13063 rc = pProgress->init(i_getVirtualBox(),
13064 static_cast<IMachine *>(this) /* aInitiator */,
13065 tr("Saving the execution state of the virtual machine"),
13066 FALSE /* aCancelable */);
13067 if (FAILED(rc))
13068 return rc;
13069
13070 Utf8Str strStateFilePath;
13071 i_composeSavedStateFilename(strStateFilePath);
13072
13073 /* create and start the task on a separate thread (note that it will not
13074 * start working until we release alock) */
13075 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13076 rc = pTask->createThread();
13077 if (FAILED(rc))
13078 return rc;
13079
13080 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13081 i_setMachineState(MachineState_Saving);
13082 i_updateMachineStateOnClient();
13083
13084 pProgress.queryInterfaceTo(aProgress.asOutParam());
13085
13086 return S_OK;
13087}
13088
13089/**
13090 * @note Locks this object for writing.
13091 */
13092HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13093{
13094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13095
13096 HRESULT rc = i_checkStateDependency(MutableStateDep);
13097 if (FAILED(rc)) return rc;
13098
13099 if ( mData->mMachineState != MachineState_PoweredOff
13100 && mData->mMachineState != MachineState_Teleported
13101 && mData->mMachineState != MachineState_Aborted
13102 )
13103 return setError(VBOX_E_INVALID_VM_STATE,
13104 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13105 Global::stringifyMachineState(mData->mMachineState));
13106
13107 com::Utf8Str stateFilePathFull;
13108 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13109 if (RT_FAILURE(vrc))
13110 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13111 tr("Invalid saved state file path '%s' (%Rrc)"),
13112 aSavedStateFile.c_str(),
13113 vrc);
13114
13115 mSSData->strStateFilePath = stateFilePathFull;
13116
13117 /* The below i_setMachineState() will detect the state transition and will
13118 * update the settings file */
13119
13120 return i_setMachineState(MachineState_Saved);
13121}
13122
13123/**
13124 * @note Locks this object for writing.
13125 */
13126HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13127{
13128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13129
13130 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13131 if (FAILED(rc)) return rc;
13132
13133 if ( mData->mMachineState != MachineState_Saved
13134 && mData->mMachineState != MachineState_AbortedSaved)
13135 return setError(VBOX_E_INVALID_VM_STATE,
13136 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13137 Global::stringifyMachineState(mData->mMachineState));
13138
13139 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13140
13141 /*
13142 * Saved -> PoweredOff transition will be detected in the SessionMachine
13143 * and properly handled.
13144 */
13145 rc = i_setMachineState(MachineState_PoweredOff);
13146 return rc;
13147}
13148
13149
13150/**
13151 * @note Locks the same as #i_setMachineState() does.
13152 */
13153HRESULT SessionMachine::updateState(MachineState_T aState)
13154{
13155 return i_setMachineState(aState);
13156}
13157
13158/**
13159 * @note Locks this object for writing.
13160 */
13161HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13162{
13163 IProgress *pProgress(aProgress);
13164
13165 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13166
13167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13168
13169 if (mData->mSession.mState != SessionState_Locked)
13170 return VBOX_E_INVALID_OBJECT_STATE;
13171
13172 if (!mData->mSession.mProgress.isNull())
13173 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13174
13175 /* If we didn't reference the NAT network service yet, add a reference to
13176 * force a start */
13177 if (miNATNetworksStarted < 1)
13178 {
13179 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13180 {
13181 BOOL enabled;
13182 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13183 if ( FAILED(hrc)
13184 || !enabled)
13185 continue;
13186
13187 NetworkAttachmentType_T type;
13188 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13189 if ( SUCCEEDED(hrc)
13190 && type == NetworkAttachmentType_NATNetwork)
13191 {
13192 Bstr name;
13193 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13194 if (SUCCEEDED(hrc))
13195 {
13196 Utf8Str strName(name);
13197 LogRel(("VM '%s' starts using NAT network '%s'\n",
13198 mUserData->s.strName.c_str(), strName.c_str()));
13199 mPeer->lockHandle()->unlockWrite();
13200 mParent->i_natNetworkRefInc(strName);
13201#ifdef RT_LOCK_STRICT
13202 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13203#else
13204 mPeer->lockHandle()->lockWrite();
13205#endif
13206 }
13207 }
13208 }
13209 miNATNetworksStarted++;
13210 }
13211
13212 LogFlowThisFunc(("returns S_OK.\n"));
13213 return S_OK;
13214}
13215
13216/**
13217 * @note Locks this object for writing.
13218 */
13219HRESULT SessionMachine::endPowerUp(LONG aResult)
13220{
13221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13222
13223 if (mData->mSession.mState != SessionState_Locked)
13224 return VBOX_E_INVALID_OBJECT_STATE;
13225
13226 /* Finalize the LaunchVMProcess progress object. */
13227 if (mData->mSession.mProgress)
13228 {
13229 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13230 mData->mSession.mProgress.setNull();
13231 }
13232
13233 if (SUCCEEDED((HRESULT)aResult))
13234 {
13235#ifdef VBOX_WITH_RESOURCE_USAGE_API
13236 /* The VM has been powered up successfully, so it makes sense
13237 * now to offer the performance metrics for a running machine
13238 * object. Doing it earlier wouldn't be safe. */
13239 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13240 mData->mSession.mPID);
13241#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13242 }
13243
13244 return S_OK;
13245}
13246
13247/**
13248 * @note Locks this object for writing.
13249 */
13250HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13251{
13252 LogFlowThisFuncEnter();
13253
13254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13255
13256 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13257 E_FAIL);
13258
13259 /* create a progress object to track operation completion */
13260 ComObjPtr<Progress> pProgress;
13261 pProgress.createObject();
13262 pProgress->init(i_getVirtualBox(),
13263 static_cast<IMachine *>(this) /* aInitiator */,
13264 tr("Stopping the virtual machine"),
13265 FALSE /* aCancelable */);
13266
13267 /* fill in the console task data */
13268 mConsoleTaskData.mLastState = mData->mMachineState;
13269 mConsoleTaskData.mProgress = pProgress;
13270
13271 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13272 i_setMachineState(MachineState_Stopping);
13273
13274 pProgress.queryInterfaceTo(aProgress.asOutParam());
13275
13276 return S_OK;
13277}
13278
13279/**
13280 * @note Locks this object for writing.
13281 */
13282HRESULT SessionMachine::endPoweringDown(LONG aResult,
13283 const com::Utf8Str &aErrMsg)
13284{
13285 HRESULT const hrcResult = (HRESULT)aResult;
13286 LogFlowThisFuncEnter();
13287
13288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13289
13290 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13291 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13292 && mConsoleTaskData.mLastState != MachineState_Null,
13293 E_FAIL);
13294
13295 /*
13296 * On failure, set the state to the state we had when BeginPoweringDown()
13297 * was called (this is expected by Console::PowerDown() and the associated
13298 * task). On success the VM process already changed the state to
13299 * MachineState_PoweredOff, so no need to do anything.
13300 */
13301 if (FAILED(hrcResult))
13302 i_setMachineState(mConsoleTaskData.mLastState);
13303
13304 /* notify the progress object about operation completion */
13305 Assert(mConsoleTaskData.mProgress);
13306 if (SUCCEEDED(hrcResult))
13307 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13308 else
13309 {
13310 if (aErrMsg.length())
13311 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13312 COM_IIDOF(ISession),
13313 getComponentName(),
13314 aErrMsg.c_str());
13315 else
13316 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13317 }
13318
13319 /* clear out the temporary saved state data */
13320 mConsoleTaskData.mLastState = MachineState_Null;
13321 mConsoleTaskData.mProgress.setNull();
13322
13323 LogFlowThisFuncLeave();
13324 return S_OK;
13325}
13326
13327
13328/**
13329 * Goes through the USB filters of the given machine to see if the given
13330 * device matches any filter or not.
13331 *
13332 * @note Locks the same as USBController::hasMatchingFilter() does.
13333 */
13334HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13335 BOOL *aMatched,
13336 ULONG *aMaskedInterfaces)
13337{
13338 LogFlowThisFunc(("\n"));
13339
13340#ifdef VBOX_WITH_USB
13341 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13342#else
13343 NOREF(aDevice);
13344 NOREF(aMaskedInterfaces);
13345 *aMatched = FALSE;
13346#endif
13347
13348 return S_OK;
13349}
13350
13351/**
13352 * @note Locks the same as Host::captureUSBDevice() does.
13353 */
13354HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13355{
13356 LogFlowThisFunc(("\n"));
13357
13358#ifdef VBOX_WITH_USB
13359 /* if captureDeviceForVM() fails, it must have set extended error info */
13360 clearError();
13361 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13362 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13363 return rc;
13364
13365 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13366 AssertReturn(service, E_FAIL);
13367 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13368#else
13369 RT_NOREF(aId, aCaptureFilename);
13370 return E_NOTIMPL;
13371#endif
13372}
13373
13374/**
13375 * @note Locks the same as Host::detachUSBDevice() does.
13376 */
13377HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13378 BOOL aDone)
13379{
13380 LogFlowThisFunc(("\n"));
13381
13382#ifdef VBOX_WITH_USB
13383 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13384 AssertReturn(service, E_FAIL);
13385 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13386#else
13387 NOREF(aId);
13388 NOREF(aDone);
13389 return E_NOTIMPL;
13390#endif
13391}
13392
13393/**
13394 * Inserts all machine filters to the USB proxy service and then calls
13395 * Host::autoCaptureUSBDevices().
13396 *
13397 * Called by Console from the VM process upon VM startup.
13398 *
13399 * @note Locks what called methods lock.
13400 */
13401HRESULT SessionMachine::autoCaptureUSBDevices()
13402{
13403 LogFlowThisFunc(("\n"));
13404
13405#ifdef VBOX_WITH_USB
13406 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13407 AssertComRC(rc);
13408 NOREF(rc);
13409
13410 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13411 AssertReturn(service, E_FAIL);
13412 return service->autoCaptureDevicesForVM(this);
13413#else
13414 return S_OK;
13415#endif
13416}
13417
13418/**
13419 * Removes all machine filters from the USB proxy service and then calls
13420 * Host::detachAllUSBDevices().
13421 *
13422 * Called by Console from the VM process upon normal VM termination or by
13423 * SessionMachine::uninit() upon abnormal VM termination (from under the
13424 * Machine/SessionMachine lock).
13425 *
13426 * @note Locks what called methods lock.
13427 */
13428HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13429{
13430 LogFlowThisFunc(("\n"));
13431
13432#ifdef VBOX_WITH_USB
13433 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13434 AssertComRC(rc);
13435 NOREF(rc);
13436
13437 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13438 AssertReturn(service, E_FAIL);
13439 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13440#else
13441 NOREF(aDone);
13442 return S_OK;
13443#endif
13444}
13445
13446/**
13447 * @note Locks this object for writing.
13448 */
13449HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13450 ComPtr<IProgress> &aProgress)
13451{
13452 LogFlowThisFuncEnter();
13453
13454 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13455 /*
13456 * We don't assert below because it might happen that a non-direct session
13457 * informs us it is closed right after we've been uninitialized -- it's ok.
13458 */
13459
13460 /* get IInternalSessionControl interface */
13461 ComPtr<IInternalSessionControl> control(aSession);
13462
13463 ComAssertRet(!control.isNull(), E_INVALIDARG);
13464
13465 /* Creating a Progress object requires the VirtualBox lock, and
13466 * thus locking it here is required by the lock order rules. */
13467 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13468
13469 if (control == mData->mSession.mDirectControl)
13470 {
13471 /* The direct session is being normally closed by the client process
13472 * ----------------------------------------------------------------- */
13473
13474 /* go to the closing state (essential for all open*Session() calls and
13475 * for #i_checkForDeath()) */
13476 Assert(mData->mSession.mState == SessionState_Locked);
13477 mData->mSession.mState = SessionState_Unlocking;
13478
13479 /* set direct control to NULL to release the remote instance */
13480 mData->mSession.mDirectControl.setNull();
13481 LogFlowThisFunc(("Direct control is set to NULL\n"));
13482
13483 if (mData->mSession.mProgress)
13484 {
13485 /* finalize the progress, someone might wait if a frontend
13486 * closes the session before powering on the VM. */
13487 mData->mSession.mProgress->notifyComplete(E_FAIL,
13488 COM_IIDOF(ISession),
13489 getComponentName(),
13490 tr("The VM session was closed before any attempt to power it on"));
13491 mData->mSession.mProgress.setNull();
13492 }
13493
13494 /* Create the progress object the client will use to wait until
13495 * #i_checkForDeath() is called to uninitialize this session object after
13496 * it releases the IPC semaphore.
13497 * Note! Because we're "reusing" mProgress here, this must be a proxy
13498 * object just like for LaunchVMProcess. */
13499 Assert(mData->mSession.mProgress.isNull());
13500 ComObjPtr<ProgressProxy> progress;
13501 progress.createObject();
13502 ComPtr<IUnknown> pPeer(mPeer);
13503 progress->init(mParent, pPeer,
13504 Bstr(tr("Closing session")).raw(),
13505 FALSE /* aCancelable */);
13506 progress.queryInterfaceTo(aProgress.asOutParam());
13507 mData->mSession.mProgress = progress;
13508 }
13509 else
13510 {
13511 /* the remote session is being normally closed */
13512 bool found = false;
13513 for (Data::Session::RemoteControlList::iterator
13514 it = mData->mSession.mRemoteControls.begin();
13515 it != mData->mSession.mRemoteControls.end();
13516 ++it)
13517 {
13518 if (control == *it)
13519 {
13520 found = true;
13521 // This MUST be erase(it), not remove(*it) as the latter
13522 // triggers a very nasty use after free due to the place where
13523 // the value "lives".
13524 mData->mSession.mRemoteControls.erase(it);
13525 break;
13526 }
13527 }
13528 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13529 E_INVALIDARG);
13530 }
13531
13532 /* signal the client watcher thread, because the client is going away */
13533 mParent->i_updateClientWatcher();
13534
13535 LogFlowThisFuncLeave();
13536 return S_OK;
13537}
13538
13539HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13540 std::vector<com::Utf8Str> &aValues,
13541 std::vector<LONG64> &aTimestamps,
13542 std::vector<com::Utf8Str> &aFlags)
13543{
13544 LogFlowThisFunc(("\n"));
13545
13546#ifdef VBOX_WITH_GUEST_PROPS
13547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13548
13549 size_t cEntries = mHWData->mGuestProperties.size();
13550 aNames.resize(cEntries);
13551 aValues.resize(cEntries);
13552 aTimestamps.resize(cEntries);
13553 aFlags.resize(cEntries);
13554
13555 size_t i = 0;
13556 for (HWData::GuestPropertyMap::const_iterator
13557 it = mHWData->mGuestProperties.begin();
13558 it != mHWData->mGuestProperties.end();
13559 ++it, ++i)
13560 {
13561 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13562 aNames[i] = it->first;
13563 aValues[i] = it->second.strValue;
13564 aTimestamps[i] = it->second.mTimestamp;
13565
13566 /* If it is NULL, keep it NULL. */
13567 if (it->second.mFlags)
13568 {
13569 GuestPropWriteFlags(it->second.mFlags, szFlags);
13570 aFlags[i] = szFlags;
13571 }
13572 else
13573 aFlags[i] = "";
13574 }
13575 return S_OK;
13576#else
13577 ReturnComNotImplemented();
13578#endif
13579}
13580
13581HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13582 const com::Utf8Str &aValue,
13583 LONG64 aTimestamp,
13584 const com::Utf8Str &aFlags)
13585{
13586 LogFlowThisFunc(("\n"));
13587
13588#ifdef VBOX_WITH_GUEST_PROPS
13589 try
13590 {
13591 /*
13592 * Convert input up front.
13593 */
13594 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13595 if (aFlags.length())
13596 {
13597 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13598 AssertRCReturn(vrc, E_INVALIDARG);
13599 }
13600
13601 /*
13602 * Now grab the object lock, validate the state and do the update.
13603 */
13604
13605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13606
13607 if (!Global::IsOnline(mData->mMachineState))
13608 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13609
13610 i_setModified(IsModified_MachineData);
13611 mHWData.backup();
13612
13613 bool fDelete = !aValue.length();
13614 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13615 if (it != mHWData->mGuestProperties.end())
13616 {
13617 if (!fDelete)
13618 {
13619 it->second.strValue = aValue;
13620 it->second.mTimestamp = aTimestamp;
13621 it->second.mFlags = fFlags;
13622 }
13623 else
13624 mHWData->mGuestProperties.erase(it);
13625
13626 mData->mGuestPropertiesModified = TRUE;
13627 }
13628 else if (!fDelete)
13629 {
13630 HWData::GuestProperty prop;
13631 prop.strValue = aValue;
13632 prop.mTimestamp = aTimestamp;
13633 prop.mFlags = fFlags;
13634
13635 mHWData->mGuestProperties[aName] = prop;
13636 mData->mGuestPropertiesModified = TRUE;
13637 }
13638
13639 alock.release();
13640
13641 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13642 }
13643 catch (...)
13644 {
13645 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13646 }
13647 return S_OK;
13648#else
13649 ReturnComNotImplemented();
13650#endif
13651}
13652
13653
13654HRESULT SessionMachine::lockMedia()
13655{
13656 AutoMultiWriteLock2 alock(this->lockHandle(),
13657 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13658
13659 AssertReturn( mData->mMachineState == MachineState_Starting
13660 || mData->mMachineState == MachineState_Restoring
13661 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13662
13663 clearError();
13664 alock.release();
13665 return i_lockMedia();
13666}
13667
13668HRESULT SessionMachine::unlockMedia()
13669{
13670 HRESULT hrc = i_unlockMedia();
13671 return hrc;
13672}
13673
13674HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13675 ComPtr<IMediumAttachment> &aNewAttachment)
13676{
13677 // request the host lock first, since might be calling Host methods for getting host drives;
13678 // next, protect the media tree all the while we're in here, as well as our member variables
13679 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13680 this->lockHandle(),
13681 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13682
13683 IMediumAttachment *iAttach = aAttachment;
13684 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13685
13686 Utf8Str ctrlName;
13687 LONG lPort;
13688 LONG lDevice;
13689 bool fTempEject;
13690 {
13691 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13692
13693 /* Need to query the details first, as the IMediumAttachment reference
13694 * might be to the original settings, which we are going to change. */
13695 ctrlName = pAttach->i_getControllerName();
13696 lPort = pAttach->i_getPort();
13697 lDevice = pAttach->i_getDevice();
13698 fTempEject = pAttach->i_getTempEject();
13699 }
13700
13701 if (!fTempEject)
13702 {
13703 /* Remember previously mounted medium. The medium before taking the
13704 * backup is not necessarily the same thing. */
13705 ComObjPtr<Medium> oldmedium;
13706 oldmedium = pAttach->i_getMedium();
13707
13708 i_setModified(IsModified_Storage);
13709 mMediumAttachments.backup();
13710
13711 // The backup operation makes the pAttach reference point to the
13712 // old settings. Re-get the correct reference.
13713 pAttach = i_findAttachment(*mMediumAttachments.data(),
13714 ctrlName,
13715 lPort,
13716 lDevice);
13717
13718 {
13719 AutoCaller autoAttachCaller(this);
13720 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13721
13722 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13723 if (!oldmedium.isNull())
13724 oldmedium->i_removeBackReference(mData->mUuid);
13725
13726 pAttach->i_updateMedium(NULL);
13727 pAttach->i_updateEjected();
13728 }
13729
13730 i_setModified(IsModified_Storage);
13731 }
13732 else
13733 {
13734 {
13735 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13736 pAttach->i_updateEjected();
13737 }
13738 }
13739
13740 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13741
13742 return S_OK;
13743}
13744
13745HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13746 com::Utf8Str &aResult)
13747{
13748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13749
13750 HRESULT hr = S_OK;
13751
13752 if (!mAuthLibCtx.hAuthLibrary)
13753 {
13754 /* Load the external authentication library. */
13755 Bstr authLibrary;
13756 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13757
13758 Utf8Str filename = authLibrary;
13759
13760 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13761 if (RT_FAILURE(vrc))
13762 hr = setErrorBoth(E_FAIL, vrc,
13763 tr("Could not load the external authentication library '%s' (%Rrc)"),
13764 filename.c_str(), vrc);
13765 }
13766
13767 /* The auth library might need the machine lock. */
13768 alock.release();
13769
13770 if (FAILED(hr))
13771 return hr;
13772
13773 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13774 {
13775 enum VRDEAuthParams
13776 {
13777 parmUuid = 1,
13778 parmGuestJudgement,
13779 parmUser,
13780 parmPassword,
13781 parmDomain,
13782 parmClientId
13783 };
13784
13785 AuthResult result = AuthResultAccessDenied;
13786
13787 Guid uuid(aAuthParams[parmUuid]);
13788 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13789 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13790
13791 result = AuthLibAuthenticate(&mAuthLibCtx,
13792 uuid.raw(), guestJudgement,
13793 aAuthParams[parmUser].c_str(),
13794 aAuthParams[parmPassword].c_str(),
13795 aAuthParams[parmDomain].c_str(),
13796 u32ClientId);
13797
13798 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13799 size_t cbPassword = aAuthParams[parmPassword].length();
13800 if (cbPassword)
13801 {
13802 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13803 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13804 }
13805
13806 if (result == AuthResultAccessGranted)
13807 aResult = "granted";
13808 else
13809 aResult = "denied";
13810
13811 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13812 aAuthParams[parmUser].c_str(), aResult.c_str()));
13813 }
13814 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13815 {
13816 enum VRDEAuthDisconnectParams
13817 {
13818 parmUuid = 1,
13819 parmClientId
13820 };
13821
13822 Guid uuid(aAuthParams[parmUuid]);
13823 uint32_t u32ClientId = 0;
13824 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13825 }
13826 else
13827 {
13828 hr = E_INVALIDARG;
13829 }
13830
13831 return hr;
13832}
13833
13834// public methods only for internal purposes
13835/////////////////////////////////////////////////////////////////////////////
13836
13837#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13838/**
13839 * Called from the client watcher thread to check for expected or unexpected
13840 * death of the client process that has a direct session to this machine.
13841 *
13842 * On Win32 and on OS/2, this method is called only when we've got the
13843 * mutex (i.e. the client has either died or terminated normally) so it always
13844 * returns @c true (the client is terminated, the session machine is
13845 * uninitialized).
13846 *
13847 * On other platforms, the method returns @c true if the client process has
13848 * terminated normally or abnormally and the session machine was uninitialized,
13849 * and @c false if the client process is still alive.
13850 *
13851 * @note Locks this object for writing.
13852 */
13853bool SessionMachine::i_checkForDeath()
13854{
13855 Uninit::Reason reason;
13856 bool terminated = false;
13857
13858 /* Enclose autoCaller with a block because calling uninit() from under it
13859 * will deadlock. */
13860 {
13861 AutoCaller autoCaller(this);
13862 if (!autoCaller.isOk())
13863 {
13864 /* return true if not ready, to cause the client watcher to exclude
13865 * the corresponding session from watching */
13866 LogFlowThisFunc(("Already uninitialized!\n"));
13867 return true;
13868 }
13869
13870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13871
13872 /* Determine the reason of death: if the session state is Closing here,
13873 * everything is fine. Otherwise it means that the client did not call
13874 * OnSessionEnd() before it released the IPC semaphore. This may happen
13875 * either because the client process has abnormally terminated, or
13876 * because it simply forgot to call ISession::Close() before exiting. We
13877 * threat the latter also as an abnormal termination (see
13878 * Session::uninit() for details). */
13879 reason = mData->mSession.mState == SessionState_Unlocking ?
13880 Uninit::Normal :
13881 Uninit::Abnormal;
13882
13883 if (mClientToken)
13884 terminated = mClientToken->release();
13885 } /* AutoCaller block */
13886
13887 if (terminated)
13888 uninit(reason);
13889
13890 return terminated;
13891}
13892
13893void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13894{
13895 LogFlowThisFunc(("\n"));
13896
13897 strTokenId.setNull();
13898
13899 AutoCaller autoCaller(this);
13900 AssertComRCReturnVoid(autoCaller.rc());
13901
13902 Assert(mClientToken);
13903 if (mClientToken)
13904 mClientToken->getId(strTokenId);
13905}
13906#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13907IToken *SessionMachine::i_getToken()
13908{
13909 LogFlowThisFunc(("\n"));
13910
13911 AutoCaller autoCaller(this);
13912 AssertComRCReturn(autoCaller.rc(), NULL);
13913
13914 Assert(mClientToken);
13915 if (mClientToken)
13916 return mClientToken->getToken();
13917 else
13918 return NULL;
13919}
13920#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13921
13922Machine::ClientToken *SessionMachine::i_getClientToken()
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926 AutoCaller autoCaller(this);
13927 AssertComRCReturn(autoCaller.rc(), NULL);
13928
13929 return mClientToken;
13930}
13931
13932
13933/**
13934 * @note Locks this object for reading.
13935 */
13936HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13937{
13938 LogFlowThisFunc(("\n"));
13939
13940 AutoCaller autoCaller(this);
13941 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13942
13943 ComPtr<IInternalSessionControl> directControl;
13944 {
13945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13946 if (mData->mSession.mLockType == LockType_VM)
13947 directControl = mData->mSession.mDirectControl;
13948 }
13949
13950 /* ignore notifications sent after #OnSessionEnd() is called */
13951 if (!directControl)
13952 return S_OK;
13953
13954 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13955}
13956
13957/**
13958 * @note Locks this object for reading.
13959 */
13960HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13961 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13962 const Utf8Str &aGuestIp, LONG aGuestPort)
13963{
13964 LogFlowThisFunc(("\n"));
13965
13966 AutoCaller autoCaller(this);
13967 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13968
13969 ComPtr<IInternalSessionControl> directControl;
13970 {
13971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13972 if (mData->mSession.mLockType == LockType_VM)
13973 directControl = mData->mSession.mDirectControl;
13974 }
13975
13976 /* ignore notifications sent after #OnSessionEnd() is called */
13977 if (!directControl)
13978 return S_OK;
13979 /*
13980 * instead acting like callback we ask IVirtualBox deliver corresponding event
13981 */
13982
13983 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13984 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13985 return S_OK;
13986}
13987
13988/**
13989 * @note Locks this object for reading.
13990 */
13991HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13992{
13993 LogFlowThisFunc(("\n"));
13994
13995 AutoCaller autoCaller(this);
13996 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13997
13998 ComPtr<IInternalSessionControl> directControl;
13999 {
14000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14001 if (mData->mSession.mLockType == LockType_VM)
14002 directControl = mData->mSession.mDirectControl;
14003 }
14004
14005 /* ignore notifications sent after #OnSessionEnd() is called */
14006 if (!directControl)
14007 return S_OK;
14008
14009 return directControl->OnAudioAdapterChange(audioAdapter);
14010}
14011
14012/**
14013 * @note Locks this object for reading.
14014 */
14015HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14016{
14017 LogFlowThisFunc(("\n"));
14018
14019 AutoCaller autoCaller(this);
14020 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14021
14022 ComPtr<IInternalSessionControl> directControl;
14023 {
14024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14025 if (mData->mSession.mLockType == LockType_VM)
14026 directControl = mData->mSession.mDirectControl;
14027 }
14028
14029 /* ignore notifications sent after #OnSessionEnd() is called */
14030 if (!directControl)
14031 return S_OK;
14032
14033 return directControl->OnSerialPortChange(serialPort);
14034}
14035
14036/**
14037 * @note Locks this object for reading.
14038 */
14039HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14040{
14041 LogFlowThisFunc(("\n"));
14042
14043 AutoCaller autoCaller(this);
14044 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14045
14046 ComPtr<IInternalSessionControl> directControl;
14047 {
14048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14049 if (mData->mSession.mLockType == LockType_VM)
14050 directControl = mData->mSession.mDirectControl;
14051 }
14052
14053 /* ignore notifications sent after #OnSessionEnd() is called */
14054 if (!directControl)
14055 return S_OK;
14056
14057 return directControl->OnParallelPortChange(parallelPort);
14058}
14059
14060/**
14061 * @note Locks this object for reading.
14062 */
14063HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14064{
14065 LogFlowThisFunc(("\n"));
14066
14067 AutoCaller autoCaller(this);
14068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14069
14070 ComPtr<IInternalSessionControl> directControl;
14071 {
14072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14073 if (mData->mSession.mLockType == LockType_VM)
14074 directControl = mData->mSession.mDirectControl;
14075 }
14076
14077 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14078
14079 /* ignore notifications sent after #OnSessionEnd() is called */
14080 if (!directControl)
14081 return S_OK;
14082
14083 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14084}
14085
14086/**
14087 * @note Locks this object for reading.
14088 */
14089HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14090{
14091 LogFlowThisFunc(("\n"));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14095
14096 ComPtr<IInternalSessionControl> directControl;
14097 {
14098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14099 if (mData->mSession.mLockType == LockType_VM)
14100 directControl = mData->mSession.mDirectControl;
14101 }
14102
14103 mParent->i_onMediumChanged(aAttachment);
14104
14105 /* ignore notifications sent after #OnSessionEnd() is called */
14106 if (!directControl)
14107 return S_OK;
14108
14109 return directControl->OnMediumChange(aAttachment, aForce);
14110}
14111
14112HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14113{
14114 LogFlowThisFunc(("\n"));
14115
14116 AutoCaller autoCaller(this);
14117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14118
14119 ComPtr<IInternalSessionControl> directControl;
14120 {
14121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14122 if (mData->mSession.mLockType == LockType_VM)
14123 directControl = mData->mSession.mDirectControl;
14124 }
14125
14126 /* ignore notifications sent after #OnSessionEnd() is called */
14127 if (!directControl)
14128 return S_OK;
14129
14130 return directControl->OnVMProcessPriorityChange(aPriority);
14131}
14132
14133/**
14134 * @note Locks this object for reading.
14135 */
14136HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14137{
14138 LogFlowThisFunc(("\n"));
14139
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14142
14143 ComPtr<IInternalSessionControl> directControl;
14144 {
14145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14146 if (mData->mSession.mLockType == LockType_VM)
14147 directControl = mData->mSession.mDirectControl;
14148 }
14149
14150 /* ignore notifications sent after #OnSessionEnd() is called */
14151 if (!directControl)
14152 return S_OK;
14153
14154 return directControl->OnCPUChange(aCPU, aRemove);
14155}
14156
14157HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14158{
14159 LogFlowThisFunc(("\n"));
14160
14161 AutoCaller autoCaller(this);
14162 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14163
14164 ComPtr<IInternalSessionControl> directControl;
14165 {
14166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14167 if (mData->mSession.mLockType == LockType_VM)
14168 directControl = mData->mSession.mDirectControl;
14169 }
14170
14171 /* ignore notifications sent after #OnSessionEnd() is called */
14172 if (!directControl)
14173 return S_OK;
14174
14175 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14176}
14177
14178/**
14179 * @note Locks this object for reading.
14180 */
14181HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14182{
14183 LogFlowThisFunc(("\n"));
14184
14185 AutoCaller autoCaller(this);
14186 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14187
14188 ComPtr<IInternalSessionControl> directControl;
14189 {
14190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14191 if (mData->mSession.mLockType == LockType_VM)
14192 directControl = mData->mSession.mDirectControl;
14193 }
14194
14195 /* ignore notifications sent after #OnSessionEnd() is called */
14196 if (!directControl)
14197 return S_OK;
14198
14199 return directControl->OnVRDEServerChange(aRestart);
14200}
14201
14202/**
14203 * @note Locks this object for reading.
14204 */
14205HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14206{
14207 LogFlowThisFunc(("\n"));
14208
14209 AutoCaller autoCaller(this);
14210 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14211
14212 ComPtr<IInternalSessionControl> directControl;
14213 {
14214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14215 if (mData->mSession.mLockType == LockType_VM)
14216 directControl = mData->mSession.mDirectControl;
14217 }
14218
14219 /* ignore notifications sent after #OnSessionEnd() is called */
14220 if (!directControl)
14221 return S_OK;
14222
14223 return directControl->OnRecordingChange(aEnable);
14224}
14225
14226/**
14227 * @note Locks this object for reading.
14228 */
14229HRESULT SessionMachine::i_onUSBControllerChange()
14230{
14231 LogFlowThisFunc(("\n"));
14232
14233 AutoCaller autoCaller(this);
14234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14235
14236 ComPtr<IInternalSessionControl> directControl;
14237 {
14238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14239 if (mData->mSession.mLockType == LockType_VM)
14240 directControl = mData->mSession.mDirectControl;
14241 }
14242
14243 /* ignore notifications sent after #OnSessionEnd() is called */
14244 if (!directControl)
14245 return S_OK;
14246
14247 return directControl->OnUSBControllerChange();
14248}
14249
14250/**
14251 * @note Locks this object for reading.
14252 */
14253HRESULT SessionMachine::i_onSharedFolderChange()
14254{
14255 LogFlowThisFunc(("\n"));
14256
14257 AutoCaller autoCaller(this);
14258 AssertComRCReturnRC(autoCaller.rc());
14259
14260 ComPtr<IInternalSessionControl> directControl;
14261 {
14262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14263 if (mData->mSession.mLockType == LockType_VM)
14264 directControl = mData->mSession.mDirectControl;
14265 }
14266
14267 /* ignore notifications sent after #OnSessionEnd() is called */
14268 if (!directControl)
14269 return S_OK;
14270
14271 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14272}
14273
14274/**
14275 * @note Locks this object for reading.
14276 */
14277HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14278{
14279 LogFlowThisFunc(("\n"));
14280
14281 AutoCaller autoCaller(this);
14282 AssertComRCReturnRC(autoCaller.rc());
14283
14284 ComPtr<IInternalSessionControl> directControl;
14285 {
14286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14287 if (mData->mSession.mLockType == LockType_VM)
14288 directControl = mData->mSession.mDirectControl;
14289 }
14290
14291 /* ignore notifications sent after #OnSessionEnd() is called */
14292 if (!directControl)
14293 return S_OK;
14294
14295 return directControl->OnClipboardModeChange(aClipboardMode);
14296}
14297
14298/**
14299 * @note Locks this object for reading.
14300 */
14301HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14302{
14303 LogFlowThisFunc(("\n"));
14304
14305 AutoCaller autoCaller(this);
14306 AssertComRCReturnRC(autoCaller.rc());
14307
14308 ComPtr<IInternalSessionControl> directControl;
14309 {
14310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14311 if (mData->mSession.mLockType == LockType_VM)
14312 directControl = mData->mSession.mDirectControl;
14313 }
14314
14315 /* ignore notifications sent after #OnSessionEnd() is called */
14316 if (!directControl)
14317 return S_OK;
14318
14319 return directControl->OnClipboardFileTransferModeChange(aEnable);
14320}
14321
14322/**
14323 * @note Locks this object for reading.
14324 */
14325HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14326{
14327 LogFlowThisFunc(("\n"));
14328
14329 AutoCaller autoCaller(this);
14330 AssertComRCReturnRC(autoCaller.rc());
14331
14332 ComPtr<IInternalSessionControl> directControl;
14333 {
14334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14335 if (mData->mSession.mLockType == LockType_VM)
14336 directControl = mData->mSession.mDirectControl;
14337 }
14338
14339 /* ignore notifications sent after #OnSessionEnd() is called */
14340 if (!directControl)
14341 return S_OK;
14342
14343 return directControl->OnDnDModeChange(aDnDMode);
14344}
14345
14346/**
14347 * @note Locks this object for reading.
14348 */
14349HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14350{
14351 LogFlowThisFunc(("\n"));
14352
14353 AutoCaller autoCaller(this);
14354 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14355
14356 ComPtr<IInternalSessionControl> directControl;
14357 {
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359 if (mData->mSession.mLockType == LockType_VM)
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* ignore notifications sent after #OnSessionEnd() is called */
14364 if (!directControl)
14365 return S_OK;
14366
14367 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14368}
14369
14370/**
14371 * @note Locks this object for reading.
14372 */
14373HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14374{
14375 LogFlowThisFunc(("\n"));
14376
14377 AutoCaller autoCaller(this);
14378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14379
14380 ComPtr<IInternalSessionControl> directControl;
14381 {
14382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14383 if (mData->mSession.mLockType == LockType_VM)
14384 directControl = mData->mSession.mDirectControl;
14385 }
14386
14387 /* ignore notifications sent after #OnSessionEnd() is called */
14388 if (!directControl)
14389 return S_OK;
14390
14391 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14392}
14393
14394/**
14395 * Returns @c true if this machine's USB controller reports it has a matching
14396 * filter for the given USB device and @c false otherwise.
14397 *
14398 * @note locks this object for reading.
14399 */
14400bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14401{
14402 AutoCaller autoCaller(this);
14403 /* silently return if not ready -- this method may be called after the
14404 * direct machine session has been called */
14405 if (!autoCaller.isOk())
14406 return false;
14407
14408#ifdef VBOX_WITH_USB
14409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14410
14411 switch (mData->mMachineState)
14412 {
14413 case MachineState_Starting:
14414 case MachineState_Restoring:
14415 case MachineState_TeleportingIn:
14416 case MachineState_Paused:
14417 case MachineState_Running:
14418 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14419 * elsewhere... */
14420 alock.release();
14421 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14422 default: break;
14423 }
14424#else
14425 NOREF(aDevice);
14426 NOREF(aMaskedIfs);
14427#endif
14428 return false;
14429}
14430
14431/**
14432 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14433 */
14434HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14435 IVirtualBoxErrorInfo *aError,
14436 ULONG aMaskedIfs,
14437 const com::Utf8Str &aCaptureFilename)
14438{
14439 LogFlowThisFunc(("\n"));
14440
14441 AutoCaller autoCaller(this);
14442
14443 /* This notification may happen after the machine object has been
14444 * uninitialized (the session was closed), so don't assert. */
14445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14446
14447 ComPtr<IInternalSessionControl> directControl;
14448 {
14449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14450 if (mData->mSession.mLockType == LockType_VM)
14451 directControl = mData->mSession.mDirectControl;
14452 }
14453
14454 /* fail on notifications sent after #OnSessionEnd() is called, it is
14455 * expected by the caller */
14456 if (!directControl)
14457 return E_FAIL;
14458
14459 /* No locks should be held at this point. */
14460 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14461 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14462
14463 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14464}
14465
14466/**
14467 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14468 */
14469HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14470 IVirtualBoxErrorInfo *aError)
14471{
14472 LogFlowThisFunc(("\n"));
14473
14474 AutoCaller autoCaller(this);
14475
14476 /* This notification may happen after the machine object has been
14477 * uninitialized (the session was closed), so don't assert. */
14478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14479
14480 ComPtr<IInternalSessionControl> directControl;
14481 {
14482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14483 if (mData->mSession.mLockType == LockType_VM)
14484 directControl = mData->mSession.mDirectControl;
14485 }
14486
14487 /* fail on notifications sent after #OnSessionEnd() is called, it is
14488 * expected by the caller */
14489 if (!directControl)
14490 return E_FAIL;
14491
14492 /* No locks should be held at this point. */
14493 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14494 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14495
14496 return directControl->OnUSBDeviceDetach(aId, aError);
14497}
14498
14499// protected methods
14500/////////////////////////////////////////////////////////////////////////////
14501
14502/**
14503 * Deletes the given file if it is no longer in use by either the current machine state
14504 * (if the machine is "saved") or any of the machine's snapshots.
14505 *
14506 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14507 * but is different for each SnapshotMachine. When calling this, the order of calling this
14508 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14509 * is therefore critical. I know, it's all rather messy.
14510 *
14511 * @param strStateFile
14512 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14513 * the test for whether the saved state file is in use.
14514 */
14515void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14516 Snapshot *pSnapshotToIgnore)
14517{
14518 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14519 if ( (strStateFile.isNotEmpty())
14520 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14521 )
14522 // ... and it must also not be shared with other snapshots
14523 if ( !mData->mFirstSnapshot
14524 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14525 // this checks the SnapshotMachine's state file paths
14526 )
14527 RTFileDelete(strStateFile.c_str());
14528}
14529
14530/**
14531 * Locks the attached media.
14532 *
14533 * All attached hard disks are locked for writing and DVD/floppy are locked for
14534 * reading. Parents of attached hard disks (if any) are locked for reading.
14535 *
14536 * This method also performs accessibility check of all media it locks: if some
14537 * media is inaccessible, the method will return a failure and a bunch of
14538 * extended error info objects per each inaccessible medium.
14539 *
14540 * Note that this method is atomic: if it returns a success, all media are
14541 * locked as described above; on failure no media is locked at all (all
14542 * succeeded individual locks will be undone).
14543 *
14544 * The caller is responsible for doing the necessary state sanity checks.
14545 *
14546 * The locks made by this method must be undone by calling #unlockMedia() when
14547 * no more needed.
14548 */
14549HRESULT SessionMachine::i_lockMedia()
14550{
14551 AutoCaller autoCaller(this);
14552 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14553
14554 AutoMultiWriteLock2 alock(this->lockHandle(),
14555 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14556
14557 /* bail out if trying to lock things with already set up locking */
14558 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14559
14560 MultiResult mrc(S_OK);
14561
14562 /* Collect locking information for all medium objects attached to the VM. */
14563 for (MediumAttachmentList::const_iterator
14564 it = mMediumAttachments->begin();
14565 it != mMediumAttachments->end();
14566 ++it)
14567 {
14568 MediumAttachment *pAtt = *it;
14569 DeviceType_T devType = pAtt->i_getType();
14570 Medium *pMedium = pAtt->i_getMedium();
14571
14572 MediumLockList *pMediumLockList(new MediumLockList());
14573 // There can be attachments without a medium (floppy/dvd), and thus
14574 // it's impossible to create a medium lock list. It still makes sense
14575 // to have the empty medium lock list in the map in case a medium is
14576 // attached later.
14577 if (pMedium != NULL)
14578 {
14579 MediumType_T mediumType = pMedium->i_getType();
14580 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14581 || mediumType == MediumType_Shareable;
14582 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14583
14584 alock.release();
14585 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14586 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14587 false /* fMediumLockWriteAll */,
14588 NULL,
14589 *pMediumLockList);
14590 alock.acquire();
14591 if (FAILED(mrc))
14592 {
14593 delete pMediumLockList;
14594 mData->mSession.mLockedMedia.Clear();
14595 break;
14596 }
14597 }
14598
14599 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14600 if (FAILED(rc))
14601 {
14602 mData->mSession.mLockedMedia.Clear();
14603 mrc = setError(rc,
14604 tr("Collecting locking information for all attached media failed"));
14605 break;
14606 }
14607 }
14608
14609 if (SUCCEEDED(mrc))
14610 {
14611 /* Now lock all media. If this fails, nothing is locked. */
14612 alock.release();
14613 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14614 alock.acquire();
14615 if (FAILED(rc))
14616 {
14617 mrc = setError(rc,
14618 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14619 }
14620 }
14621
14622 return mrc;
14623}
14624
14625/**
14626 * Undoes the locks made by by #lockMedia().
14627 */
14628HRESULT SessionMachine::i_unlockMedia()
14629{
14630 AutoCaller autoCaller(this);
14631 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14632
14633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14634
14635 /* we may be holding important error info on the current thread;
14636 * preserve it */
14637 ErrorInfoKeeper eik;
14638
14639 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14640 AssertComRC(rc);
14641 return rc;
14642}
14643
14644/**
14645 * Helper to change the machine state (reimplementation).
14646 *
14647 * @note Locks this object for writing.
14648 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14649 * it can cause crashes in random places due to unexpectedly committing
14650 * the current settings. The caller is responsible for that. The call
14651 * to saveStateSettings is fine, because this method does not commit.
14652 */
14653HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14654{
14655 LogFlowThisFuncEnter();
14656
14657 AutoCaller autoCaller(this);
14658 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14659
14660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14661
14662 MachineState_T oldMachineState = mData->mMachineState;
14663
14664 AssertMsgReturn(oldMachineState != aMachineState,
14665 ("oldMachineState=%s, aMachineState=%s\n",
14666 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14667 E_FAIL);
14668
14669 HRESULT rc = S_OK;
14670
14671 int stsFlags = 0;
14672 bool deleteSavedState = false;
14673
14674 /* detect some state transitions */
14675
14676 if ( ( ( oldMachineState == MachineState_Saved
14677 || oldMachineState == MachineState_AbortedSaved
14678 )
14679 && aMachineState == MachineState_Restoring
14680 )
14681 || ( ( oldMachineState == MachineState_PoweredOff
14682 || oldMachineState == MachineState_Teleported
14683 || oldMachineState == MachineState_Aborted
14684 )
14685 && ( aMachineState == MachineState_TeleportingIn
14686 || aMachineState == MachineState_Starting
14687 )
14688 )
14689 )
14690 {
14691 /* The EMT thread is about to start */
14692
14693 /* Nothing to do here for now... */
14694
14695 /// @todo NEWMEDIA don't let mDVDDrive and other children
14696 /// change anything when in the Starting/Restoring state
14697 }
14698 else if ( ( oldMachineState == MachineState_Running
14699 || oldMachineState == MachineState_Paused
14700 || oldMachineState == MachineState_Teleporting
14701 || oldMachineState == MachineState_OnlineSnapshotting
14702 || oldMachineState == MachineState_LiveSnapshotting
14703 || oldMachineState == MachineState_Stuck
14704 || oldMachineState == MachineState_Starting
14705 || oldMachineState == MachineState_Stopping
14706 || oldMachineState == MachineState_Saving
14707 || oldMachineState == MachineState_Restoring
14708 || oldMachineState == MachineState_TeleportingPausedVM
14709 || oldMachineState == MachineState_TeleportingIn
14710 )
14711 && ( aMachineState == MachineState_PoweredOff
14712 || aMachineState == MachineState_Saved
14713 || aMachineState == MachineState_Teleported
14714 || aMachineState == MachineState_Aborted
14715 || aMachineState == MachineState_AbortedSaved
14716 )
14717 )
14718 {
14719 /* The EMT thread has just stopped, unlock attached media. Note that as
14720 * opposed to locking that is done from Console, we do unlocking here
14721 * because the VM process may have aborted before having a chance to
14722 * properly unlock all media it locked. */
14723
14724 unlockMedia();
14725 }
14726
14727 if (oldMachineState == MachineState_Restoring)
14728 {
14729 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14730 {
14731 /*
14732 * delete the saved state file once the machine has finished
14733 * restoring from it (note that Console sets the state from
14734 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14735 * to give the user an ability to fix an error and retry --
14736 * we keep the saved state file in this case)
14737 */
14738 deleteSavedState = true;
14739 }
14740 }
14741 else if ( oldMachineState == MachineState_Saved
14742 && ( aMachineState == MachineState_PoweredOff
14743 || aMachineState == MachineState_Teleported
14744 )
14745 )
14746 {
14747 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14748 deleteSavedState = true;
14749 mData->mCurrentStateModified = TRUE;
14750 stsFlags |= SaveSTS_CurStateModified;
14751 }
14752 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14753 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14754
14755 if ( aMachineState == MachineState_Starting
14756 || aMachineState == MachineState_Restoring
14757 || aMachineState == MachineState_TeleportingIn
14758 )
14759 {
14760 /* set the current state modified flag to indicate that the current
14761 * state is no more identical to the state in the
14762 * current snapshot */
14763 if (!mData->mCurrentSnapshot.isNull())
14764 {
14765 mData->mCurrentStateModified = TRUE;
14766 stsFlags |= SaveSTS_CurStateModified;
14767 }
14768 }
14769
14770 if (deleteSavedState)
14771 {
14772 if (mRemoveSavedState)
14773 {
14774 Assert(!mSSData->strStateFilePath.isEmpty());
14775
14776 // it is safe to delete the saved state file if ...
14777 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14778 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14779 // ... none of the snapshots share the saved state file
14780 )
14781 RTFileDelete(mSSData->strStateFilePath.c_str());
14782 }
14783
14784 mSSData->strStateFilePath.setNull();
14785 stsFlags |= SaveSTS_StateFilePath;
14786 }
14787
14788 /* redirect to the underlying peer machine */
14789 mPeer->i_setMachineState(aMachineState);
14790
14791 if ( oldMachineState != MachineState_RestoringSnapshot
14792 && ( aMachineState == MachineState_PoweredOff
14793 || aMachineState == MachineState_Teleported
14794 || aMachineState == MachineState_Aborted
14795 || aMachineState == MachineState_AbortedSaved
14796 || aMachineState == MachineState_Saved))
14797 {
14798 /* the machine has stopped execution
14799 * (or the saved state file was adopted) */
14800 stsFlags |= SaveSTS_StateTimeStamp;
14801 }
14802
14803 if ( ( oldMachineState == MachineState_PoweredOff
14804 || oldMachineState == MachineState_Aborted
14805 || oldMachineState == MachineState_Teleported
14806 )
14807 && aMachineState == MachineState_Saved)
14808 {
14809 /* the saved state file was adopted */
14810 Assert(!mSSData->strStateFilePath.isEmpty());
14811 stsFlags |= SaveSTS_StateFilePath;
14812 }
14813
14814#ifdef VBOX_WITH_GUEST_PROPS
14815 if ( aMachineState == MachineState_PoweredOff
14816 || aMachineState == MachineState_Aborted
14817 || aMachineState == MachineState_Teleported)
14818 {
14819 /* Make sure any transient guest properties get removed from the
14820 * property store on shutdown. */
14821 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14822
14823 /* remove it from the settings representation */
14824 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14825 for (settings::GuestPropertiesList::iterator
14826 it = llGuestProperties.begin();
14827 it != llGuestProperties.end();
14828 /*nothing*/)
14829 {
14830 const settings::GuestProperty &prop = *it;
14831 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14832 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14833 {
14834 it = llGuestProperties.erase(it);
14835 fNeedsSaving = true;
14836 }
14837 else
14838 {
14839 ++it;
14840 }
14841 }
14842
14843 /* Additionally remove it from the HWData representation. Required to
14844 * keep everything in sync, as this is what the API keeps using. */
14845 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14846 for (HWData::GuestPropertyMap::iterator
14847 it = llHWGuestProperties.begin();
14848 it != llHWGuestProperties.end();
14849 /*nothing*/)
14850 {
14851 uint32_t fFlags = it->second.mFlags;
14852 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14853 {
14854 /* iterator where we need to continue after the erase call
14855 * (C++03 is a fact still, and it doesn't return the iterator
14856 * which would allow continuing) */
14857 HWData::GuestPropertyMap::iterator it2 = it;
14858 ++it2;
14859 llHWGuestProperties.erase(it);
14860 it = it2;
14861 fNeedsSaving = true;
14862 }
14863 else
14864 {
14865 ++it;
14866 }
14867 }
14868
14869 if (fNeedsSaving)
14870 {
14871 mData->mCurrentStateModified = TRUE;
14872 stsFlags |= SaveSTS_CurStateModified;
14873 }
14874 }
14875#endif /* VBOX_WITH_GUEST_PROPS */
14876
14877 rc = i_saveStateSettings(stsFlags);
14878
14879 if ( ( oldMachineState != MachineState_PoweredOff
14880 && oldMachineState != MachineState_Aborted
14881 && oldMachineState != MachineState_Teleported
14882 )
14883 && ( aMachineState == MachineState_PoweredOff
14884 || aMachineState == MachineState_Aborted
14885 || aMachineState == MachineState_Teleported
14886 )
14887 )
14888 {
14889 /* we've been shut down for any reason */
14890 /* no special action so far */
14891 }
14892
14893 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
14894 LogFlowThisFuncLeave();
14895 return rc;
14896}
14897
14898/**
14899 * Sends the current machine state value to the VM process.
14900 *
14901 * @note Locks this object for reading, then calls a client process.
14902 */
14903HRESULT SessionMachine::i_updateMachineStateOnClient()
14904{
14905 AutoCaller autoCaller(this);
14906 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14907
14908 ComPtr<IInternalSessionControl> directControl;
14909 {
14910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14911 AssertReturn(!!mData, E_FAIL);
14912 if (mData->mSession.mLockType == LockType_VM)
14913 directControl = mData->mSession.mDirectControl;
14914
14915 /* directControl may be already set to NULL here in #OnSessionEnd()
14916 * called too early by the direct session process while there is still
14917 * some operation (like deleting the snapshot) in progress. The client
14918 * process in this case is waiting inside Session::close() for the
14919 * "end session" process object to complete, while #uninit() called by
14920 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14921 * operation to complete. For now, we accept this inconsistent behavior
14922 * and simply do nothing here. */
14923
14924 if (mData->mSession.mState == SessionState_Unlocking)
14925 return S_OK;
14926 }
14927
14928 /* ignore notifications sent after #OnSessionEnd() is called */
14929 if (!directControl)
14930 return S_OK;
14931
14932 return directControl->UpdateMachineState(mData->mMachineState);
14933}
14934
14935
14936/*static*/
14937HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14938{
14939 va_list args;
14940 va_start(args, pcszMsg);
14941 HRESULT rc = setErrorInternalV(aResultCode,
14942 getStaticClassIID(),
14943 getStaticComponentName(),
14944 pcszMsg, args,
14945 false /* aWarning */,
14946 true /* aLogIt */);
14947 va_end(args);
14948 return rc;
14949}
14950
14951
14952HRESULT Machine::updateState(MachineState_T aState)
14953{
14954 NOREF(aState);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14959{
14960 NOREF(aProgress);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::endPowerUp(LONG aResult)
14965{
14966 NOREF(aResult);
14967 ReturnComNotImplemented();
14968}
14969
14970HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14971{
14972 NOREF(aProgress);
14973 ReturnComNotImplemented();
14974}
14975
14976HRESULT Machine::endPoweringDown(LONG aResult,
14977 const com::Utf8Str &aErrMsg)
14978{
14979 NOREF(aResult);
14980 NOREF(aErrMsg);
14981 ReturnComNotImplemented();
14982}
14983
14984HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14985 BOOL *aMatched,
14986 ULONG *aMaskedInterfaces)
14987{
14988 NOREF(aDevice);
14989 NOREF(aMatched);
14990 NOREF(aMaskedInterfaces);
14991 ReturnComNotImplemented();
14992
14993}
14994
14995HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14996{
14997 NOREF(aId); NOREF(aCaptureFilename);
14998 ReturnComNotImplemented();
14999}
15000
15001HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15002 BOOL aDone)
15003{
15004 NOREF(aId);
15005 NOREF(aDone);
15006 ReturnComNotImplemented();
15007}
15008
15009HRESULT Machine::autoCaptureUSBDevices()
15010{
15011 ReturnComNotImplemented();
15012}
15013
15014HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15015{
15016 NOREF(aDone);
15017 ReturnComNotImplemented();
15018}
15019
15020HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15021 ComPtr<IProgress> &aProgress)
15022{
15023 NOREF(aSession);
15024 NOREF(aProgress);
15025 ReturnComNotImplemented();
15026}
15027
15028HRESULT Machine::finishOnlineMergeMedium()
15029{
15030 ReturnComNotImplemented();
15031}
15032
15033HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15034 std::vector<com::Utf8Str> &aValues,
15035 std::vector<LONG64> &aTimestamps,
15036 std::vector<com::Utf8Str> &aFlags)
15037{
15038 NOREF(aNames);
15039 NOREF(aValues);
15040 NOREF(aTimestamps);
15041 NOREF(aFlags);
15042 ReturnComNotImplemented();
15043}
15044
15045HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15046 const com::Utf8Str &aValue,
15047 LONG64 aTimestamp,
15048 const com::Utf8Str &aFlags)
15049{
15050 NOREF(aName);
15051 NOREF(aValue);
15052 NOREF(aTimestamp);
15053 NOREF(aFlags);
15054 ReturnComNotImplemented();
15055}
15056
15057HRESULT Machine::lockMedia()
15058{
15059 ReturnComNotImplemented();
15060}
15061
15062HRESULT Machine::unlockMedia()
15063{
15064 ReturnComNotImplemented();
15065}
15066
15067HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15068 ComPtr<IMediumAttachment> &aNewAttachment)
15069{
15070 NOREF(aAttachment);
15071 NOREF(aNewAttachment);
15072 ReturnComNotImplemented();
15073}
15074
15075HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15076 ULONG aCpuUser,
15077 ULONG aCpuKernel,
15078 ULONG aCpuIdle,
15079 ULONG aMemTotal,
15080 ULONG aMemFree,
15081 ULONG aMemBalloon,
15082 ULONG aMemShared,
15083 ULONG aMemCache,
15084 ULONG aPagedTotal,
15085 ULONG aMemAllocTotal,
15086 ULONG aMemFreeTotal,
15087 ULONG aMemBalloonTotal,
15088 ULONG aMemSharedTotal,
15089 ULONG aVmNetRx,
15090 ULONG aVmNetTx)
15091{
15092 NOREF(aValidStats);
15093 NOREF(aCpuUser);
15094 NOREF(aCpuKernel);
15095 NOREF(aCpuIdle);
15096 NOREF(aMemTotal);
15097 NOREF(aMemFree);
15098 NOREF(aMemBalloon);
15099 NOREF(aMemShared);
15100 NOREF(aMemCache);
15101 NOREF(aPagedTotal);
15102 NOREF(aMemAllocTotal);
15103 NOREF(aMemFreeTotal);
15104 NOREF(aMemBalloonTotal);
15105 NOREF(aMemSharedTotal);
15106 NOREF(aVmNetRx);
15107 NOREF(aVmNetTx);
15108 ReturnComNotImplemented();
15109}
15110
15111HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15112 com::Utf8Str &aResult)
15113{
15114 NOREF(aAuthParams);
15115 NOREF(aResult);
15116 ReturnComNotImplemented();
15117}
15118
15119com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15120{
15121 com::Utf8Str strControllerName = "Unknown";
15122 switch (aBusType)
15123 {
15124 case StorageBus_IDE:
15125 {
15126 strControllerName = "IDE";
15127 break;
15128 }
15129 case StorageBus_SATA:
15130 {
15131 strControllerName = "SATA";
15132 break;
15133 }
15134 case StorageBus_SCSI:
15135 {
15136 strControllerName = "SCSI";
15137 break;
15138 }
15139 case StorageBus_Floppy:
15140 {
15141 strControllerName = "Floppy";
15142 break;
15143 }
15144 case StorageBus_SAS:
15145 {
15146 strControllerName = "SAS";
15147 break;
15148 }
15149 case StorageBus_USB:
15150 {
15151 strControllerName = "USB";
15152 break;
15153 }
15154 default:
15155 break;
15156 }
15157 return strControllerName;
15158}
15159
15160HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15161{
15162 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15163
15164 AutoCaller autoCaller(this);
15165 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15166
15167 HRESULT rc = S_OK;
15168
15169 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15170 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15171 rc = getUSBDeviceFilters(usbDeviceFilters);
15172 if (FAILED(rc)) return rc;
15173
15174 NOREF(aFlags);
15175 com::Utf8Str osTypeId;
15176 ComObjPtr<GuestOSType> osType = NULL;
15177
15178 /* Get the guest os type as a string from the VB. */
15179 rc = getOSTypeId(osTypeId);
15180 if (FAILED(rc)) return rc;
15181
15182 /* Get the os type obj that coresponds, can be used to get
15183 * the defaults for this guest OS. */
15184 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15185 if (FAILED(rc)) return rc;
15186
15187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15188
15189 /* Let the OS type select 64-bit ness. */
15190 mHWData->mLongMode = osType->i_is64Bit()
15191 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15192
15193 /* Let the OS type enable the X2APIC */
15194 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15195
15196 /* This one covers IOAPICEnabled. */
15197 mBIOSSettings->i_applyDefaults(osType);
15198
15199 /* Initialize default record settings. */
15200 mRecordingSettings->i_applyDefaults();
15201
15202 /* Initialize default BIOS settings here */
15203 /* Hardware virtualization must be ON by default */
15204 mHWData->mAPIC = true;
15205 mHWData->mHWVirtExEnabled = true;
15206
15207 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15208 if (FAILED(rc)) return rc;
15209
15210 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15211 if (FAILED(rc)) return rc;
15212
15213 /* Graphics stuff. */
15214 GraphicsControllerType_T graphicsController;
15215 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15216 if (FAILED(rc)) return rc;
15217
15218 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15219 if (FAILED(rc)) return rc;
15220
15221 ULONG vramSize;
15222 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15223 if (FAILED(rc)) return rc;
15224
15225 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15226 if (FAILED(rc)) return rc;
15227
15228 BOOL fAccelerate2DVideoEnabled;
15229 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15230 if (FAILED(rc)) return rc;
15231
15232 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15233 if (FAILED(rc)) return rc;
15234
15235 BOOL fAccelerate3DEnabled;
15236 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15237 if (FAILED(rc)) return rc;
15238
15239 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15240 if (FAILED(rc)) return rc;
15241
15242 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15243 if (FAILED(rc)) return rc;
15244
15245 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15246 if (FAILED(rc)) return rc;
15247
15248 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15249 if (FAILED(rc)) return rc;
15250
15251 BOOL mRTCUseUTC;
15252 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15253 if (FAILED(rc)) return rc;
15254
15255 setRTCUseUTC(mRTCUseUTC);
15256 if (FAILED(rc)) return rc;
15257
15258 /* the setter does more than just the assignment, so use it */
15259 ChipsetType_T enmChipsetType;
15260 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15261 if (FAILED(rc)) return rc;
15262
15263 rc = COMSETTER(ChipsetType)(enmChipsetType);
15264 if (FAILED(rc)) return rc;
15265
15266 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15267 if (FAILED(rc)) return rc;
15268
15269 /* Apply IOMMU defaults. */
15270 IommuType_T enmIommuType;
15271 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = COMSETTER(IommuType)(enmIommuType);
15275 if (FAILED(rc)) return rc;
15276
15277 /* Apply network adapters defaults */
15278 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15279 mNetworkAdapters[slot]->i_applyDefaults(osType);
15280
15281 /* Apply serial port defaults */
15282 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15283 mSerialPorts[slot]->i_applyDefaults(osType);
15284
15285 /* Apply parallel port defaults - not OS dependent*/
15286 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15287 mParallelPorts[slot]->i_applyDefaults();
15288
15289 /* This one covers the TPM type. */
15290 mTrustedPlatformModule->i_applyDefaults(osType);
15291
15292 /* This one covers secure boot. */
15293 rc = mNvramStore->i_applyDefaults(osType);
15294 if (FAILED(rc)) return rc;
15295
15296 /* Audio stuff. */
15297 AudioControllerType_T audioController;
15298 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15299 if (FAILED(rc)) return rc;
15300
15301 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15302 if (FAILED(rc)) return rc;
15303
15304 AudioCodecType_T audioCodec;
15305 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15306 if (FAILED(rc)) return rc;
15307
15308 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15309 if (FAILED(rc)) return rc;
15310
15311 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15312 if (FAILED(rc)) return rc;
15313
15314 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15315 if (FAILED(rc)) return rc;
15316
15317 /* Storage Controllers */
15318 StorageControllerType_T hdStorageControllerType;
15319 StorageBus_T hdStorageBusType;
15320 StorageControllerType_T dvdStorageControllerType;
15321 StorageBus_T dvdStorageBusType;
15322 BOOL recommendedFloppy;
15323 ComPtr<IStorageController> floppyController;
15324 ComPtr<IStorageController> hdController;
15325 ComPtr<IStorageController> dvdController;
15326 Utf8Str strFloppyName, strDVDName, strHDName;
15327
15328 /* GUI auto generates controller names using bus type. Do the same*/
15329 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15330
15331 /* Floppy recommended? add one. */
15332 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15333 if (FAILED(rc)) return rc;
15334 if (recommendedFloppy)
15335 {
15336 rc = addStorageController(strFloppyName,
15337 StorageBus_Floppy,
15338 floppyController);
15339 if (FAILED(rc)) return rc;
15340 }
15341
15342 /* Setup one DVD storage controller. */
15343 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15344 if (FAILED(rc)) return rc;
15345
15346 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15347 if (FAILED(rc)) return rc;
15348
15349 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15350
15351 rc = addStorageController(strDVDName,
15352 dvdStorageBusType,
15353 dvdController);
15354 if (FAILED(rc)) return rc;
15355
15356 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15357 if (FAILED(rc)) return rc;
15358
15359 /* Setup one HDD storage controller. */
15360 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15361 if (FAILED(rc)) return rc;
15362
15363 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15364 if (FAILED(rc)) return rc;
15365
15366 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15367
15368 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15369 {
15370 rc = addStorageController(strHDName,
15371 hdStorageBusType,
15372 hdController);
15373 if (FAILED(rc)) return rc;
15374
15375 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15376 if (FAILED(rc)) return rc;
15377 }
15378 else
15379 {
15380 /* The HD controller is the same as DVD: */
15381 hdController = dvdController;
15382 }
15383
15384 /* Limit the AHCI port count if it's used because windows has trouble with
15385 * too many ports and other guest (OS X in particular) may take extra long
15386 * boot: */
15387
15388 // pParent = static_cast<Medium*>(aP)
15389 IStorageController *temp = hdController;
15390 ComObjPtr<StorageController> storageController;
15391 storageController = static_cast<StorageController *>(temp);
15392
15393 // tempHDController = aHDController;
15394 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15395 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15396 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15397 storageController->COMSETTER(PortCount)(1);
15398
15399 /* USB stuff */
15400
15401 bool ohciEnabled = false;
15402
15403 ComPtr<IUSBController> usbController;
15404 BOOL recommendedUSB3;
15405 BOOL recommendedUSB;
15406 BOOL usbProxyAvailable;
15407
15408 getUSBProxyAvailable(&usbProxyAvailable);
15409 if (FAILED(rc)) return rc;
15410
15411 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15412 if (FAILED(rc)) return rc;
15413 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15414 if (FAILED(rc)) return rc;
15415
15416 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15417 {
15418#ifdef VBOX_WITH_EXTPACK
15419 /* USB 3.0 is only available if the proper ExtPack is installed. */
15420 ExtPackManager *aManager = mParent->i_getExtPackManager();
15421 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15422 {
15423 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15424 if (FAILED(rc)) return rc;
15425
15426 /* xHci includes OHCI */
15427 ohciEnabled = true;
15428 }
15429#endif
15430 }
15431 if ( !ohciEnabled
15432 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15433 {
15434 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15435 if (FAILED(rc)) return rc;
15436 ohciEnabled = true;
15437
15438#ifdef VBOX_WITH_EXTPACK
15439 /* USB 2.0 is only available if the proper ExtPack is installed.
15440 * Note. Configuring EHCI here and providing messages about
15441 * the missing extpack isn't exactly clean, but it is a
15442 * necessary evil to patch over legacy compatability issues
15443 * introduced by the new distribution model. */
15444 ExtPackManager *manager = mParent->i_getExtPackManager();
15445 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15446 {
15447 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15448 if (FAILED(rc)) return rc;
15449 }
15450#endif
15451 }
15452
15453 /* Set recommended human interface device types: */
15454 BOOL recommendedUSBHID;
15455 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15456 if (FAILED(rc)) return rc;
15457
15458 if (recommendedUSBHID)
15459 {
15460 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15461 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15462 if (!ohciEnabled && !usbDeviceFilters.isNull())
15463 {
15464 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15465 if (FAILED(rc)) return rc;
15466 }
15467 }
15468
15469 BOOL recommendedUSBTablet;
15470 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15471 if (FAILED(rc)) return rc;
15472
15473 if (recommendedUSBTablet)
15474 {
15475 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15476 if (!ohciEnabled && !usbDeviceFilters.isNull())
15477 {
15478 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15479 if (FAILED(rc)) return rc;
15480 }
15481 }
15482
15483 /* Enable the VMMDev testing feature for bootsector VMs: */
15484 if (osTypeId == "VBoxBS_64")
15485 {
15486 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15487 if (FAILED(rc))
15488 return rc;
15489 }
15490
15491 return S_OK;
15492}
15493
15494/* This isn't handled entirely by the wrapper generator yet. */
15495#ifdef VBOX_WITH_XPCOM
15496NS_DECL_CLASSINFO(SessionMachine)
15497NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15498
15499NS_DECL_CLASSINFO(SnapshotMachine)
15500NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15501#endif
Note: See TracBrowser for help on using the repository browser.

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