VirtualBox

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

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

Main/Machine: IMachine::mountMedium inadvertently works without
a session. bugref:5490

IMachine::mountMedium() and IMachine::unmountMedium() need to detect and
reject attempts to use an immutable Machine object rather than a mutable
session object from ISession::machine(). This is done by including a
call to i_checkStateDependency(MutableOrRunningStateDep) as is done
elsewhere for IMachine methods that modify a VM's configuration or
settings.

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

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