VirtualBox

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

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

VBox/ostypes.h+Main/Global,Machine,UnattendedInstaller+FE/Qt: First pass
at updating the variety of Linux OS subtypes used at install time along
with their corresponding recommended installation defaults based on each
vendor's respective documentation. bugref:5936

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 542.5 KB
Line 
1/* $Id: MachineImpl.cpp 94460 2022-04-04 20:30:21Z 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.215389.xyz. 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 (guestTypeFamilyId == "Linux" &&
1396 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1397 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1398 mUserData->s.strOsType != "Linux24_64")
1399 {
1400 *aParavirtProvider = ParavirtProvider_KVM;
1401 }
1402 else
1403 *aParavirtProvider = ParavirtProvider_None;
1404 break;
1405 }
1406
1407 default: AssertFailedBreak(); /* Shut up MSC. */
1408 }
1409 break;
1410 }
1411 }
1412
1413 Assert( *aParavirtProvider == ParavirtProvider_None
1414 || *aParavirtProvider == ParavirtProvider_Minimal
1415 || *aParavirtProvider == ParavirtProvider_HyperV
1416 || *aParavirtProvider == ParavirtProvider_KVM);
1417 return S_OK;
1418}
1419
1420HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1421{
1422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1423
1424 aHardwareVersion = mHWData->mHWVersion;
1425
1426 return S_OK;
1427}
1428
1429HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1430{
1431 /* check known version */
1432 Utf8Str hwVersion = aHardwareVersion;
1433 if ( hwVersion.compare("1") != 0
1434 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1435 return setError(E_INVALIDARG,
1436 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1437
1438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 mHWData->mHWVersion = aHardwareVersion;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 if (!mHWData->mHardwareUUID.isZero())
1455 aHardwareUUID = mHWData->mHardwareUUID;
1456 else
1457 aHardwareUUID = mData->mUuid;
1458
1459 return S_OK;
1460}
1461
1462HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1463{
1464 if (!aHardwareUUID.isValid())
1465 return E_INVALIDARG;
1466
1467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1468
1469 HRESULT rc = i_checkStateDependency(MutableStateDep);
1470 if (FAILED(rc)) return rc;
1471
1472 i_setModified(IsModified_MachineData);
1473 mHWData.backup();
1474 if (aHardwareUUID == mData->mUuid)
1475 mHWData->mHardwareUUID.clear();
1476 else
1477 mHWData->mHardwareUUID = aHardwareUUID;
1478
1479 return S_OK;
1480}
1481
1482HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1483{
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 *aMemorySize = mHWData->mMemorySize;
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::setMemorySize(ULONG aMemorySize)
1492{
1493 /* check RAM limits */
1494 if ( aMemorySize < MM_RAM_MIN_IN_MB
1495 || aMemorySize > MM_RAM_MAX_IN_MB
1496 )
1497 return setError(E_INVALIDARG,
1498 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1499 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 HRESULT rc = i_checkStateDependency(MutableStateDep);
1504 if (FAILED(rc)) return rc;
1505
1506 i_setModified(IsModified_MachineData);
1507 mHWData.backup();
1508 mHWData->mMemorySize = aMemorySize;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aCPUCount = mHWData->mCPUCount;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setCPUCount(ULONG aCPUCount)
1523{
1524 /* check CPU limits */
1525 if ( aCPUCount < SchemaDefs::MinCPUCount
1526 || aCPUCount > SchemaDefs::MaxCPUCount
1527 )
1528 return setError(E_INVALIDARG,
1529 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1530 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1535 if (mHWData->mCPUHotPlugEnabled)
1536 {
1537 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1538 {
1539 if (mHWData->mCPUAttached[idx])
1540 return setError(E_INVALIDARG,
1541 tr("There is still a CPU attached to socket %lu."
1542 "Detach the CPU before removing the socket"),
1543 aCPUCount, idx+1);
1544 }
1545 }
1546
1547 HRESULT rc = i_checkStateDependency(MutableStateDep);
1548 if (FAILED(rc)) return rc;
1549
1550 i_setModified(IsModified_MachineData);
1551 mHWData.backup();
1552 mHWData->mCPUCount = aCPUCount;
1553
1554 return S_OK;
1555}
1556
1557HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1558{
1559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1560
1561 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1562
1563 return S_OK;
1564}
1565
1566HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1567{
1568 HRESULT rc = S_OK;
1569
1570 /* check throttle limits */
1571 if ( aCPUExecutionCap < 1
1572 || aCPUExecutionCap > 100
1573 )
1574 return setError(E_INVALIDARG,
1575 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1576 aCPUExecutionCap, 1, 100);
1577
1578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1579
1580 rc = i_checkStateDependency(MutableOrRunningStateDep);
1581 if (FAILED(rc)) return rc;
1582
1583 alock.release();
1584 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1585 alock.acquire();
1586 if (FAILED(rc)) return rc;
1587
1588 i_setModified(IsModified_MachineData);
1589 mHWData.backup();
1590 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1591
1592 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1593 if (Global::IsOnline(mData->mMachineState))
1594 i_saveSettings(NULL, alock);
1595
1596 return S_OK;
1597}
1598
1599HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1600{
1601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1602
1603 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1604
1605 return S_OK;
1606}
1607
1608HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1609{
1610 HRESULT rc = S_OK;
1611
1612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 rc = i_checkStateDependency(MutableStateDep);
1615 if (FAILED(rc)) return rc;
1616
1617 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1618 {
1619 if (aCPUHotPlugEnabled)
1620 {
1621 i_setModified(IsModified_MachineData);
1622 mHWData.backup();
1623
1624 /* Add the amount of CPUs currently attached */
1625 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1626 mHWData->mCPUAttached[i] = true;
1627 }
1628 else
1629 {
1630 /*
1631 * We can disable hotplug only if the amount of maximum CPUs is equal
1632 * to the amount of attached CPUs
1633 */
1634 unsigned cCpusAttached = 0;
1635 unsigned iHighestId = 0;
1636
1637 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1638 {
1639 if (mHWData->mCPUAttached[i])
1640 {
1641 cCpusAttached++;
1642 iHighestId = i;
1643 }
1644 }
1645
1646 if ( (cCpusAttached != mHWData->mCPUCount)
1647 || (iHighestId >= mHWData->mCPUCount))
1648 return setError(E_INVALIDARG,
1649 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1650
1651 i_setModified(IsModified_MachineData);
1652 mHWData.backup();
1653 }
1654 }
1655
1656 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1657
1658 return rc;
1659}
1660
1661HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1662{
1663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1664
1665 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1666
1667 return S_OK;
1668}
1669
1670HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1671{
1672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1675 if (SUCCEEDED(hrc))
1676 {
1677 i_setModified(IsModified_MachineData);
1678 mHWData.backup();
1679 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1680 }
1681 return hrc;
1682}
1683
1684HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1685{
1686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1687 aCPUProfile = mHWData->mCpuProfile;
1688 return S_OK;
1689}
1690
1691HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1692{
1693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1694 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1695 if (SUCCEEDED(hrc))
1696 {
1697 i_setModified(IsModified_MachineData);
1698 mHWData.backup();
1699 /* Empty equals 'host'. */
1700 if (aCPUProfile.isNotEmpty())
1701 mHWData->mCpuProfile = aCPUProfile;
1702 else
1703 mHWData->mCpuProfile = "host";
1704 }
1705 return hrc;
1706}
1707
1708HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1709{
1710#ifdef VBOX_WITH_USB_CARDREADER
1711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1712
1713 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1714
1715 return S_OK;
1716#else
1717 NOREF(aEmulatedUSBCardReaderEnabled);
1718 return E_NOTIMPL;
1719#endif
1720}
1721
1722HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1723{
1724#ifdef VBOX_WITH_USB_CARDREADER
1725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1726
1727 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1728 if (FAILED(rc)) return rc;
1729
1730 i_setModified(IsModified_MachineData);
1731 mHWData.backup();
1732 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1733
1734 return S_OK;
1735#else
1736 NOREF(aEmulatedUSBCardReaderEnabled);
1737 return E_NOTIMPL;
1738#endif
1739}
1740
1741HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1742{
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 *aHPETEnabled = mHWData->mHPETEnabled;
1746
1747 return S_OK;
1748}
1749
1750HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1751{
1752 HRESULT rc = S_OK;
1753
1754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1755
1756 rc = i_checkStateDependency(MutableStateDep);
1757 if (FAILED(rc)) return rc;
1758
1759 i_setModified(IsModified_MachineData);
1760 mHWData.backup();
1761
1762 mHWData->mHPETEnabled = aHPETEnabled;
1763
1764 return rc;
1765}
1766
1767/** @todo this method should not be public */
1768HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1769{
1770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1771
1772 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1773
1774 return S_OK;
1775}
1776
1777/**
1778 * Set the memory balloon size.
1779 *
1780 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1781 * we have to make sure that we never call IGuest from here.
1782 */
1783HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1784{
1785 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1786#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1787 /* check limits */
1788 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1789 return setError(E_INVALIDARG,
1790 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1791 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1792
1793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1794
1795 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
1796 if (FAILED(rc)) return rc;
1797
1798 i_setModified(IsModified_MachineData);
1799 mHWData.backup();
1800 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1801
1802 return S_OK;
1803#else
1804 NOREF(aMemoryBalloonSize);
1805 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1806#endif
1807}
1808
1809HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1810{
1811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1812
1813 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1814 return S_OK;
1815}
1816
1817HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1818{
1819#ifdef VBOX_WITH_PAGE_SHARING
1820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 HRESULT rc = i_checkStateDependency(MutableStateDep);
1823 if (FAILED(rc)) return rc;
1824
1825 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1826 i_setModified(IsModified_MachineData);
1827 mHWData.backup();
1828 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1829 return S_OK;
1830#else
1831 NOREF(aPageFusionEnabled);
1832 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1833#endif
1834}
1835
1836HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1837{
1838 /* mBIOSSettings is constant during life time, no need to lock */
1839 aBIOSSettings = mBIOSSettings;
1840
1841 return S_OK;
1842}
1843
1844HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1845{
1846 /* mTrustedPlatformModule is constant during life time, no need to lock */
1847 aTrustedPlatformModule = mTrustedPlatformModule;
1848
1849 return S_OK;
1850}
1851
1852HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1853{
1854 /* mNvramStore is constant during life time, no need to lock */
1855 aNvramStore = mNvramStore;
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 aRecordingSettings = mRecordingSettings;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1870{
1871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 aGraphicsAdapter = mGraphicsAdapter;
1874
1875 return S_OK;
1876}
1877
1878HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1879{
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 switch (aProperty)
1883 {
1884 case CPUPropertyType_PAE:
1885 *aValue = mHWData->mPAEEnabled;
1886 break;
1887
1888 case CPUPropertyType_LongMode:
1889 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1890 *aValue = TRUE;
1891 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1892 *aValue = FALSE;
1893#if HC_ARCH_BITS == 64
1894 else
1895 *aValue = TRUE;
1896#else
1897 else
1898 {
1899 *aValue = FALSE;
1900
1901 ComObjPtr<GuestOSType> pGuestOSType;
1902 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1903 pGuestOSType);
1904 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1905 {
1906 if (pGuestOSType->i_is64Bit())
1907 {
1908 ComObjPtr<Host> pHost = mParent->i_host();
1909 alock.release();
1910
1911 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1912 if (FAILED(hrc2))
1913 *aValue = FALSE;
1914 }
1915 }
1916 }
1917#endif
1918 break;
1919
1920 case CPUPropertyType_TripleFaultReset:
1921 *aValue = mHWData->mTripleFaultReset;
1922 break;
1923
1924 case CPUPropertyType_APIC:
1925 *aValue = mHWData->mAPIC;
1926 break;
1927
1928 case CPUPropertyType_X2APIC:
1929 *aValue = mHWData->mX2APIC;
1930 break;
1931
1932 case CPUPropertyType_IBPBOnVMExit:
1933 *aValue = mHWData->mIBPBOnVMExit;
1934 break;
1935
1936 case CPUPropertyType_IBPBOnVMEntry:
1937 *aValue = mHWData->mIBPBOnVMEntry;
1938 break;
1939
1940 case CPUPropertyType_SpecCtrl:
1941 *aValue = mHWData->mSpecCtrl;
1942 break;
1943
1944 case CPUPropertyType_SpecCtrlByHost:
1945 *aValue = mHWData->mSpecCtrlByHost;
1946 break;
1947
1948 case CPUPropertyType_HWVirt:
1949 *aValue = mHWData->mNestedHWVirt;
1950 break;
1951
1952 case CPUPropertyType_L1DFlushOnEMTScheduling:
1953 *aValue = mHWData->mL1DFlushOnSched;
1954 break;
1955
1956 case CPUPropertyType_L1DFlushOnVMEntry:
1957 *aValue = mHWData->mL1DFlushOnVMEntry;
1958 break;
1959
1960 case CPUPropertyType_MDSClearOnEMTScheduling:
1961 *aValue = mHWData->mMDSClearOnSched;
1962 break;
1963
1964 case CPUPropertyType_MDSClearOnVMEntry:
1965 *aValue = mHWData->mMDSClearOnVMEntry;
1966 break;
1967
1968 default:
1969 return E_INVALIDARG;
1970 }
1971 return S_OK;
1972}
1973
1974HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1975{
1976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 HRESULT rc = i_checkStateDependency(MutableStateDep);
1979 if (FAILED(rc)) return rc;
1980
1981 switch (aProperty)
1982 {
1983 case CPUPropertyType_PAE:
1984 i_setModified(IsModified_MachineData);
1985 mHWData.backup();
1986 mHWData->mPAEEnabled = !!aValue;
1987 break;
1988
1989 case CPUPropertyType_LongMode:
1990 i_setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1993 break;
1994
1995 case CPUPropertyType_TripleFaultReset:
1996 i_setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mTripleFaultReset = !!aValue;
1999 break;
2000
2001 case CPUPropertyType_APIC:
2002 if (mHWData->mX2APIC)
2003 aValue = TRUE;
2004 i_setModified(IsModified_MachineData);
2005 mHWData.backup();
2006 mHWData->mAPIC = !!aValue;
2007 break;
2008
2009 case CPUPropertyType_X2APIC:
2010 i_setModified(IsModified_MachineData);
2011 mHWData.backup();
2012 mHWData->mX2APIC = !!aValue;
2013 if (aValue)
2014 mHWData->mAPIC = !!aValue;
2015 break;
2016
2017 case CPUPropertyType_IBPBOnVMExit:
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mIBPBOnVMExit = !!aValue;
2021 break;
2022
2023 case CPUPropertyType_IBPBOnVMEntry:
2024 i_setModified(IsModified_MachineData);
2025 mHWData.backup();
2026 mHWData->mIBPBOnVMEntry = !!aValue;
2027 break;
2028
2029 case CPUPropertyType_SpecCtrl:
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mSpecCtrl = !!aValue;
2033 break;
2034
2035 case CPUPropertyType_SpecCtrlByHost:
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mSpecCtrlByHost = !!aValue;
2039 break;
2040
2041 case CPUPropertyType_HWVirt:
2042 i_setModified(IsModified_MachineData);
2043 mHWData.backup();
2044 mHWData->mNestedHWVirt = !!aValue;
2045 break;
2046
2047 case CPUPropertyType_L1DFlushOnEMTScheduling:
2048 i_setModified(IsModified_MachineData);
2049 mHWData.backup();
2050 mHWData->mL1DFlushOnSched = !!aValue;
2051 break;
2052
2053 case CPUPropertyType_L1DFlushOnVMEntry:
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mL1DFlushOnVMEntry = !!aValue;
2057 break;
2058
2059 case CPUPropertyType_MDSClearOnEMTScheduling:
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mMDSClearOnSched = !!aValue;
2063 break;
2064
2065 case CPUPropertyType_MDSClearOnVMEntry:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mMDSClearOnVMEntry = !!aValue;
2069 break;
2070
2071 default:
2072 return E_INVALIDARG;
2073 }
2074 return S_OK;
2075}
2076
2077HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2078 ULONG *aValEcx, ULONG *aValEdx)
2079{
2080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2081 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2082 {
2083 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2084 it != mHWData->mCpuIdLeafList.end();
2085 ++it)
2086 {
2087 if (aOrdinal == 0)
2088 {
2089 const settings::CpuIdLeaf &rLeaf= *it;
2090 *aIdx = rLeaf.idx;
2091 *aSubIdx = rLeaf.idxSub;
2092 *aValEax = rLeaf.uEax;
2093 *aValEbx = rLeaf.uEbx;
2094 *aValEcx = rLeaf.uEcx;
2095 *aValEdx = rLeaf.uEdx;
2096 return S_OK;
2097 }
2098 aOrdinal--;
2099 }
2100 }
2101 return E_INVALIDARG;
2102}
2103
2104HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2105{
2106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 /*
2109 * Search the list.
2110 */
2111 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2112 {
2113 const settings::CpuIdLeaf &rLeaf= *it;
2114 if ( rLeaf.idx == aIdx
2115 && ( aSubIdx == UINT32_MAX
2116 || rLeaf.idxSub == aSubIdx) )
2117 {
2118 *aValEax = rLeaf.uEax;
2119 *aValEbx = rLeaf.uEbx;
2120 *aValEcx = rLeaf.uEcx;
2121 *aValEdx = rLeaf.uEdx;
2122 return S_OK;
2123 }
2124 }
2125
2126 return E_INVALIDARG;
2127}
2128
2129
2130HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2131{
2132 /*
2133 * Validate input before taking locks and checking state.
2134 */
2135 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2136 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2137 if ( aIdx >= UINT32_C(0x20)
2138 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2139 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2140 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2141
2142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2143 HRESULT rc = i_checkStateDependency(MutableStateDep);
2144 if (FAILED(rc)) return rc;
2145
2146 /*
2147 * Impose a maximum number of leaves.
2148 */
2149 if (mHWData->mCpuIdLeafList.size() > 256)
2150 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2151
2152 /*
2153 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2154 */
2155 i_setModified(IsModified_MachineData);
2156 mHWData.backup();
2157
2158 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2159 {
2160 settings::CpuIdLeaf &rLeaf= *it;
2161 if ( rLeaf.idx == aIdx
2162 && ( aSubIdx == UINT32_MAX
2163 || rLeaf.idxSub == aSubIdx) )
2164 it = mHWData->mCpuIdLeafList.erase(it);
2165 else
2166 ++it;
2167 }
2168
2169 settings::CpuIdLeaf NewLeaf;
2170 NewLeaf.idx = aIdx;
2171 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2172 NewLeaf.uEax = aValEax;
2173 NewLeaf.uEbx = aValEbx;
2174 NewLeaf.uEcx = aValEcx;
2175 NewLeaf.uEdx = aValEdx;
2176 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2177 return S_OK;
2178}
2179
2180HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2181{
2182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2183
2184 HRESULT rc = i_checkStateDependency(MutableStateDep);
2185 if (FAILED(rc)) return rc;
2186
2187 /*
2188 * Do the removal.
2189 */
2190 bool fModified = mHWData.isBackedUp();
2191 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2192 {
2193 settings::CpuIdLeaf &rLeaf= *it;
2194 if ( rLeaf.idx == aIdx
2195 && ( aSubIdx == UINT32_MAX
2196 || rLeaf.idxSub == aSubIdx) )
2197 {
2198 if (!fModified)
2199 {
2200 fModified = true;
2201 i_setModified(IsModified_MachineData);
2202 mHWData.backup();
2203 // Start from the beginning, since mHWData.backup() creates
2204 // a new list, causing iterator mixup. This makes sure that
2205 // the settings are not unnecessarily marked as modified,
2206 // at the price of extra list walking.
2207 it = mHWData->mCpuIdLeafList.begin();
2208 }
2209 else
2210 it = mHWData->mCpuIdLeafList.erase(it);
2211 }
2212 else
2213 ++it;
2214 }
2215
2216 return S_OK;
2217}
2218
2219HRESULT Machine::removeAllCPUIDLeaves()
2220{
2221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2222
2223 HRESULT rc = i_checkStateDependency(MutableStateDep);
2224 if (FAILED(rc)) return rc;
2225
2226 if (mHWData->mCpuIdLeafList.size() > 0)
2227 {
2228 i_setModified(IsModified_MachineData);
2229 mHWData.backup();
2230
2231 mHWData->mCpuIdLeafList.clear();
2232 }
2233
2234 return S_OK;
2235}
2236HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2237{
2238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2239
2240 switch(aProperty)
2241 {
2242 case HWVirtExPropertyType_Enabled:
2243 *aValue = mHWData->mHWVirtExEnabled;
2244 break;
2245
2246 case HWVirtExPropertyType_VPID:
2247 *aValue = mHWData->mHWVirtExVPIDEnabled;
2248 break;
2249
2250 case HWVirtExPropertyType_NestedPaging:
2251 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2252 break;
2253
2254 case HWVirtExPropertyType_UnrestrictedExecution:
2255 *aValue = mHWData->mHWVirtExUXEnabled;
2256 break;
2257
2258 case HWVirtExPropertyType_LargePages:
2259 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2260 break;
2261
2262 case HWVirtExPropertyType_Force:
2263 *aValue = mHWData->mHWVirtExForceEnabled;
2264 break;
2265
2266 case HWVirtExPropertyType_UseNativeApi:
2267 *aValue = mHWData->mHWVirtExUseNativeApi;
2268 break;
2269
2270 case HWVirtExPropertyType_VirtVmsaveVmload:
2271 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2272 break;
2273
2274 default:
2275 return E_INVALIDARG;
2276 }
2277 return S_OK;
2278}
2279
2280HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2281{
2282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2283
2284 HRESULT rc = i_checkStateDependency(MutableStateDep);
2285 if (FAILED(rc)) return rc;
2286
2287 switch (aProperty)
2288 {
2289 case HWVirtExPropertyType_Enabled:
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mHWVirtExEnabled = !!aValue;
2293 break;
2294
2295 case HWVirtExPropertyType_VPID:
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2305 break;
2306
2307 case HWVirtExPropertyType_UnrestrictedExecution:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mHWVirtExUXEnabled = !!aValue;
2311 break;
2312
2313 case HWVirtExPropertyType_LargePages:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2317 break;
2318
2319 case HWVirtExPropertyType_Force:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mHWVirtExForceEnabled = !!aValue;
2323 break;
2324
2325 case HWVirtExPropertyType_UseNativeApi:
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mHWVirtExUseNativeApi = !!aValue;
2329 break;
2330
2331 case HWVirtExPropertyType_VirtVmsaveVmload:
2332 i_setModified(IsModified_MachineData);
2333 mHWData.backup();
2334 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2335 break;
2336
2337 default:
2338 return E_INVALIDARG;
2339 }
2340
2341 return S_OK;
2342}
2343
2344HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2345{
2346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2347
2348 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2349
2350 return S_OK;
2351}
2352
2353HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2354{
2355 /** @todo (r=dmik):
2356 * 1. Allow to change the name of the snapshot folder containing snapshots
2357 * 2. Rename the folder on disk instead of just changing the property
2358 * value (to be smart and not to leave garbage). Note that it cannot be
2359 * done here because the change may be rolled back. Thus, the right
2360 * place is #saveSettings().
2361 */
2362
2363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2364
2365 HRESULT rc = i_checkStateDependency(MutableStateDep);
2366 if (FAILED(rc)) return rc;
2367
2368 if (!mData->mCurrentSnapshot.isNull())
2369 return setError(E_FAIL,
2370 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2371
2372 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2373
2374 if (strSnapshotFolder.isEmpty())
2375 strSnapshotFolder = "Snapshots";
2376 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2377 if (RT_FAILURE(vrc))
2378 return setErrorBoth(E_FAIL, vrc,
2379 tr("Invalid snapshot folder '%s' (%Rrc)"),
2380 strSnapshotFolder.c_str(), vrc);
2381
2382 i_setModified(IsModified_MachineData);
2383 mUserData.backup();
2384
2385 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2386
2387 return S_OK;
2388}
2389
2390HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2391{
2392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2393
2394 aMediumAttachments.resize(mMediumAttachments->size());
2395 size_t i = 0;
2396 for (MediumAttachmentList::const_iterator
2397 it = mMediumAttachments->begin();
2398 it != mMediumAttachments->end();
2399 ++it, ++i)
2400 aMediumAttachments[i] = *it;
2401
2402 return S_OK;
2403}
2404
2405HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2406{
2407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 Assert(!!mVRDEServer);
2410
2411 aVRDEServer = mVRDEServer;
2412
2413 return S_OK;
2414}
2415
2416HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2417{
2418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2419
2420 aAudioAdapter = mAudioAdapter;
2421
2422 return S_OK;
2423}
2424
2425HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2426{
2427#ifdef VBOX_WITH_VUSB
2428 clearError();
2429 MultiResult rc(S_OK);
2430
2431# ifdef VBOX_WITH_USB
2432 rc = mParent->i_host()->i_checkUSBProxyService();
2433 if (FAILED(rc)) return rc;
2434# endif
2435
2436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2437
2438 aUSBControllers.resize(mUSBControllers->size());
2439 size_t i = 0;
2440 for (USBControllerList::const_iterator
2441 it = mUSBControllers->begin();
2442 it != mUSBControllers->end();
2443 ++it, ++i)
2444 aUSBControllers[i] = *it;
2445
2446 return S_OK;
2447#else
2448 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2449 * extended error info to indicate that USB is simply not available
2450 * (w/o treating it as a failure), for example, as in OSE */
2451 NOREF(aUSBControllers);
2452 ReturnComNotImplemented();
2453#endif /* VBOX_WITH_VUSB */
2454}
2455
2456HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2457{
2458#ifdef VBOX_WITH_VUSB
2459 clearError();
2460 MultiResult rc(S_OK);
2461
2462# ifdef VBOX_WITH_USB
2463 rc = mParent->i_host()->i_checkUSBProxyService();
2464 if (FAILED(rc)) return rc;
2465# endif
2466
2467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 aUSBDeviceFilters = mUSBDeviceFilters;
2470 return rc;
2471#else
2472 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2473 * extended error info to indicate that USB is simply not available
2474 * (w/o treating it as a failure), for example, as in OSE */
2475 NOREF(aUSBDeviceFilters);
2476 ReturnComNotImplemented();
2477#endif /* VBOX_WITH_VUSB */
2478}
2479
2480HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2481{
2482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 aSettingsFilePath = mData->m_strConfigFileFull;
2485
2486 return S_OK;
2487}
2488
2489HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2490{
2491 RT_NOREF(aSettingsFilePath);
2492 ReturnComNotImplemented();
2493}
2494
2495HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2496{
2497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2500 if (FAILED(rc)) return rc;
2501
2502 if (!mData->pMachineConfigFile->fileExists())
2503 // this is a new machine, and no config file exists yet:
2504 *aSettingsModified = TRUE;
2505 else
2506 *aSettingsModified = (mData->flModifications != 0);
2507
2508 return S_OK;
2509}
2510
2511HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2512{
2513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 *aSessionState = mData->mSession.mState;
2516
2517 return S_OK;
2518}
2519
2520HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2521{
2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2523
2524 aSessionName = mData->mSession.mName;
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 *aSessionPID = mData->mSession.mPID;
2534
2535 return S_OK;
2536}
2537
2538HRESULT Machine::getState(MachineState_T *aState)
2539{
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 *aState = mData->mMachineState;
2543 Assert(mData->mMachineState != MachineState_Null);
2544
2545 return S_OK;
2546}
2547
2548HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2549{
2550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2551
2552 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2553
2554 return S_OK;
2555}
2556
2557HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2558{
2559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 aStateFilePath = mSSData->strStateFilePath;
2562
2563 return S_OK;
2564}
2565
2566HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2567{
2568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2569
2570 i_getLogFolder(aLogFolder);
2571
2572 return S_OK;
2573}
2574
2575HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2576{
2577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 aCurrentSnapshot = mData->mCurrentSnapshot;
2580
2581 return S_OK;
2582}
2583
2584HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2585{
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2589 ? 0
2590 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2591
2592 return S_OK;
2593}
2594
2595HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2596{
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599 /* Note: for machines with no snapshots, we always return FALSE
2600 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2601 * reasons :) */
2602
2603 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2604 ? FALSE
2605 : mData->mCurrentStateModified;
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2611{
2612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2613
2614 aSharedFolders.resize(mHWData->mSharedFolders.size());
2615 size_t i = 0;
2616 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2617 it = mHWData->mSharedFolders.begin();
2618 it != mHWData->mSharedFolders.end();
2619 ++it, ++i)
2620 aSharedFolders[i] = *it;
2621
2622 return S_OK;
2623}
2624
2625HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2626{
2627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 *aClipboardMode = mHWData->mClipboardMode;
2630
2631 return S_OK;
2632}
2633
2634HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2635{
2636 HRESULT rc = S_OK;
2637
2638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2639
2640 rc = i_checkStateDependency(MutableOrRunningStateDep);
2641 if (FAILED(rc)) return rc;
2642
2643 alock.release();
2644 rc = i_onClipboardModeChange(aClipboardMode);
2645 alock.acquire();
2646 if (FAILED(rc)) return rc;
2647
2648 i_setModified(IsModified_MachineData);
2649 mHWData.backup();
2650 mHWData->mClipboardMode = aClipboardMode;
2651
2652 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2653 if (Global::IsOnline(mData->mMachineState))
2654 i_saveSettings(NULL, alock);
2655
2656 return S_OK;
2657}
2658
2659HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2660{
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2664
2665 return S_OK;
2666}
2667
2668HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2669{
2670 HRESULT rc = S_OK;
2671
2672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 rc = i_checkStateDependency(MutableOrRunningStateDep);
2675 if (FAILED(rc)) return rc;
2676
2677 alock.release();
2678 rc = i_onClipboardFileTransferModeChange(aEnabled);
2679 alock.acquire();
2680 if (FAILED(rc)) return rc;
2681
2682 i_setModified(IsModified_MachineData);
2683 mHWData.backup();
2684 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2685
2686 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2687 if (Global::IsOnline(mData->mMachineState))
2688 i_saveSettings(NULL, alock);
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 *aDnDMode = mHWData->mDnDMode;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2703{
2704 HRESULT rc = S_OK;
2705
2706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 rc = i_checkStateDependency(MutableOrRunningStateDep);
2709 if (FAILED(rc)) return rc;
2710
2711 alock.release();
2712 rc = i_onDnDModeChange(aDnDMode);
2713
2714 alock.acquire();
2715 if (FAILED(rc)) return rc;
2716
2717 i_setModified(IsModified_MachineData);
2718 mHWData.backup();
2719 mHWData->mDnDMode = aDnDMode;
2720
2721 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2722 if (Global::IsOnline(mData->mMachineState))
2723 i_saveSettings(NULL, alock);
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aStorageControllers.resize(mStorageControllers->size());
2733 size_t i = 0;
2734 for (StorageControllerList::const_iterator
2735 it = mStorageControllers->begin();
2736 it != mStorageControllers->end();
2737 ++it, ++i)
2738 aStorageControllers[i] = *it;
2739
2740 return S_OK;
2741}
2742
2743HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2744{
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 *aEnabled = mUserData->s.fTeleporterEnabled;
2748
2749 return S_OK;
2750}
2751
2752HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2753{
2754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 /* Only allow it to be set to true when PoweredOff or Aborted.
2757 (Clearing it is always permitted.) */
2758 if ( aTeleporterEnabled
2759 && mData->mRegistered
2760 && ( !i_isSessionMachine()
2761 || ( mData->mMachineState != MachineState_PoweredOff
2762 && mData->mMachineState != MachineState_Teleported
2763 && mData->mMachineState != MachineState_Aborted
2764 )
2765 )
2766 )
2767 return setError(VBOX_E_INVALID_VM_STATE,
2768 tr("The machine is not powered off (state is %s)"),
2769 Global::stringifyMachineState(mData->mMachineState));
2770
2771 i_setModified(IsModified_MachineData);
2772 mUserData.backup();
2773 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2774
2775 return S_OK;
2776}
2777
2778HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2779{
2780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2781
2782 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2788{
2789 if (aTeleporterPort >= _64K)
2790 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2791
2792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2795 if (FAILED(rc)) return rc;
2796
2797 i_setModified(IsModified_MachineData);
2798 mUserData.backup();
2799 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2814{
2815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2818 if (FAILED(rc)) return rc;
2819
2820 i_setModified(IsModified_MachineData);
2821 mUserData.backup();
2822 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2828{
2829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2830 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2831
2832 return S_OK;
2833}
2834
2835HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2836{
2837 /*
2838 * Hash the password first.
2839 */
2840 com::Utf8Str aT = aTeleporterPassword;
2841
2842 if (!aT.isEmpty())
2843 {
2844 if (VBoxIsPasswordHashed(&aT))
2845 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2846 VBoxHashPassword(&aT);
2847 }
2848
2849 /*
2850 * Do the update.
2851 */
2852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2854 if (SUCCEEDED(hrc))
2855 {
2856 i_setModified(IsModified_MachineData);
2857 mUserData.backup();
2858 mUserData->s.strTeleporterPassword = aT;
2859 }
2860
2861 return hrc;
2862}
2863
2864HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2874{
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 /* Only allow it to be set to true when PoweredOff or Aborted.
2878 (Clearing it is always permitted.) */
2879 if ( aRTCUseUTC
2880 && mData->mRegistered
2881 && ( !i_isSessionMachine()
2882 || ( mData->mMachineState != MachineState_PoweredOff
2883 && mData->mMachineState != MachineState_Teleported
2884 && mData->mMachineState != MachineState_Aborted
2885 )
2886 )
2887 )
2888 return setError(VBOX_E_INVALID_VM_STATE,
2889 tr("The machine is not powered off (state is %s)"),
2890 Global::stringifyMachineState(mData->mMachineState));
2891
2892 i_setModified(IsModified_MachineData);
2893 mUserData.backup();
2894 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2904
2905 return S_OK;
2906}
2907
2908HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2909{
2910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 HRESULT rc = i_checkStateDependency(MutableStateDep);
2913 if (FAILED(rc)) return rc;
2914
2915 i_setModified(IsModified_MachineData);
2916 mHWData.backup();
2917 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2923{
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aIOCacheSize = mHWData->mIOCacheSize;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2932{
2933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2934
2935 HRESULT rc = i_checkStateDependency(MutableStateDep);
2936 if (FAILED(rc)) return rc;
2937
2938 i_setModified(IsModified_MachineData);
2939 mHWData.backup();
2940 mHWData->mIOCacheSize = aIOCacheSize;
2941
2942 return S_OK;
2943}
2944
2945
2946/**
2947 * @note Locks objects!
2948 */
2949HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2950 LockType_T aLockType)
2951{
2952 /* check the session state */
2953 SessionState_T state;
2954 HRESULT rc = aSession->COMGETTER(State)(&state);
2955 if (FAILED(rc)) return rc;
2956
2957 if (state != SessionState_Unlocked)
2958 return setError(VBOX_E_INVALID_OBJECT_STATE,
2959 tr("The given session is busy"));
2960
2961 // get the client's IInternalSessionControl interface
2962 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2963 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2964 E_INVALIDARG);
2965
2966 // session name (only used in some code paths)
2967 Utf8Str strSessionName;
2968
2969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 if (!mData->mRegistered)
2972 return setError(E_UNEXPECTED,
2973 tr("The machine '%s' is not registered"),
2974 mUserData->s.strName.c_str());
2975
2976 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2977
2978 SessionState_T oldState = mData->mSession.mState;
2979 /* Hack: in case the session is closing and there is a progress object
2980 * which allows waiting for the session to be closed, take the opportunity
2981 * and do a limited wait (max. 1 second). This helps a lot when the system
2982 * is busy and thus session closing can take a little while. */
2983 if ( mData->mSession.mState == SessionState_Unlocking
2984 && mData->mSession.mProgress)
2985 {
2986 alock.release();
2987 mData->mSession.mProgress->WaitForCompletion(1000);
2988 alock.acquire();
2989 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2990 }
2991
2992 // try again now
2993 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2994 // (i.e. session machine exists)
2995 && (aLockType == LockType_Shared) // caller wants a shared link to the
2996 // existing session that holds the write lock:
2997 )
2998 {
2999 // OK, share the session... we are now dealing with three processes:
3000 // 1) VBoxSVC (where this code runs);
3001 // 2) process C: the caller's client process (who wants a shared session);
3002 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3003
3004 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3005 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3006 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3007 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3008 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3009
3010 /*
3011 * Release the lock before calling the client process. It's safe here
3012 * since the only thing to do after we get the lock again is to add
3013 * the remote control to the list (which doesn't directly influence
3014 * anything).
3015 */
3016 alock.release();
3017
3018 // get the console of the session holding the write lock (this is a remote call)
3019 ComPtr<IConsole> pConsoleW;
3020 if (mData->mSession.mLockType == LockType_VM)
3021 {
3022 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3023 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3024 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3025 if (FAILED(rc))
3026 // the failure may occur w/o any error info (from RPC), so provide one
3027 return setError(VBOX_E_VM_ERROR,
3028 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3029 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3030 }
3031
3032 // share the session machine and W's console with the caller's session
3033 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3034 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3035 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3036
3037 if (FAILED(rc))
3038 // the failure may occur w/o any error info (from RPC), so provide one
3039 return setError(VBOX_E_VM_ERROR,
3040 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3041 alock.acquire();
3042
3043 // need to revalidate the state after acquiring the lock again
3044 if (mData->mSession.mState != SessionState_Locked)
3045 {
3046 pSessionControl->Uninitialize();
3047 return setError(VBOX_E_INVALID_SESSION_STATE,
3048 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3049 mUserData->s.strName.c_str());
3050 }
3051
3052 // add the caller's session to the list
3053 mData->mSession.mRemoteControls.push_back(pSessionControl);
3054 }
3055 else if ( mData->mSession.mState == SessionState_Locked
3056 || mData->mSession.mState == SessionState_Unlocking
3057 )
3058 {
3059 // sharing not permitted, or machine still unlocking:
3060 return setError(VBOX_E_INVALID_OBJECT_STATE,
3061 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3062 mUserData->s.strName.c_str());
3063 }
3064 else
3065 {
3066 // machine is not locked: then write-lock the machine (create the session machine)
3067
3068 // must not be busy
3069 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3070
3071 // get the caller's session PID
3072 RTPROCESS pid = NIL_RTPROCESS;
3073 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3074 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3075 Assert(pid != NIL_RTPROCESS);
3076
3077 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3078
3079 if (fLaunchingVMProcess)
3080 {
3081 if (mData->mSession.mPID == NIL_RTPROCESS)
3082 {
3083 // two or more clients racing for a lock, the one which set the
3084 // session state to Spawning will win, the others will get an
3085 // error as we can't decide here if waiting a little would help
3086 // (only for shared locks this would avoid an error)
3087 return setError(VBOX_E_INVALID_OBJECT_STATE,
3088 tr("The machine '%s' already has a lock request pending"),
3089 mUserData->s.strName.c_str());
3090 }
3091
3092 // this machine is awaiting for a spawning session to be opened:
3093 // then the calling process must be the one that got started by
3094 // LaunchVMProcess()
3095
3096 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3097 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3098
3099#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3100 /* Hardened windows builds spawns three processes when a VM is
3101 launched, the 3rd one is the one that will end up here. */
3102 RTPROCESS pidParent;
3103 int vrc = RTProcQueryParent(pid, &pidParent);
3104 if (RT_SUCCESS(vrc))
3105 vrc = RTProcQueryParent(pidParent, &pidParent);
3106 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3107 || vrc == VERR_ACCESS_DENIED)
3108 {
3109 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3110 mData->mSession.mPID = pid;
3111 }
3112#endif
3113
3114 if (mData->mSession.mPID != pid)
3115 return setError(E_ACCESSDENIED,
3116 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3117 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3118 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3119 }
3120
3121 // create the mutable SessionMachine from the current machine
3122 ComObjPtr<SessionMachine> sessionMachine;
3123 sessionMachine.createObject();
3124 rc = sessionMachine->init(this);
3125 AssertComRC(rc);
3126
3127 /* NOTE: doing return from this function after this point but
3128 * before the end is forbidden since it may call SessionMachine::uninit()
3129 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3130 * lock while still holding the Machine lock in alock so that a deadlock
3131 * is possible due to the wrong lock order. */
3132
3133 if (SUCCEEDED(rc))
3134 {
3135 /*
3136 * Set the session state to Spawning to protect against subsequent
3137 * attempts to open a session and to unregister the machine after
3138 * we release the lock.
3139 */
3140 SessionState_T origState = mData->mSession.mState;
3141 mData->mSession.mState = SessionState_Spawning;
3142
3143#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3144 /* Get the client token ID to be passed to the client process */
3145 Utf8Str strTokenId;
3146 sessionMachine->i_getTokenId(strTokenId);
3147 Assert(!strTokenId.isEmpty());
3148#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3149 /* Get the client token to be passed to the client process */
3150 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3151 /* The token is now "owned" by pToken, fix refcount */
3152 if (!pToken.isNull())
3153 pToken->Release();
3154#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3155
3156 /*
3157 * Release the lock before calling the client process -- it will call
3158 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3159 * because the state is Spawning, so that LaunchVMProcess() and
3160 * LockMachine() calls will fail. This method, called before we
3161 * acquire the lock again, will fail because of the wrong PID.
3162 *
3163 * Note that mData->mSession.mRemoteControls accessed outside
3164 * the lock may not be modified when state is Spawning, so it's safe.
3165 */
3166 alock.release();
3167
3168 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3169#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3170 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3171#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3172 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3173 /* Now the token is owned by the client process. */
3174 pToken.setNull();
3175#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3176 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3177
3178 /* The failure may occur w/o any error info (from RPC), so provide one */
3179 if (FAILED(rc))
3180 setError(VBOX_E_VM_ERROR,
3181 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3182
3183 // get session name, either to remember or to compare against
3184 // the already known session name.
3185 {
3186 Bstr bstrSessionName;
3187 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3188 if (SUCCEEDED(rc2))
3189 strSessionName = bstrSessionName;
3190 }
3191
3192 if ( SUCCEEDED(rc)
3193 && fLaunchingVMProcess
3194 )
3195 {
3196 /* complete the remote session initialization */
3197
3198 /* get the console from the direct session */
3199 ComPtr<IConsole> console;
3200 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3201 ComAssertComRC(rc);
3202
3203 if (SUCCEEDED(rc) && !console)
3204 {
3205 ComAssert(!!console);
3206 rc = E_FAIL;
3207 }
3208
3209 /* assign machine & console to the remote session */
3210 if (SUCCEEDED(rc))
3211 {
3212 /*
3213 * after LaunchVMProcess(), the first and the only
3214 * entry in remoteControls is that remote session
3215 */
3216 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3217 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3218 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3219
3220 /* The failure may occur w/o any error info (from RPC), so provide one */
3221 if (FAILED(rc))
3222 setError(VBOX_E_VM_ERROR,
3223 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3224 }
3225
3226 if (FAILED(rc))
3227 pSessionControl->Uninitialize();
3228 }
3229
3230 /* acquire the lock again */
3231 alock.acquire();
3232
3233 /* Restore the session state */
3234 mData->mSession.mState = origState;
3235 }
3236
3237 // finalize spawning anyway (this is why we don't return on errors above)
3238 if (fLaunchingVMProcess)
3239 {
3240 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3241 /* Note that the progress object is finalized later */
3242 /** @todo Consider checking mData->mSession.mProgress for cancellation
3243 * around here. */
3244
3245 /* We don't reset mSession.mPID here because it is necessary for
3246 * SessionMachine::uninit() to reap the child process later. */
3247
3248 if (FAILED(rc))
3249 {
3250 /* Close the remote session, remove the remote control from the list
3251 * and reset session state to Closed (@note keep the code in sync
3252 * with the relevant part in checkForSpawnFailure()). */
3253
3254 Assert(mData->mSession.mRemoteControls.size() == 1);
3255 if (mData->mSession.mRemoteControls.size() == 1)
3256 {
3257 ErrorInfoKeeper eik;
3258 mData->mSession.mRemoteControls.front()->Uninitialize();
3259 }
3260
3261 mData->mSession.mRemoteControls.clear();
3262 mData->mSession.mState = SessionState_Unlocked;
3263 }
3264 }
3265 else
3266 {
3267 /* memorize PID of the directly opened session */
3268 if (SUCCEEDED(rc))
3269 mData->mSession.mPID = pid;
3270 }
3271
3272 if (SUCCEEDED(rc))
3273 {
3274 mData->mSession.mLockType = aLockType;
3275 /* memorize the direct session control and cache IUnknown for it */
3276 mData->mSession.mDirectControl = pSessionControl;
3277 mData->mSession.mState = SessionState_Locked;
3278 if (!fLaunchingVMProcess)
3279 mData->mSession.mName = strSessionName;
3280 /* associate the SessionMachine with this Machine */
3281 mData->mSession.mMachine = sessionMachine;
3282
3283 /* request an IUnknown pointer early from the remote party for later
3284 * identity checks (it will be internally cached within mDirectControl
3285 * at least on XPCOM) */
3286 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3287 NOREF(unk);
3288 }
3289
3290 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3291 * would break the lock order */
3292 alock.release();
3293
3294 /* uninitialize the created session machine on failure */
3295 if (FAILED(rc))
3296 sessionMachine->uninit();
3297 }
3298
3299 if (SUCCEEDED(rc))
3300 {
3301 /*
3302 * tell the client watcher thread to update the set of
3303 * machines that have open sessions
3304 */
3305 mParent->i_updateClientWatcher();
3306
3307 if (oldState != SessionState_Locked)
3308 /* fire an event */
3309 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3310 }
3311
3312 return rc;
3313}
3314
3315/**
3316 * @note Locks objects!
3317 */
3318HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3319 const com::Utf8Str &aName,
3320 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3321 ComPtr<IProgress> &aProgress)
3322{
3323 Utf8Str strFrontend(aName);
3324 /* "emergencystop" doesn't need the session, so skip the checks/interface
3325 * retrieval. This code doesn't quite fit in here, but introducing a
3326 * special API method would be even more effort, and would require explicit
3327 * support by every API client. It's better to hide the feature a bit. */
3328 if (strFrontend != "emergencystop")
3329 CheckComArgNotNull(aSession);
3330
3331 HRESULT rc = S_OK;
3332 if (strFrontend.isEmpty())
3333 {
3334 Bstr bstrFrontend;
3335 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3336 if (FAILED(rc))
3337 return rc;
3338 strFrontend = bstrFrontend;
3339 if (strFrontend.isEmpty())
3340 {
3341 ComPtr<ISystemProperties> systemProperties;
3342 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3343 if (FAILED(rc))
3344 return rc;
3345 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3346 if (FAILED(rc))
3347 return rc;
3348 strFrontend = bstrFrontend;
3349 }
3350 /* paranoia - emergencystop is not a valid default */
3351 if (strFrontend == "emergencystop")
3352 strFrontend = Utf8Str::Empty;
3353 }
3354 /* default frontend: Qt GUI */
3355 if (strFrontend.isEmpty())
3356 strFrontend = "GUI/Qt";
3357
3358 if (strFrontend != "emergencystop")
3359 {
3360 /* check the session state */
3361 SessionState_T state;
3362 rc = aSession->COMGETTER(State)(&state);
3363 if (FAILED(rc))
3364 return rc;
3365
3366 if (state != SessionState_Unlocked)
3367 return setError(VBOX_E_INVALID_OBJECT_STATE,
3368 tr("The given session is busy"));
3369
3370 /* get the IInternalSessionControl interface */
3371 ComPtr<IInternalSessionControl> control(aSession);
3372 ComAssertMsgRet(!control.isNull(),
3373 ("No IInternalSessionControl interface"),
3374 E_INVALIDARG);
3375
3376 /* get the teleporter enable state for the progress object init. */
3377 BOOL fTeleporterEnabled;
3378 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3379 if (FAILED(rc))
3380 return rc;
3381
3382 /* create a progress object */
3383 ComObjPtr<ProgressProxy> progress;
3384 progress.createObject();
3385 rc = progress->init(mParent,
3386 static_cast<IMachine*>(this),
3387 Bstr(tr("Starting VM")).raw(),
3388 TRUE /* aCancelable */,
3389 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3390 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3391 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3392 2 /* uFirstOperationWeight */,
3393 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3394
3395 if (SUCCEEDED(rc))
3396 {
3397 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3398 if (SUCCEEDED(rc))
3399 {
3400 aProgress = progress;
3401
3402 /* signal the client watcher thread */
3403 mParent->i_updateClientWatcher();
3404
3405 /* fire an event */
3406 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3407 }
3408 }
3409 }
3410 else
3411 {
3412 /* no progress object - either instant success or failure */
3413 aProgress = NULL;
3414
3415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3416
3417 if (mData->mSession.mState != SessionState_Locked)
3418 return setError(VBOX_E_INVALID_OBJECT_STATE,
3419 tr("The machine '%s' is not locked by a session"),
3420 mUserData->s.strName.c_str());
3421
3422 /* must have a VM process associated - do not kill normal API clients
3423 * with an open session */
3424 if (!Global::IsOnline(mData->mMachineState))
3425 return setError(VBOX_E_INVALID_OBJECT_STATE,
3426 tr("The machine '%s' does not have a VM process"),
3427 mUserData->s.strName.c_str());
3428
3429 /* forcibly terminate the VM process */
3430 if (mData->mSession.mPID != NIL_RTPROCESS)
3431 RTProcTerminate(mData->mSession.mPID);
3432
3433 /* signal the client watcher thread, as most likely the client has
3434 * been terminated */
3435 mParent->i_updateClientWatcher();
3436 }
3437
3438 return rc;
3439}
3440
3441HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3442{
3443 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3444 return setError(E_INVALIDARG,
3445 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3446 aPosition, SchemaDefs::MaxBootPosition);
3447
3448 if (aDevice == DeviceType_USB)
3449 return setError(E_NOTIMPL,
3450 tr("Booting from USB device is currently not supported"));
3451
3452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3453
3454 HRESULT rc = i_checkStateDependency(MutableStateDep);
3455 if (FAILED(rc)) return rc;
3456
3457 i_setModified(IsModified_MachineData);
3458 mHWData.backup();
3459 mHWData->mBootOrder[aPosition - 1] = aDevice;
3460
3461 return S_OK;
3462}
3463
3464HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3465{
3466 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3467 return setError(E_INVALIDARG,
3468 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3469 aPosition, SchemaDefs::MaxBootPosition);
3470
3471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 *aDevice = mHWData->mBootOrder[aPosition - 1];
3474
3475 return S_OK;
3476}
3477
3478HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3479 LONG aControllerPort,
3480 LONG aDevice,
3481 DeviceType_T aType,
3482 const ComPtr<IMedium> &aMedium)
3483{
3484 IMedium *aM = aMedium;
3485 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3486 aName.c_str(), aControllerPort, aDevice, aType, aM));
3487
3488 // request the host lock first, since might be calling Host methods for getting host drives;
3489 // next, protect the media tree all the while we're in here, as well as our member variables
3490 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3491 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3492
3493 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3494 if (FAILED(rc)) return rc;
3495
3496 /// @todo NEWMEDIA implicit machine registration
3497 if (!mData->mRegistered)
3498 return setError(VBOX_E_INVALID_OBJECT_STATE,
3499 tr("Cannot attach storage devices to an unregistered machine"));
3500
3501 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3502
3503 /* Check for an existing controller. */
3504 ComObjPtr<StorageController> ctl;
3505 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3506 if (FAILED(rc)) return rc;
3507
3508 StorageControllerType_T ctrlType;
3509 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3510 if (FAILED(rc))
3511 return setError(E_FAIL,
3512 tr("Could not get type of controller '%s'"),
3513 aName.c_str());
3514
3515 bool fSilent = false;
3516 Utf8Str strReconfig;
3517
3518 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3519 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3520 if ( mData->mMachineState == MachineState_Paused
3521 && strReconfig == "1")
3522 fSilent = true;
3523
3524 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3525 bool fHotplug = false;
3526 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3527 fHotplug = true;
3528
3529 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3530 return setError(VBOX_E_INVALID_VM_STATE,
3531 tr("Controller '%s' does not support hot-plugging"),
3532 aName.c_str());
3533
3534 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3535 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3536 fHotplug = true;
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 hot-plugging 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 hot-plugging"),
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 hot-plugging"),
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 hot-plugging 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 hot-plugging 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 /* silently ignore attempts to modify the hot-plug status of USB devices */
4402 if (ctrlType == StorageControllerType_USB)
4403 return S_OK;
4404
4405 i_setModified(IsModified_Storage);
4406 mMediumAttachments.backup();
4407
4408 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4409
4410 if (pAttach->i_getType() == DeviceType_Floppy)
4411 return setError(E_INVALIDARG,
4412 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"),
4413 aDevice, aControllerPort, aName.c_str());
4414 pAttach->i_updateHotPluggable(!!aHotPluggable);
4415
4416 return S_OK;
4417}
4418
4419HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4420 LONG aDevice)
4421{
4422 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4423 aName.c_str(), aControllerPort, aDevice));
4424
4425 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4426}
4427
4428HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4429 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4430{
4431 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4432 aName.c_str(), aControllerPort, aDevice));
4433
4434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4435
4436 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4437 if (FAILED(rc)) return rc;
4438
4439 if (Global::IsOnlineOrTransient(mData->mMachineState))
4440 return setError(VBOX_E_INVALID_VM_STATE,
4441 tr("Invalid machine state: %s"),
4442 Global::stringifyMachineState(mData->mMachineState));
4443
4444 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4445 aName,
4446 aControllerPort,
4447 aDevice);
4448 if (!pAttach)
4449 return setError(VBOX_E_OBJECT_NOT_FOUND,
4450 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4451 aDevice, aControllerPort, aName.c_str());
4452
4453
4454 i_setModified(IsModified_Storage);
4455 mMediumAttachments.backup();
4456
4457 IBandwidthGroup *iB = aBandwidthGroup;
4458 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4459 if (aBandwidthGroup && group.isNull())
4460 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4461
4462 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4463
4464 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4465 if (strBandwidthGroupOld.isNotEmpty())
4466 {
4467 /* Get the bandwidth group object and release it - this must not fail. */
4468 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4469 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4470 Assert(SUCCEEDED(rc));
4471
4472 pBandwidthGroupOld->i_release();
4473 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4474 }
4475
4476 if (!group.isNull())
4477 {
4478 group->i_reference();
4479 pAttach->i_updateBandwidthGroup(group->i_getName());
4480 }
4481
4482 return S_OK;
4483}
4484
4485HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4486 LONG aControllerPort,
4487 LONG aDevice,
4488 DeviceType_T aType)
4489{
4490 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4491 aName.c_str(), aControllerPort, aDevice, aType));
4492
4493 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4494}
4495
4496
4497HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4498 LONG aControllerPort,
4499 LONG aDevice,
4500 BOOL aForce)
4501{
4502 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4503 aName.c_str(), aControllerPort, aForce));
4504
4505 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4506}
4507
4508HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4509 LONG aControllerPort,
4510 LONG aDevice,
4511 const ComPtr<IMedium> &aMedium,
4512 BOOL aForce)
4513{
4514 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4515 aName.c_str(), aControllerPort, aDevice, aForce));
4516
4517 // request the host lock first, since might be calling Host methods for getting host drives;
4518 // next, protect the media tree all the while we're in here, as well as our member variables
4519 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4520 this->lockHandle(),
4521 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4522
4523 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4524 if (FAILED(hrc)) return hrc;
4525
4526 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4527 aName,
4528 aControllerPort,
4529 aDevice);
4530 if (pAttach.isNull())
4531 return setError(VBOX_E_OBJECT_NOT_FOUND,
4532 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4533 aDevice, aControllerPort, aName.c_str());
4534
4535 /* Remember previously mounted medium. The medium before taking the
4536 * backup is not necessarily the same thing. */
4537 ComObjPtr<Medium> oldmedium;
4538 oldmedium = pAttach->i_getMedium();
4539
4540 IMedium *iM = aMedium;
4541 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4542 if (aMedium && pMedium.isNull())
4543 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4544
4545 AutoCaller mediumCaller(pMedium);
4546 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4547
4548 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4549 if (pMedium)
4550 {
4551 DeviceType_T mediumType = pAttach->i_getType();
4552 switch (mediumType)
4553 {
4554 case DeviceType_DVD:
4555 case DeviceType_Floppy:
4556 break;
4557
4558 default:
4559 return setError(VBOX_E_INVALID_OBJECT_STATE,
4560 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4561 aControllerPort,
4562 aDevice,
4563 aName.c_str());
4564 }
4565 }
4566
4567 i_setModified(IsModified_Storage);
4568 mMediumAttachments.backup();
4569
4570 {
4571 // The backup operation makes the pAttach reference point to the
4572 // old settings. Re-get the correct reference.
4573 pAttach = i_findAttachment(*mMediumAttachments.data(),
4574 aName,
4575 aControllerPort,
4576 aDevice);
4577 if (!oldmedium.isNull())
4578 oldmedium->i_removeBackReference(mData->mUuid);
4579 if (!pMedium.isNull())
4580 {
4581 pMedium->i_addBackReference(mData->mUuid);
4582
4583 mediumLock.release();
4584 multiLock.release();
4585 i_addMediumToRegistry(pMedium);
4586 multiLock.acquire();
4587 mediumLock.acquire();
4588 }
4589
4590 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4591 pAttach->i_updateMedium(pMedium);
4592 }
4593
4594 i_setModified(IsModified_Storage);
4595
4596 mediumLock.release();
4597 multiLock.release();
4598 HRESULT rc = i_onMediumChange(pAttach, aForce);
4599 multiLock.acquire();
4600 mediumLock.acquire();
4601
4602 /* On error roll back this change only. */
4603 if (FAILED(rc))
4604 {
4605 if (!pMedium.isNull())
4606 pMedium->i_removeBackReference(mData->mUuid);
4607 pAttach = i_findAttachment(*mMediumAttachments.data(),
4608 aName,
4609 aControllerPort,
4610 aDevice);
4611 /* If the attachment is gone in the meantime, bail out. */
4612 if (pAttach.isNull())
4613 return rc;
4614 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4615 if (!oldmedium.isNull())
4616 oldmedium->i_addBackReference(mData->mUuid);
4617 pAttach->i_updateMedium(oldmedium);
4618 }
4619
4620 mediumLock.release();
4621 multiLock.release();
4622
4623 /* Save modified registries, but skip this machine as it's the caller's
4624 * job to save its settings like all other settings changes. */
4625 mParent->i_unmarkRegistryModified(i_getId());
4626 mParent->i_saveModifiedRegistries();
4627
4628 return rc;
4629}
4630HRESULT Machine::getMedium(const com::Utf8Str &aName,
4631 LONG aControllerPort,
4632 LONG aDevice,
4633 ComPtr<IMedium> &aMedium)
4634{
4635 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4636 aName.c_str(), aControllerPort, aDevice));
4637
4638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4639
4640 aMedium = NULL;
4641
4642 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4643 aName,
4644 aControllerPort,
4645 aDevice);
4646 if (pAttach.isNull())
4647 return setError(VBOX_E_OBJECT_NOT_FOUND,
4648 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4649 aDevice, aControllerPort, aName.c_str());
4650
4651 aMedium = pAttach->i_getMedium();
4652
4653 return S_OK;
4654}
4655
4656HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4657{
4658 if (aSlot < RT_ELEMENTS(mSerialPorts))
4659 {
4660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4661 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4662 return S_OK;
4663 }
4664 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4665}
4666
4667HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4668{
4669 if (aSlot < RT_ELEMENTS(mParallelPorts))
4670 {
4671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4672 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4673 return S_OK;
4674 }
4675 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4676}
4677
4678
4679HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4680{
4681 /* Do not assert if slot is out of range, just return the advertised
4682 status. testdriver/vbox.py triggers this in logVmInfo. */
4683 if (aSlot >= mNetworkAdapters.size())
4684 return setError(E_INVALIDARG,
4685 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4686 aSlot, mNetworkAdapters.size());
4687
4688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4689
4690 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4691
4692 return S_OK;
4693}
4694
4695HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4696{
4697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4698
4699 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4700 size_t i = 0;
4701 for (settings::StringsMap::const_iterator
4702 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4703 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4704 ++it, ++i)
4705 aKeys[i] = it->first;
4706
4707 return S_OK;
4708}
4709
4710 /**
4711 * @note Locks this object for reading.
4712 */
4713HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4714 com::Utf8Str &aValue)
4715{
4716 /* start with nothing found */
4717 aValue = "";
4718
4719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4720
4721 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4722 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4723 // found:
4724 aValue = it->second; // source is a Utf8Str
4725
4726 /* return the result to caller (may be empty) */
4727 return S_OK;
4728}
4729
4730 /**
4731 * @note Locks mParent for writing + this object for writing.
4732 */
4733HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4734{
4735 /* Because control characters in aKey have caused problems in the settings
4736 * they are rejected unless the key should be deleted. */
4737 if (!aValue.isEmpty())
4738 {
4739 for (size_t i = 0; i < aKey.length(); ++i)
4740 {
4741 char ch = aKey[i];
4742 if (RTLocCIsCntrl(ch))
4743 return E_INVALIDARG;
4744 }
4745 }
4746
4747 Utf8Str strOldValue; // empty
4748
4749 // locking note: we only hold the read lock briefly to look up the old value,
4750 // then release it and call the onExtraCanChange callbacks. There is a small
4751 // chance of a race insofar as the callback might be called twice if two callers
4752 // change the same key at the same time, but that's a much better solution
4753 // than the deadlock we had here before. The actual changing of the extradata
4754 // is then performed under the write lock and race-free.
4755
4756 // look up the old value first; if nothing has changed then we need not do anything
4757 {
4758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4759
4760 // For snapshots don't even think about allowing changes, extradata
4761 // is global for a machine, so there is nothing snapshot specific.
4762 if (i_isSnapshotMachine())
4763 return setError(VBOX_E_INVALID_VM_STATE,
4764 tr("Cannot set extradata for a snapshot"));
4765
4766 // check if the right IMachine instance is used
4767 if (mData->mRegistered && !i_isSessionMachine())
4768 return setError(VBOX_E_INVALID_VM_STATE,
4769 tr("Cannot set extradata for an immutable machine"));
4770
4771 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4772 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4773 strOldValue = it->second;
4774 }
4775
4776 bool fChanged;
4777 if ((fChanged = (strOldValue != aValue)))
4778 {
4779 // ask for permission from all listeners outside the locks;
4780 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4781 // lock to copy the list of callbacks to invoke
4782 Bstr bstrError;
4783 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4784 {
4785 const char *sep = bstrError.isEmpty() ? "" : ": ";
4786 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4787 return setError(E_ACCESSDENIED,
4788 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4789 aKey.c_str(),
4790 aValue.c_str(),
4791 sep,
4792 bstrError.raw());
4793 }
4794
4795 // data is changing and change not vetoed: then write it out under the lock
4796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4797
4798 if (aValue.isEmpty())
4799 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4800 else
4801 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4802 // creates a new key if needed
4803
4804 bool fNeedsGlobalSaveSettings = false;
4805 // This saving of settings is tricky: there is no "old state" for the
4806 // extradata items at all (unlike all other settings), so the old/new
4807 // settings comparison would give a wrong result!
4808 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4809
4810 if (fNeedsGlobalSaveSettings)
4811 {
4812 // save the global settings; for that we should hold only the VirtualBox lock
4813 alock.release();
4814 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4815 mParent->i_saveSettings();
4816 }
4817 }
4818
4819 // fire notification outside the lock
4820 if (fChanged)
4821 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4822
4823 return S_OK;
4824}
4825
4826HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4827{
4828 aProgress = NULL;
4829 NOREF(aSettingsFilePath);
4830 ReturnComNotImplemented();
4831}
4832
4833HRESULT Machine::saveSettings()
4834{
4835 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4836
4837 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4838 if (FAILED(rc)) return rc;
4839
4840 /* the settings file path may never be null */
4841 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4842
4843 /* save all VM data excluding snapshots */
4844 bool fNeedsGlobalSaveSettings = false;
4845 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4846 mlock.release();
4847
4848 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4849 {
4850 // save the global settings; for that we should hold only the VirtualBox lock
4851 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4852 rc = mParent->i_saveSettings();
4853 }
4854
4855 return rc;
4856}
4857
4858
4859HRESULT Machine::discardSettings()
4860{
4861 /*
4862 * We need to take the machine list lock here as well as the machine one
4863 * or we'll get into trouble should any media stuff require rolling back.
4864 *
4865 * Details:
4866 *
4867 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4869 * 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]
4870 * 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
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4872 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4873 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4874 * 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
4875 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4876 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4877 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4878 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4879 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4880 * 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]
4881 * 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] (*)
4882 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4883 * 0:005> k
4884 * # Child-SP RetAddr Call Site
4885 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4886 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4887 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4888 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4889 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4890 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4891 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4892 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4893 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4894 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4895 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4896 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4897 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4898 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4899 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4900 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4901 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4902 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4903 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4904 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4905 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4906 *
4907 */
4908 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4910
4911 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4912 if (FAILED(rc)) return rc;
4913
4914 /*
4915 * during this rollback, the session will be notified if data has
4916 * been actually changed
4917 */
4918 i_rollback(true /* aNotify */);
4919
4920 return S_OK;
4921}
4922
4923/** @note Locks objects! */
4924HRESULT Machine::unregister(AutoCaller &autoCaller,
4925 CleanupMode_T aCleanupMode,
4926 std::vector<ComPtr<IMedium> > &aMedia)
4927{
4928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4929
4930 Guid id(i_getId());
4931
4932 if (mData->mSession.mState != SessionState_Unlocked)
4933 return setError(VBOX_E_INVALID_OBJECT_STATE,
4934 tr("Cannot unregister the machine '%s' while it is locked"),
4935 mUserData->s.strName.c_str());
4936
4937 // wait for state dependents to drop to zero
4938 i_ensureNoStateDependencies(alock);
4939
4940 if (!mData->mAccessible)
4941 {
4942 // inaccessible machines can only be unregistered; uninitialize ourselves
4943 // here because currently there may be no unregistered that are inaccessible
4944 // (this state combination is not supported). Note releasing the caller and
4945 // leaving the lock before calling uninit()
4946 alock.release();
4947 autoCaller.release();
4948
4949 uninit();
4950
4951 mParent->i_unregisterMachine(this, id);
4952 // calls VirtualBox::i_saveSettings()
4953
4954 return S_OK;
4955 }
4956
4957 HRESULT rc = S_OK;
4958 mData->llFilesToDelete.clear();
4959
4960 if (!mSSData->strStateFilePath.isEmpty())
4961 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4962
4963 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4964 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4965 mData->llFilesToDelete.push_back(strNVRAMFile);
4966
4967 // This list collects the medium objects from all medium attachments
4968 // which we will detach from the machine and its snapshots, in a specific
4969 // order which allows for closing all media without getting "media in use"
4970 // errors, simply by going through the list from the front to the back:
4971 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4972 // and must be closed before the parent media from the snapshots, or closing the parents
4973 // will fail because they still have children);
4974 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4975 // the root ("first") snapshot of the machine.
4976 MediaList llMedia;
4977
4978 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4979 && mMediumAttachments->size()
4980 )
4981 {
4982 // we have media attachments: detach them all and add the Medium objects to our list
4983 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4984 }
4985
4986 if (mData->mFirstSnapshot)
4987 {
4988 // add the media from the medium attachments of the snapshots to llMedia
4989 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4990 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4991 // into the children first
4992
4993 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4994 MachineState_T oldState = mData->mMachineState;
4995 mData->mMachineState = MachineState_DeletingSnapshot;
4996
4997 // make a copy of the first snapshot reference so the refcount does not
4998 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4999 // (would hang due to the AutoCaller voodoo)
5000 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5001
5002 // GO!
5003 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5004
5005 mData->mMachineState = oldState;
5006 }
5007
5008 if (FAILED(rc))
5009 {
5010 i_rollbackMedia();
5011 return rc;
5012 }
5013
5014 // commit all the media changes made above
5015 i_commitMedia();
5016
5017 mData->mRegistered = false;
5018
5019 // machine lock no longer needed
5020 alock.release();
5021
5022 /* Make sure that the settings of the current VM are not saved, because
5023 * they are rather crippled at this point to meet the cleanup expectations
5024 * and there's no point destroying the VM config on disk just because. */
5025 mParent->i_unmarkRegistryModified(id);
5026
5027 // return media to caller
5028 aMedia.resize(llMedia.size());
5029 size_t i = 0;
5030 for (MediaList::const_iterator
5031 it = llMedia.begin();
5032 it != llMedia.end();
5033 ++it, ++i)
5034 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5035
5036 mParent->i_unregisterMachine(this, id);
5037 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5038
5039 return S_OK;
5040}
5041
5042/**
5043 * Task record for deleting a machine config.
5044 */
5045class Machine::DeleteConfigTask
5046 : public Machine::Task
5047{
5048public:
5049 DeleteConfigTask(Machine *m,
5050 Progress *p,
5051 const Utf8Str &t,
5052 const RTCList<ComPtr<IMedium> > &llMediums,
5053 const StringsList &llFilesToDelete)
5054 : Task(m, p, t),
5055 m_llMediums(llMediums),
5056 m_llFilesToDelete(llFilesToDelete)
5057 {}
5058
5059private:
5060 void handler()
5061 {
5062 try
5063 {
5064 m_pMachine->i_deleteConfigHandler(*this);
5065 }
5066 catch (...)
5067 {
5068 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5069 }
5070 }
5071
5072 RTCList<ComPtr<IMedium> > m_llMediums;
5073 StringsList m_llFilesToDelete;
5074
5075 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5076};
5077
5078/**
5079 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5080 * SessionMachine::taskHandler().
5081 *
5082 * @note Locks this object for writing.
5083 *
5084 * @param task
5085 * @return
5086 */
5087void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5088{
5089 LogFlowThisFuncEnter();
5090
5091 AutoCaller autoCaller(this);
5092 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5093 if (FAILED(autoCaller.rc()))
5094 {
5095 /* we might have been uninitialized because the session was accidentally
5096 * closed by the client, so don't assert */
5097 HRESULT rc = setError(E_FAIL,
5098 tr("The session has been accidentally closed"));
5099 task.m_pProgress->i_notifyComplete(rc);
5100 LogFlowThisFuncLeave();
5101 return;
5102 }
5103
5104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5105
5106 HRESULT rc = S_OK;
5107
5108 try
5109 {
5110 ULONG uLogHistoryCount = 3;
5111 ComPtr<ISystemProperties> systemProperties;
5112 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5113 if (FAILED(rc)) throw rc;
5114
5115 if (!systemProperties.isNull())
5116 {
5117 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5118 if (FAILED(rc)) throw rc;
5119 }
5120
5121 MachineState_T oldState = mData->mMachineState;
5122 i_setMachineState(MachineState_SettingUp);
5123 alock.release();
5124 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5125 {
5126 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5127 {
5128 AutoCaller mac(pMedium);
5129 if (FAILED(mac.rc())) throw mac.rc();
5130 Utf8Str strLocation = pMedium->i_getLocationFull();
5131 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5132 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5133 if (FAILED(rc)) throw rc;
5134 }
5135 if (pMedium->i_isMediumFormatFile())
5136 {
5137 ComPtr<IProgress> pProgress2;
5138 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5139 if (FAILED(rc)) throw rc;
5140 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5141 if (FAILED(rc)) throw rc;
5142 }
5143
5144 /* Close the medium, deliberately without checking the return
5145 * code, and without leaving any trace in the error info, as
5146 * a failure here is a very minor issue, which shouldn't happen
5147 * as above we even managed to delete the medium. */
5148 {
5149 ErrorInfoKeeper eik;
5150 pMedium->Close();
5151 }
5152 }
5153 i_setMachineState(oldState);
5154 alock.acquire();
5155
5156 // delete the files pushed on the task list by Machine::Delete()
5157 // (this includes saved states of the machine and snapshots and
5158 // medium storage files from the IMedium list passed in, and the
5159 // machine XML file)
5160 for (StringsList::const_iterator
5161 it = task.m_llFilesToDelete.begin();
5162 it != task.m_llFilesToDelete.end();
5163 ++it)
5164 {
5165 const Utf8Str &strFile = *it;
5166 LogFunc(("Deleting file %s\n", strFile.c_str()));
5167 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5168 if (FAILED(rc)) throw rc;
5169
5170 int vrc = RTFileDelete(strFile.c_str());
5171 if (RT_FAILURE(vrc))
5172 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5173 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5174 }
5175
5176 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5177 if (FAILED(rc)) throw rc;
5178
5179 /* delete the settings only when the file actually exists */
5180 if (mData->pMachineConfigFile->fileExists())
5181 {
5182 /* Delete any backup or uncommitted XML files. Ignore failures.
5183 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5184 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5185 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5186 RTFileDelete(otherXml.c_str());
5187 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5188 RTFileDelete(otherXml.c_str());
5189
5190 /* delete the Logs folder, nothing important should be left
5191 * there (we don't check for errors because the user might have
5192 * some private files there that we don't want to delete) */
5193 Utf8Str logFolder;
5194 getLogFolder(logFolder);
5195 Assert(logFolder.length());
5196 if (RTDirExists(logFolder.c_str()))
5197 {
5198 /* Delete all VBox.log[.N] files from the Logs folder
5199 * (this must be in sync with the rotation logic in
5200 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5201 * files that may have been created by the GUI. */
5202 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5203 RTFileDelete(log.c_str());
5204 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5205 RTFileDelete(log.c_str());
5206 for (ULONG i = uLogHistoryCount; i > 0; i--)
5207 {
5208 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5209 RTFileDelete(log.c_str());
5210 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5211 RTFileDelete(log.c_str());
5212 }
5213 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5214 RTFileDelete(log.c_str());
5215#if defined(RT_OS_WINDOWS)
5216 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5217 RTFileDelete(log.c_str());
5218 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5219 RTFileDelete(log.c_str());
5220#endif
5221
5222 RTDirRemove(logFolder.c_str());
5223 }
5224
5225 /* delete the Snapshots folder, nothing important should be left
5226 * there (we don't check for errors because the user might have
5227 * some private files there that we don't want to delete) */
5228 Utf8Str strFullSnapshotFolder;
5229 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5230 Assert(!strFullSnapshotFolder.isEmpty());
5231 if (RTDirExists(strFullSnapshotFolder.c_str()))
5232 RTDirRemove(strFullSnapshotFolder.c_str());
5233
5234 // delete the directory that contains the settings file, but only
5235 // if it matches the VM name
5236 Utf8Str settingsDir;
5237 if (i_isInOwnDir(&settingsDir))
5238 RTDirRemove(settingsDir.c_str());
5239 }
5240
5241 alock.release();
5242
5243 mParent->i_saveModifiedRegistries();
5244 }
5245 catch (HRESULT aRC) { rc = aRC; }
5246
5247 task.m_pProgress->i_notifyComplete(rc);
5248
5249 LogFlowThisFuncLeave();
5250}
5251
5252HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5253{
5254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5255
5256 HRESULT rc = i_checkStateDependency(MutableStateDep);
5257 if (FAILED(rc)) return rc;
5258
5259 if (mData->mRegistered)
5260 return setError(VBOX_E_INVALID_VM_STATE,
5261 tr("Cannot delete settings of a registered machine"));
5262
5263 // collect files to delete
5264 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5265 // machine config file
5266 if (mData->pMachineConfigFile->fileExists())
5267 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5268 // backup of machine config file
5269 Utf8Str strTmp(mData->m_strConfigFileFull);
5270 strTmp.append("-prev");
5271 if (RTFileExists(strTmp.c_str()))
5272 llFilesToDelete.push_back(strTmp);
5273
5274 RTCList<ComPtr<IMedium> > llMediums;
5275 for (size_t i = 0; i < aMedia.size(); ++i)
5276 {
5277 IMedium *pIMedium(aMedia[i]);
5278 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5279 if (pMedium.isNull())
5280 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5281 SafeArray<BSTR> ids;
5282 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5283 if (FAILED(rc)) return rc;
5284 /* At this point the medium should not have any back references
5285 * anymore. If it has it is attached to another VM and *must* not
5286 * deleted. */
5287 if (ids.size() < 1)
5288 llMediums.append(pMedium);
5289 }
5290
5291 ComObjPtr<Progress> pProgress;
5292 pProgress.createObject();
5293 rc = pProgress->init(i_getVirtualBox(),
5294 static_cast<IMachine*>(this) /* aInitiator */,
5295 tr("Deleting files"),
5296 true /* fCancellable */,
5297 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5298 tr("Collecting file inventory"));
5299 if (FAILED(rc))
5300 return rc;
5301
5302 /* create and start the task on a separate thread (note that it will not
5303 * start working until we release alock) */
5304 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5305 rc = pTask->createThread();
5306 pTask = NULL;
5307 if (FAILED(rc))
5308 return rc;
5309
5310 pProgress.queryInterfaceTo(aProgress.asOutParam());
5311
5312 LogFlowFuncLeave();
5313
5314 return S_OK;
5315}
5316
5317HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5318{
5319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5320
5321 ComObjPtr<Snapshot> pSnapshot;
5322 HRESULT rc;
5323
5324 if (aNameOrId.isEmpty())
5325 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5326 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5327 else
5328 {
5329 Guid uuid(aNameOrId);
5330 if (uuid.isValid())
5331 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5332 else
5333 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5334 }
5335 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5336
5337 return rc;
5338}
5339
5340HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5341 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5342{
5343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5344
5345 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5346 if (FAILED(rc)) return rc;
5347
5348 ComObjPtr<SharedFolder> sharedFolder;
5349 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5350 if (SUCCEEDED(rc))
5351 return setError(VBOX_E_OBJECT_IN_USE,
5352 tr("Shared folder named '%s' already exists"),
5353 aName.c_str());
5354
5355 sharedFolder.createObject();
5356 rc = sharedFolder->init(i_getMachine(),
5357 aName,
5358 aHostPath,
5359 !!aWritable,
5360 !!aAutomount,
5361 aAutoMountPoint,
5362 true /* fFailOnError */);
5363 if (FAILED(rc)) return rc;
5364
5365 i_setModified(IsModified_SharedFolders);
5366 mHWData.backup();
5367 mHWData->mSharedFolders.push_back(sharedFolder);
5368
5369 /* inform the direct session if any */
5370 alock.release();
5371 i_onSharedFolderChange();
5372
5373 return S_OK;
5374}
5375
5376HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5377{
5378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5381 if (FAILED(rc)) return rc;
5382
5383 ComObjPtr<SharedFolder> sharedFolder;
5384 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5385 if (FAILED(rc)) return rc;
5386
5387 i_setModified(IsModified_SharedFolders);
5388 mHWData.backup();
5389 mHWData->mSharedFolders.remove(sharedFolder);
5390
5391 /* inform the direct session if any */
5392 alock.release();
5393 i_onSharedFolderChange();
5394
5395 return S_OK;
5396}
5397
5398HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5399{
5400 /* start with No */
5401 *aCanShow = FALSE;
5402
5403 ComPtr<IInternalSessionControl> directControl;
5404 {
5405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5406
5407 if (mData->mSession.mState != SessionState_Locked)
5408 return setError(VBOX_E_INVALID_VM_STATE,
5409 tr("Machine is not locked for session (session state: %s)"),
5410 Global::stringifySessionState(mData->mSession.mState));
5411
5412 if (mData->mSession.mLockType == LockType_VM)
5413 directControl = mData->mSession.mDirectControl;
5414 }
5415
5416 /* ignore calls made after #OnSessionEnd() is called */
5417 if (!directControl)
5418 return S_OK;
5419
5420 LONG64 dummy;
5421 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5422}
5423
5424HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5425{
5426 ComPtr<IInternalSessionControl> directControl;
5427 {
5428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5429
5430 if (mData->mSession.mState != SessionState_Locked)
5431 return setError(E_FAIL,
5432 tr("Machine is not locked for session (session state: %s)"),
5433 Global::stringifySessionState(mData->mSession.mState));
5434
5435 if (mData->mSession.mLockType == LockType_VM)
5436 directControl = mData->mSession.mDirectControl;
5437 }
5438
5439 /* ignore calls made after #OnSessionEnd() is called */
5440 if (!directControl)
5441 return S_OK;
5442
5443 BOOL dummy;
5444 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5445}
5446
5447#ifdef VBOX_WITH_GUEST_PROPS
5448/**
5449 * Look up a guest property in VBoxSVC's internal structures.
5450 */
5451HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5452 com::Utf8Str &aValue,
5453 LONG64 *aTimestamp,
5454 com::Utf8Str &aFlags) const
5455{
5456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5457
5458 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5459 if (it != mHWData->mGuestProperties.end())
5460 {
5461 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5462 aValue = it->second.strValue;
5463 *aTimestamp = it->second.mTimestamp;
5464 GuestPropWriteFlags(it->second.mFlags, szFlags);
5465 aFlags = Utf8Str(szFlags);
5466 }
5467
5468 return S_OK;
5469}
5470
5471/**
5472 * Query the VM that a guest property belongs to for the property.
5473 * @returns E_ACCESSDENIED if the VM process is not available or not
5474 * currently handling queries and the lookup should then be done in
5475 * VBoxSVC.
5476 */
5477HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5478 com::Utf8Str &aValue,
5479 LONG64 *aTimestamp,
5480 com::Utf8Str &aFlags) const
5481{
5482 HRESULT rc = S_OK;
5483 Bstr bstrValue;
5484 Bstr bstrFlags;
5485
5486 ComPtr<IInternalSessionControl> directControl;
5487 {
5488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5489 if (mData->mSession.mLockType == LockType_VM)
5490 directControl = mData->mSession.mDirectControl;
5491 }
5492
5493 /* ignore calls made after #OnSessionEnd() is called */
5494 if (!directControl)
5495 rc = E_ACCESSDENIED;
5496 else
5497 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5498 0 /* accessMode */,
5499 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5500
5501 aValue = bstrValue;
5502 aFlags = bstrFlags;
5503
5504 return rc;
5505}
5506#endif // VBOX_WITH_GUEST_PROPS
5507
5508HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5509 com::Utf8Str &aValue,
5510 LONG64 *aTimestamp,
5511 com::Utf8Str &aFlags)
5512{
5513#ifndef VBOX_WITH_GUEST_PROPS
5514 ReturnComNotImplemented();
5515#else // VBOX_WITH_GUEST_PROPS
5516
5517 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5518
5519 if (rc == E_ACCESSDENIED)
5520 /* The VM is not running or the service is not (yet) accessible */
5521 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5522 return rc;
5523#endif // VBOX_WITH_GUEST_PROPS
5524}
5525
5526HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5527{
5528 LONG64 dummyTimestamp;
5529 com::Utf8Str dummyFlags;
5530 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5531 return rc;
5532
5533}
5534HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5535{
5536 com::Utf8Str dummyFlags;
5537 com::Utf8Str dummyValue;
5538 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5539 return rc;
5540}
5541
5542#ifdef VBOX_WITH_GUEST_PROPS
5543/**
5544 * Set a guest property in VBoxSVC's internal structures.
5545 */
5546HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5547 const com::Utf8Str &aFlags, bool fDelete)
5548{
5549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5550 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5551 if (FAILED(rc)) return rc;
5552
5553 try
5554 {
5555 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5556 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5557 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5558
5559 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5560 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5561
5562 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5563 if (it == mHWData->mGuestProperties.end())
5564 {
5565 if (!fDelete)
5566 {
5567 i_setModified(IsModified_MachineData);
5568 mHWData.backupEx();
5569
5570 RTTIMESPEC time;
5571 HWData::GuestProperty prop;
5572 prop.strValue = aValue;
5573 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5574 prop.mFlags = fFlags;
5575 mHWData->mGuestProperties[aName] = prop;
5576 }
5577 }
5578 else
5579 {
5580 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5581 {
5582 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5583 }
5584 else
5585 {
5586 i_setModified(IsModified_MachineData);
5587 mHWData.backupEx();
5588
5589 /* The backupEx() operation invalidates our iterator,
5590 * so get a new one. */
5591 it = mHWData->mGuestProperties.find(aName);
5592 Assert(it != mHWData->mGuestProperties.end());
5593
5594 if (!fDelete)
5595 {
5596 RTTIMESPEC time;
5597 it->second.strValue = aValue;
5598 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5599 it->second.mFlags = fFlags;
5600 }
5601 else
5602 mHWData->mGuestProperties.erase(it);
5603 }
5604 }
5605
5606 if (SUCCEEDED(rc))
5607 {
5608 alock.release();
5609
5610 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5611 }
5612 }
5613 catch (std::bad_alloc &)
5614 {
5615 rc = E_OUTOFMEMORY;
5616 }
5617
5618 return rc;
5619}
5620
5621/**
5622 * Set a property on the VM that that property belongs to.
5623 * @returns E_ACCESSDENIED if the VM process is not available or not
5624 * currently handling queries and the setting should then be done in
5625 * VBoxSVC.
5626 */
5627HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5628 const com::Utf8Str &aFlags, bool fDelete)
5629{
5630 HRESULT rc;
5631
5632 try
5633 {
5634 ComPtr<IInternalSessionControl> directControl;
5635 {
5636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5637 if (mData->mSession.mLockType == LockType_VM)
5638 directControl = mData->mSession.mDirectControl;
5639 }
5640
5641 Bstr dummy1; /* will not be changed (setter) */
5642 Bstr dummy2; /* will not be changed (setter) */
5643 LONG64 dummy64;
5644 if (!directControl)
5645 rc = E_ACCESSDENIED;
5646 else
5647 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5648 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5649 fDelete ? 2 : 1 /* accessMode */,
5650 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5651 }
5652 catch (std::bad_alloc &)
5653 {
5654 rc = E_OUTOFMEMORY;
5655 }
5656
5657 return rc;
5658}
5659#endif // VBOX_WITH_GUEST_PROPS
5660
5661HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5662 const com::Utf8Str &aFlags)
5663{
5664#ifndef VBOX_WITH_GUEST_PROPS
5665 ReturnComNotImplemented();
5666#else // VBOX_WITH_GUEST_PROPS
5667 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5668 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5669
5670 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5671 if (rc == E_ACCESSDENIED)
5672 /* The VM is not running or the service is not (yet) accessible */
5673 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5674 return rc;
5675#endif // VBOX_WITH_GUEST_PROPS
5676}
5677
5678HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5679{
5680 return setGuestProperty(aProperty, aValue, "");
5681}
5682
5683HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5684{
5685#ifndef VBOX_WITH_GUEST_PROPS
5686 ReturnComNotImplemented();
5687#else // VBOX_WITH_GUEST_PROPS
5688 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5689 if (rc == E_ACCESSDENIED)
5690 /* The VM is not running or the service is not (yet) accessible */
5691 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5692 return rc;
5693#endif // VBOX_WITH_GUEST_PROPS
5694}
5695
5696#ifdef VBOX_WITH_GUEST_PROPS
5697/**
5698 * Enumerate the guest properties in VBoxSVC's internal structures.
5699 */
5700HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5701 std::vector<com::Utf8Str> &aNames,
5702 std::vector<com::Utf8Str> &aValues,
5703 std::vector<LONG64> &aTimestamps,
5704 std::vector<com::Utf8Str> &aFlags)
5705{
5706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5707 Utf8Str strPatterns(aPatterns);
5708
5709 /*
5710 * Look for matching patterns and build up a list.
5711 */
5712 HWData::GuestPropertyMap propMap;
5713 for (HWData::GuestPropertyMap::const_iterator
5714 it = mHWData->mGuestProperties.begin();
5715 it != mHWData->mGuestProperties.end();
5716 ++it)
5717 {
5718 if ( strPatterns.isEmpty()
5719 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5720 RTSTR_MAX,
5721 it->first.c_str(),
5722 RTSTR_MAX,
5723 NULL)
5724 )
5725 propMap.insert(*it);
5726 }
5727
5728 alock.release();
5729
5730 /*
5731 * And build up the arrays for returning the property information.
5732 */
5733 size_t cEntries = propMap.size();
5734
5735 aNames.resize(cEntries);
5736 aValues.resize(cEntries);
5737 aTimestamps.resize(cEntries);
5738 aFlags.resize(cEntries);
5739
5740 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5741 size_t i = 0;
5742 for (HWData::GuestPropertyMap::const_iterator
5743 it = propMap.begin();
5744 it != propMap.end();
5745 ++it, ++i)
5746 {
5747 aNames[i] = it->first;
5748 aValues[i] = it->second.strValue;
5749 aTimestamps[i] = it->second.mTimestamp;
5750 GuestPropWriteFlags(it->second.mFlags, szFlags);
5751 aFlags[i] = Utf8Str(szFlags);
5752
5753 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5754 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5755 }
5756
5757 return S_OK;
5758}
5759
5760/**
5761 * Enumerate the properties managed by a VM.
5762 * @returns E_ACCESSDENIED if the VM process is not available or not
5763 * currently handling queries and the setting should then be done in
5764 * VBoxSVC.
5765 */
5766HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5767 std::vector<com::Utf8Str> &aNames,
5768 std::vector<com::Utf8Str> &aValues,
5769 std::vector<LONG64> &aTimestamps,
5770 std::vector<com::Utf8Str> &aFlags)
5771{
5772 HRESULT rc;
5773 ComPtr<IInternalSessionControl> directControl;
5774 {
5775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5776 if (mData->mSession.mLockType == LockType_VM)
5777 directControl = mData->mSession.mDirectControl;
5778 }
5779
5780 com::SafeArray<BSTR> bNames;
5781 com::SafeArray<BSTR> bValues;
5782 com::SafeArray<LONG64> bTimestamps;
5783 com::SafeArray<BSTR> bFlags;
5784
5785 if (!directControl)
5786 rc = E_ACCESSDENIED;
5787 else
5788 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5789 ComSafeArrayAsOutParam(bNames),
5790 ComSafeArrayAsOutParam(bValues),
5791 ComSafeArrayAsOutParam(bTimestamps),
5792 ComSafeArrayAsOutParam(bFlags));
5793 size_t i;
5794 aNames.resize(bNames.size());
5795 for (i = 0; i < bNames.size(); ++i)
5796 aNames[i] = Utf8Str(bNames[i]);
5797 aValues.resize(bValues.size());
5798 for (i = 0; i < bValues.size(); ++i)
5799 aValues[i] = Utf8Str(bValues[i]);
5800 aTimestamps.resize(bTimestamps.size());
5801 for (i = 0; i < bTimestamps.size(); ++i)
5802 aTimestamps[i] = bTimestamps[i];
5803 aFlags.resize(bFlags.size());
5804 for (i = 0; i < bFlags.size(); ++i)
5805 aFlags[i] = Utf8Str(bFlags[i]);
5806
5807 return rc;
5808}
5809#endif // VBOX_WITH_GUEST_PROPS
5810HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5811 std::vector<com::Utf8Str> &aNames,
5812 std::vector<com::Utf8Str> &aValues,
5813 std::vector<LONG64> &aTimestamps,
5814 std::vector<com::Utf8Str> &aFlags)
5815{
5816#ifndef VBOX_WITH_GUEST_PROPS
5817 ReturnComNotImplemented();
5818#else // VBOX_WITH_GUEST_PROPS
5819
5820 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5821
5822 if (rc == E_ACCESSDENIED)
5823 /* The VM is not running or the service is not (yet) accessible */
5824 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5825 return rc;
5826#endif // VBOX_WITH_GUEST_PROPS
5827}
5828
5829HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5830 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5831{
5832 MediumAttachmentList atts;
5833
5834 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5835 if (FAILED(rc)) return rc;
5836
5837 aMediumAttachments.resize(atts.size());
5838 size_t i = 0;
5839 for (MediumAttachmentList::const_iterator
5840 it = atts.begin();
5841 it != atts.end();
5842 ++it, ++i)
5843 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5844
5845 return S_OK;
5846}
5847
5848HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5849 LONG aControllerPort,
5850 LONG aDevice,
5851 ComPtr<IMediumAttachment> &aAttachment)
5852{
5853 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5854 aName.c_str(), aControllerPort, aDevice));
5855
5856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5857
5858 aAttachment = NULL;
5859
5860 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5861 aName,
5862 aControllerPort,
5863 aDevice);
5864 if (pAttach.isNull())
5865 return setError(VBOX_E_OBJECT_NOT_FOUND,
5866 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5867 aDevice, aControllerPort, aName.c_str());
5868
5869 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5870
5871 return S_OK;
5872}
5873
5874
5875HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5876 StorageBus_T aConnectionType,
5877 ComPtr<IStorageController> &aController)
5878{
5879 if ( (aConnectionType <= StorageBus_Null)
5880 || (aConnectionType > StorageBus_VirtioSCSI))
5881 return setError(E_INVALIDARG,
5882 tr("Invalid connection type: %d"),
5883 aConnectionType);
5884
5885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5886
5887 HRESULT rc = i_checkStateDependency(MutableStateDep);
5888 if (FAILED(rc)) return rc;
5889
5890 /* try to find one with the name first. */
5891 ComObjPtr<StorageController> ctrl;
5892
5893 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5894 if (SUCCEEDED(rc))
5895 return setError(VBOX_E_OBJECT_IN_USE,
5896 tr("Storage controller named '%s' already exists"),
5897 aName.c_str());
5898
5899 ctrl.createObject();
5900
5901 /* get a new instance number for the storage controller */
5902 ULONG ulInstance = 0;
5903 bool fBootable = true;
5904 for (StorageControllerList::const_iterator
5905 it = mStorageControllers->begin();
5906 it != mStorageControllers->end();
5907 ++it)
5908 {
5909 if ((*it)->i_getStorageBus() == aConnectionType)
5910 {
5911 ULONG ulCurInst = (*it)->i_getInstance();
5912
5913 if (ulCurInst >= ulInstance)
5914 ulInstance = ulCurInst + 1;
5915
5916 /* Only one controller of each type can be marked as bootable. */
5917 if ((*it)->i_getBootable())
5918 fBootable = false;
5919 }
5920 }
5921
5922 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5923 if (FAILED(rc)) return rc;
5924
5925 i_setModified(IsModified_Storage);
5926 mStorageControllers.backup();
5927 mStorageControllers->push_back(ctrl);
5928
5929 ctrl.queryInterfaceTo(aController.asOutParam());
5930
5931 /* inform the direct session if any */
5932 alock.release();
5933 i_onStorageControllerChange(i_getId(), aName);
5934
5935 return S_OK;
5936}
5937
5938HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5939 ComPtr<IStorageController> &aStorageController)
5940{
5941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5942
5943 ComObjPtr<StorageController> ctrl;
5944
5945 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5946 if (SUCCEEDED(rc))
5947 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5948
5949 return rc;
5950}
5951
5952HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5953 ULONG aInstance,
5954 ComPtr<IStorageController> &aStorageController)
5955{
5956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5957
5958 for (StorageControllerList::const_iterator
5959 it = mStorageControllers->begin();
5960 it != mStorageControllers->end();
5961 ++it)
5962 {
5963 if ( (*it)->i_getStorageBus() == aConnectionType
5964 && (*it)->i_getInstance() == aInstance)
5965 {
5966 (*it).queryInterfaceTo(aStorageController.asOutParam());
5967 return S_OK;
5968 }
5969 }
5970
5971 return setError(VBOX_E_OBJECT_NOT_FOUND,
5972 tr("Could not find a storage controller with instance number '%lu'"),
5973 aInstance);
5974}
5975
5976HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5977{
5978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5979
5980 HRESULT rc = i_checkStateDependency(MutableStateDep);
5981 if (FAILED(rc)) return rc;
5982
5983 ComObjPtr<StorageController> ctrl;
5984
5985 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5986 if (SUCCEEDED(rc))
5987 {
5988 /* Ensure that only one controller of each type is marked as bootable. */
5989 if (aBootable == TRUE)
5990 {
5991 for (StorageControllerList::const_iterator
5992 it = mStorageControllers->begin();
5993 it != mStorageControllers->end();
5994 ++it)
5995 {
5996 ComObjPtr<StorageController> aCtrl = (*it);
5997
5998 if ( (aCtrl->i_getName() != aName)
5999 && aCtrl->i_getBootable() == TRUE
6000 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6001 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6002 {
6003 aCtrl->i_setBootable(FALSE);
6004 break;
6005 }
6006 }
6007 }
6008
6009 if (SUCCEEDED(rc))
6010 {
6011 ctrl->i_setBootable(aBootable);
6012 i_setModified(IsModified_Storage);
6013 }
6014 }
6015
6016 if (SUCCEEDED(rc))
6017 {
6018 /* inform the direct session if any */
6019 alock.release();
6020 i_onStorageControllerChange(i_getId(), aName);
6021 }
6022
6023 return rc;
6024}
6025
6026HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6027{
6028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6029
6030 HRESULT rc = i_checkStateDependency(MutableStateDep);
6031 if (FAILED(rc)) return rc;
6032
6033 ComObjPtr<StorageController> ctrl;
6034 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6035 if (FAILED(rc)) return rc;
6036
6037 MediumAttachmentList llDetachedAttachments;
6038 {
6039 /* find all attached devices to the appropriate storage controller and detach them all */
6040 // make a temporary list because detachDevice invalidates iterators into
6041 // mMediumAttachments
6042 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6043
6044 for (MediumAttachmentList::const_iterator
6045 it = llAttachments2.begin();
6046 it != llAttachments2.end();
6047 ++it)
6048 {
6049 MediumAttachment *pAttachTemp = *it;
6050
6051 AutoCaller localAutoCaller(pAttachTemp);
6052 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6053
6054 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6055
6056 if (pAttachTemp->i_getControllerName() == aName)
6057 {
6058 llDetachedAttachments.push_back(pAttachTemp);
6059 rc = i_detachDevice(pAttachTemp, alock, NULL);
6060 if (FAILED(rc)) return rc;
6061 }
6062 }
6063 }
6064
6065 /* send event about detached devices before removing parent controller */
6066 for (MediumAttachmentList::const_iterator
6067 it = llDetachedAttachments.begin();
6068 it != llDetachedAttachments.end();
6069 ++it)
6070 {
6071 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6072 }
6073
6074 /* We can remove it now. */
6075 i_setModified(IsModified_Storage);
6076 mStorageControllers.backup();
6077
6078 ctrl->i_unshare();
6079
6080 mStorageControllers->remove(ctrl);
6081
6082 /* inform the direct session if any */
6083 alock.release();
6084 i_onStorageControllerChange(i_getId(), aName);
6085
6086 return S_OK;
6087}
6088
6089HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6090 ComPtr<IUSBController> &aController)
6091{
6092 if ( (aType <= USBControllerType_Null)
6093 || (aType >= USBControllerType_Last))
6094 return setError(E_INVALIDARG,
6095 tr("Invalid USB controller type: %d"),
6096 aType);
6097
6098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6099
6100 HRESULT rc = i_checkStateDependency(MutableStateDep);
6101 if (FAILED(rc)) return rc;
6102
6103 /* try to find one with the same type first. */
6104 ComObjPtr<USBController> ctrl;
6105
6106 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6107 if (SUCCEEDED(rc))
6108 return setError(VBOX_E_OBJECT_IN_USE,
6109 tr("USB controller named '%s' already exists"),
6110 aName.c_str());
6111
6112 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6113 ULONG maxInstances;
6114 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6115 if (FAILED(rc))
6116 return rc;
6117
6118 ULONG cInstances = i_getUSBControllerCountByType(aType);
6119 if (cInstances >= maxInstances)
6120 return setError(E_INVALIDARG,
6121 tr("Too many USB controllers of this type"));
6122
6123 ctrl.createObject();
6124
6125 rc = ctrl->init(this, aName, aType);
6126 if (FAILED(rc)) return rc;
6127
6128 i_setModified(IsModified_USB);
6129 mUSBControllers.backup();
6130 mUSBControllers->push_back(ctrl);
6131
6132 ctrl.queryInterfaceTo(aController.asOutParam());
6133
6134 /* inform the direct session if any */
6135 alock.release();
6136 i_onUSBControllerChange();
6137
6138 return S_OK;
6139}
6140
6141HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6142{
6143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6144
6145 ComObjPtr<USBController> ctrl;
6146
6147 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6148 if (SUCCEEDED(rc))
6149 ctrl.queryInterfaceTo(aController.asOutParam());
6150
6151 return rc;
6152}
6153
6154HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6155 ULONG *aControllers)
6156{
6157 if ( (aType <= USBControllerType_Null)
6158 || (aType >= USBControllerType_Last))
6159 return setError(E_INVALIDARG,
6160 tr("Invalid USB controller type: %d"),
6161 aType);
6162
6163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6164
6165 ComObjPtr<USBController> ctrl;
6166
6167 *aControllers = i_getUSBControllerCountByType(aType);
6168
6169 return S_OK;
6170}
6171
6172HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6173{
6174
6175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 HRESULT rc = i_checkStateDependency(MutableStateDep);
6178 if (FAILED(rc)) return rc;
6179
6180 ComObjPtr<USBController> ctrl;
6181 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6182 if (FAILED(rc)) return rc;
6183
6184 i_setModified(IsModified_USB);
6185 mUSBControllers.backup();
6186
6187 ctrl->i_unshare();
6188
6189 mUSBControllers->remove(ctrl);
6190
6191 /* inform the direct session if any */
6192 alock.release();
6193 i_onUSBControllerChange();
6194
6195 return S_OK;
6196}
6197
6198HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6199 ULONG *aOriginX,
6200 ULONG *aOriginY,
6201 ULONG *aWidth,
6202 ULONG *aHeight,
6203 BOOL *aEnabled)
6204{
6205 uint32_t u32OriginX= 0;
6206 uint32_t u32OriginY= 0;
6207 uint32_t u32Width = 0;
6208 uint32_t u32Height = 0;
6209 uint16_t u16Flags = 0;
6210
6211 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6212 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6213 if (RT_FAILURE(vrc))
6214 {
6215#ifdef RT_OS_WINDOWS
6216 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6217 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6218 * So just assign fEnable to TRUE again.
6219 * The right fix would be to change GUI API wrappers to make sure that parameters
6220 * are changed only if API succeeds.
6221 */
6222 *aEnabled = TRUE;
6223#endif
6224 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6225 tr("Saved guest size is not available (%Rrc)"),
6226 vrc);
6227 }
6228
6229 *aOriginX = u32OriginX;
6230 *aOriginY = u32OriginY;
6231 *aWidth = u32Width;
6232 *aHeight = u32Height;
6233 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6234
6235 return S_OK;
6236}
6237
6238HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6239 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6240{
6241 if (aScreenId != 0)
6242 return E_NOTIMPL;
6243
6244 if ( aBitmapFormat != BitmapFormat_BGR0
6245 && aBitmapFormat != BitmapFormat_BGRA
6246 && aBitmapFormat != BitmapFormat_RGBA
6247 && aBitmapFormat != BitmapFormat_PNG)
6248 return setError(E_NOTIMPL,
6249 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6250
6251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6252
6253 uint8_t *pu8Data = NULL;
6254 uint32_t cbData = 0;
6255 uint32_t u32Width = 0;
6256 uint32_t u32Height = 0;
6257
6258 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6259
6260 if (RT_FAILURE(vrc))
6261 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6262 tr("Saved thumbnail data is not available (%Rrc)"),
6263 vrc);
6264
6265 HRESULT hr = S_OK;
6266
6267 *aWidth = u32Width;
6268 *aHeight = u32Height;
6269
6270 if (cbData > 0)
6271 {
6272 /* Convert pixels to the format expected by the API caller. */
6273 if (aBitmapFormat == BitmapFormat_BGR0)
6274 {
6275 /* [0] B, [1] G, [2] R, [3] 0. */
6276 aData.resize(cbData);
6277 memcpy(&aData.front(), pu8Data, cbData);
6278 }
6279 else if (aBitmapFormat == BitmapFormat_BGRA)
6280 {
6281 /* [0] B, [1] G, [2] R, [3] A. */
6282 aData.resize(cbData);
6283 for (uint32_t i = 0; i < cbData; i += 4)
6284 {
6285 aData[i] = pu8Data[i];
6286 aData[i + 1] = pu8Data[i + 1];
6287 aData[i + 2] = pu8Data[i + 2];
6288 aData[i + 3] = 0xff;
6289 }
6290 }
6291 else if (aBitmapFormat == BitmapFormat_RGBA)
6292 {
6293 /* [0] R, [1] G, [2] B, [3] A. */
6294 aData.resize(cbData);
6295 for (uint32_t i = 0; i < cbData; i += 4)
6296 {
6297 aData[i] = pu8Data[i + 2];
6298 aData[i + 1] = pu8Data[i + 1];
6299 aData[i + 2] = pu8Data[i];
6300 aData[i + 3] = 0xff;
6301 }
6302 }
6303 else if (aBitmapFormat == BitmapFormat_PNG)
6304 {
6305 uint8_t *pu8PNG = NULL;
6306 uint32_t cbPNG = 0;
6307 uint32_t cxPNG = 0;
6308 uint32_t cyPNG = 0;
6309
6310 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6311
6312 if (RT_SUCCESS(vrc))
6313 {
6314 aData.resize(cbPNG);
6315 if (cbPNG)
6316 memcpy(&aData.front(), pu8PNG, cbPNG);
6317 }
6318 else
6319 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6320 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6321 vrc);
6322
6323 RTMemFree(pu8PNG);
6324 }
6325 }
6326
6327 freeSavedDisplayScreenshot(pu8Data);
6328
6329 return hr;
6330}
6331
6332HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6333 ULONG *aWidth,
6334 ULONG *aHeight,
6335 std::vector<BitmapFormat_T> &aBitmapFormats)
6336{
6337 if (aScreenId != 0)
6338 return E_NOTIMPL;
6339
6340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6341
6342 uint8_t *pu8Data = NULL;
6343 uint32_t cbData = 0;
6344 uint32_t u32Width = 0;
6345 uint32_t u32Height = 0;
6346
6347 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6348
6349 if (RT_FAILURE(vrc))
6350 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6351 tr("Saved screenshot data is not available (%Rrc)"),
6352 vrc);
6353
6354 *aWidth = u32Width;
6355 *aHeight = u32Height;
6356 aBitmapFormats.resize(1);
6357 aBitmapFormats[0] = BitmapFormat_PNG;
6358
6359 freeSavedDisplayScreenshot(pu8Data);
6360
6361 return S_OK;
6362}
6363
6364HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6365 BitmapFormat_T aBitmapFormat,
6366 ULONG *aWidth,
6367 ULONG *aHeight,
6368 std::vector<BYTE> &aData)
6369{
6370 if (aScreenId != 0)
6371 return E_NOTIMPL;
6372
6373 if (aBitmapFormat != BitmapFormat_PNG)
6374 return E_NOTIMPL;
6375
6376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6377
6378 uint8_t *pu8Data = NULL;
6379 uint32_t cbData = 0;
6380 uint32_t u32Width = 0;
6381 uint32_t u32Height = 0;
6382
6383 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6384
6385 if (RT_FAILURE(vrc))
6386 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6387 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6388 vrc);
6389
6390 *aWidth = u32Width;
6391 *aHeight = u32Height;
6392
6393 aData.resize(cbData);
6394 if (cbData)
6395 memcpy(&aData.front(), pu8Data, cbData);
6396
6397 freeSavedDisplayScreenshot(pu8Data);
6398
6399 return S_OK;
6400}
6401
6402HRESULT Machine::hotPlugCPU(ULONG aCpu)
6403{
6404 HRESULT rc = S_OK;
6405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6406
6407 if (!mHWData->mCPUHotPlugEnabled)
6408 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6409
6410 if (aCpu >= mHWData->mCPUCount)
6411 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6412
6413 if (mHWData->mCPUAttached[aCpu])
6414 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6415
6416 rc = i_checkStateDependency(MutableOrRunningStateDep);
6417 if (FAILED(rc)) return rc;
6418
6419 alock.release();
6420 rc = i_onCPUChange(aCpu, false);
6421 alock.acquire();
6422 if (FAILED(rc)) return rc;
6423
6424 i_setModified(IsModified_MachineData);
6425 mHWData.backup();
6426 mHWData->mCPUAttached[aCpu] = true;
6427
6428 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6429 if (Global::IsOnline(mData->mMachineState))
6430 i_saveSettings(NULL, alock);
6431
6432 return S_OK;
6433}
6434
6435HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6436{
6437 HRESULT rc = S_OK;
6438
6439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6440
6441 if (!mHWData->mCPUHotPlugEnabled)
6442 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6443
6444 if (aCpu >= SchemaDefs::MaxCPUCount)
6445 return setError(E_INVALIDARG,
6446 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6447 SchemaDefs::MaxCPUCount);
6448
6449 if (!mHWData->mCPUAttached[aCpu])
6450 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6451
6452 /* CPU 0 can't be detached */
6453 if (aCpu == 0)
6454 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6455
6456 rc = i_checkStateDependency(MutableOrRunningStateDep);
6457 if (FAILED(rc)) return rc;
6458
6459 alock.release();
6460 rc = i_onCPUChange(aCpu, true);
6461 alock.acquire();
6462 if (FAILED(rc)) return rc;
6463
6464 i_setModified(IsModified_MachineData);
6465 mHWData.backup();
6466 mHWData->mCPUAttached[aCpu] = false;
6467
6468 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6469 if (Global::IsOnline(mData->mMachineState))
6470 i_saveSettings(NULL, alock);
6471
6472 return S_OK;
6473}
6474
6475HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6476{
6477 *aAttached = false;
6478
6479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6480
6481 /* If hotplug is enabled the CPU is always enabled. */
6482 if (!mHWData->mCPUHotPlugEnabled)
6483 {
6484 if (aCpu < mHWData->mCPUCount)
6485 *aAttached = true;
6486 }
6487 else
6488 {
6489 if (aCpu < SchemaDefs::MaxCPUCount)
6490 *aAttached = mHWData->mCPUAttached[aCpu];
6491 }
6492
6493 return S_OK;
6494}
6495
6496HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6497{
6498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6499
6500 Utf8Str log = i_getLogFilename(aIdx);
6501 if (!RTFileExists(log.c_str()))
6502 log.setNull();
6503 aFilename = log;
6504
6505 return S_OK;
6506}
6507
6508HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6509{
6510 if (aSize < 0)
6511 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6512
6513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6514
6515 HRESULT rc = S_OK;
6516 Utf8Str log = i_getLogFilename(aIdx);
6517
6518 /* do not unnecessarily hold the lock while doing something which does
6519 * not need the lock and potentially takes a long time. */
6520 alock.release();
6521
6522 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6523 * keeps the SOAP reply size under 1M for the webservice (we're using
6524 * base64 encoded strings for binary data for years now, avoiding the
6525 * expansion of each byte array element to approx. 25 bytes of XML. */
6526 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6527 aData.resize(cbData);
6528
6529 RTFILE LogFile;
6530 int vrc = RTFileOpen(&LogFile, log.c_str(),
6531 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6532 if (RT_SUCCESS(vrc))
6533 {
6534 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6535 if (RT_SUCCESS(vrc))
6536 aData.resize(cbData);
6537 else
6538 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6539 tr("Could not read log file '%s' (%Rrc)"),
6540 log.c_str(), vrc);
6541 RTFileClose(LogFile);
6542 }
6543 else
6544 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6545 tr("Could not open log file '%s' (%Rrc)"),
6546 log.c_str(), vrc);
6547
6548 if (FAILED(rc))
6549 aData.resize(0);
6550
6551 return rc;
6552}
6553
6554
6555/**
6556 * Currently this method doesn't attach device to the running VM,
6557 * just makes sure it's plugged on next VM start.
6558 */
6559HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6560{
6561 // lock scope
6562 {
6563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6564
6565 HRESULT rc = i_checkStateDependency(MutableStateDep);
6566 if (FAILED(rc)) return rc;
6567
6568 ChipsetType_T aChipset = ChipsetType_PIIX3;
6569 COMGETTER(ChipsetType)(&aChipset);
6570
6571 if (aChipset != ChipsetType_ICH9)
6572 {
6573 return setError(E_INVALIDARG,
6574 tr("Host PCI attachment only supported with ICH9 chipset"));
6575 }
6576
6577 // check if device with this host PCI address already attached
6578 for (HWData::PCIDeviceAssignmentList::const_iterator
6579 it = mHWData->mPCIDeviceAssignments.begin();
6580 it != mHWData->mPCIDeviceAssignments.end();
6581 ++it)
6582 {
6583 LONG iHostAddress = -1;
6584 ComPtr<PCIDeviceAttachment> pAttach;
6585 pAttach = *it;
6586 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6587 if (iHostAddress == aHostAddress)
6588 return setError(E_INVALIDARG,
6589 tr("Device with host PCI address already attached to this VM"));
6590 }
6591
6592 ComObjPtr<PCIDeviceAttachment> pda;
6593 char name[32];
6594
6595 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6596 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6597 pda.createObject();
6598 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6599 i_setModified(IsModified_MachineData);
6600 mHWData.backup();
6601 mHWData->mPCIDeviceAssignments.push_back(pda);
6602 }
6603
6604 return S_OK;
6605}
6606
6607/**
6608 * Currently this method doesn't detach device from the running VM,
6609 * just makes sure it's not plugged on next VM start.
6610 */
6611HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6612{
6613 ComObjPtr<PCIDeviceAttachment> pAttach;
6614 bool fRemoved = false;
6615 HRESULT rc;
6616
6617 // lock scope
6618 {
6619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6620
6621 rc = i_checkStateDependency(MutableStateDep);
6622 if (FAILED(rc)) return rc;
6623
6624 for (HWData::PCIDeviceAssignmentList::const_iterator
6625 it = mHWData->mPCIDeviceAssignments.begin();
6626 it != mHWData->mPCIDeviceAssignments.end();
6627 ++it)
6628 {
6629 LONG iHostAddress = -1;
6630 pAttach = *it;
6631 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6632 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6633 {
6634 i_setModified(IsModified_MachineData);
6635 mHWData.backup();
6636 mHWData->mPCIDeviceAssignments.remove(pAttach);
6637 fRemoved = true;
6638 break;
6639 }
6640 }
6641 }
6642
6643
6644 /* Fire event outside of the lock */
6645 if (fRemoved)
6646 {
6647 Assert(!pAttach.isNull());
6648 ComPtr<IEventSource> es;
6649 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6650 Assert(SUCCEEDED(rc));
6651 Bstr mid;
6652 rc = this->COMGETTER(Id)(mid.asOutParam());
6653 Assert(SUCCEEDED(rc));
6654 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6655 }
6656
6657 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6658 tr("No host PCI device %08x attached"),
6659 aHostAddress
6660 );
6661}
6662
6663HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6664{
6665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6666
6667 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6668 size_t i = 0;
6669 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6670 it = mHWData->mPCIDeviceAssignments.begin();
6671 it != mHWData->mPCIDeviceAssignments.end();
6672 ++it, ++i)
6673 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6674
6675 return S_OK;
6676}
6677
6678HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6679{
6680 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6681
6682 return S_OK;
6683}
6684
6685HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6686{
6687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6688
6689 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6690
6691 return S_OK;
6692}
6693
6694HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6695{
6696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6697 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6698 if (SUCCEEDED(hrc))
6699 {
6700 hrc = mHWData.backupEx();
6701 if (SUCCEEDED(hrc))
6702 {
6703 i_setModified(IsModified_MachineData);
6704 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6705 }
6706 }
6707 return hrc;
6708}
6709
6710HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6711{
6712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6713 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6714 return S_OK;
6715}
6716
6717HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6718{
6719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6720 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6721 if (SUCCEEDED(hrc))
6722 {
6723 hrc = mHWData.backupEx();
6724 if (SUCCEEDED(hrc))
6725 {
6726 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6727 if (SUCCEEDED(hrc))
6728 i_setModified(IsModified_MachineData);
6729 }
6730 }
6731 return hrc;
6732}
6733
6734HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6735{
6736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6737
6738 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6739
6740 return S_OK;
6741}
6742
6743HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6744{
6745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6746 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6747 if (SUCCEEDED(hrc))
6748 {
6749 hrc = mHWData.backupEx();
6750 if (SUCCEEDED(hrc))
6751 {
6752 i_setModified(IsModified_MachineData);
6753 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6754 }
6755 }
6756 return hrc;
6757}
6758
6759HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6760{
6761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6762
6763 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6764
6765 return S_OK;
6766}
6767
6768HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6769{
6770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6771
6772 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6773 if ( SUCCEEDED(hrc)
6774 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6775 {
6776 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6777 int vrc;
6778
6779 if (aAutostartEnabled)
6780 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6781 else
6782 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6783
6784 if (RT_SUCCESS(vrc))
6785 {
6786 hrc = mHWData.backupEx();
6787 if (SUCCEEDED(hrc))
6788 {
6789 i_setModified(IsModified_MachineData);
6790 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6791 }
6792 }
6793 else if (vrc == VERR_NOT_SUPPORTED)
6794 hrc = setError(VBOX_E_NOT_SUPPORTED,
6795 tr("The VM autostart feature is not supported on this platform"));
6796 else if (vrc == VERR_PATH_NOT_FOUND)
6797 hrc = setError(E_FAIL,
6798 tr("The path to the autostart database is not set"));
6799 else
6800 hrc = setError(E_UNEXPECTED,
6801 aAutostartEnabled ?
6802 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6803 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6804 mUserData->s.strName.c_str(), vrc);
6805 }
6806 return hrc;
6807}
6808
6809HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6810{
6811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6812
6813 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6814
6815 return S_OK;
6816}
6817
6818HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6819{
6820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6821 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6822 if (SUCCEEDED(hrc))
6823 {
6824 hrc = mHWData.backupEx();
6825 if (SUCCEEDED(hrc))
6826 {
6827 i_setModified(IsModified_MachineData);
6828 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6829 }
6830 }
6831 return hrc;
6832}
6833
6834HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6835{
6836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6839
6840 return S_OK;
6841}
6842
6843HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6844{
6845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6846 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6847 if ( SUCCEEDED(hrc)
6848 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6849 {
6850 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6851 int vrc;
6852
6853 if (aAutostopType != AutostopType_Disabled)
6854 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6855 else
6856 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6857
6858 if (RT_SUCCESS(vrc))
6859 {
6860 hrc = mHWData.backupEx();
6861 if (SUCCEEDED(hrc))
6862 {
6863 i_setModified(IsModified_MachineData);
6864 mHWData->mAutostart.enmAutostopType = aAutostopType;
6865 }
6866 }
6867 else if (vrc == VERR_NOT_SUPPORTED)
6868 hrc = setError(VBOX_E_NOT_SUPPORTED,
6869 tr("The VM autostop feature is not supported on this platform"));
6870 else if (vrc == VERR_PATH_NOT_FOUND)
6871 hrc = setError(E_FAIL,
6872 tr("The path to the autostart database is not set"));
6873 else
6874 hrc = setError(E_UNEXPECTED,
6875 aAutostopType != AutostopType_Disabled ?
6876 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6877 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6878 mUserData->s.strName.c_str(), vrc);
6879 }
6880 return hrc;
6881}
6882
6883HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6884{
6885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6886
6887 aDefaultFrontend = mHWData->mDefaultFrontend;
6888
6889 return S_OK;
6890}
6891
6892HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6893{
6894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6895 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6896 if (SUCCEEDED(hrc))
6897 {
6898 hrc = mHWData.backupEx();
6899 if (SUCCEEDED(hrc))
6900 {
6901 i_setModified(IsModified_MachineData);
6902 mHWData->mDefaultFrontend = aDefaultFrontend;
6903 }
6904 }
6905 return hrc;
6906}
6907
6908HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6909{
6910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6911 size_t cbIcon = mUserData->s.ovIcon.size();
6912 aIcon.resize(cbIcon);
6913 if (cbIcon)
6914 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6915 return S_OK;
6916}
6917
6918HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6919{
6920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6921 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6922 if (SUCCEEDED(hrc))
6923 {
6924 i_setModified(IsModified_MachineData);
6925 mUserData.backup();
6926 size_t cbIcon = aIcon.size();
6927 mUserData->s.ovIcon.resize(cbIcon);
6928 if (cbIcon)
6929 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6930 }
6931 return hrc;
6932}
6933
6934HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6935{
6936#ifdef VBOX_WITH_USB
6937 *aUSBProxyAvailable = true;
6938#else
6939 *aUSBProxyAvailable = false;
6940#endif
6941 return S_OK;
6942}
6943
6944HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6945{
6946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6947
6948 *aVMProcessPriority = mUserData->s.enmVMPriority;
6949
6950 return S_OK;
6951}
6952
6953HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6954{
6955 RT_NOREF(aVMProcessPriority);
6956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6957 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6958 if (SUCCEEDED(hrc))
6959 {
6960 hrc = mUserData.backupEx();
6961 if (SUCCEEDED(hrc))
6962 {
6963 i_setModified(IsModified_MachineData);
6964 mUserData->s.enmVMPriority = aVMProcessPriority;
6965 }
6966 }
6967 alock.release();
6968 if (SUCCEEDED(hrc))
6969 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6970 return hrc;
6971}
6972
6973HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6974 ComPtr<IProgress> &aProgress)
6975{
6976 ComObjPtr<Progress> pP;
6977 Progress *ppP = pP;
6978 IProgress *iP = static_cast<IProgress *>(ppP);
6979 IProgress **pProgress = &iP;
6980
6981 IMachine *pTarget = aTarget;
6982
6983 /* Convert the options. */
6984 RTCList<CloneOptions_T> optList;
6985 if (aOptions.size())
6986 for (size_t i = 0; i < aOptions.size(); ++i)
6987 optList.append(aOptions[i]);
6988
6989 if (optList.contains(CloneOptions_Link))
6990 {
6991 if (!i_isSnapshotMachine())
6992 return setError(E_INVALIDARG,
6993 tr("Linked clone can only be created from a snapshot"));
6994 if (aMode != CloneMode_MachineState)
6995 return setError(E_INVALIDARG,
6996 tr("Linked clone can only be created for a single machine state"));
6997 }
6998 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6999
7000 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7001
7002 HRESULT rc = pWorker->start(pProgress);
7003
7004 pP = static_cast<Progress *>(*pProgress);
7005 pP.queryInterfaceTo(aProgress.asOutParam());
7006
7007 return rc;
7008
7009}
7010
7011HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7012 const com::Utf8Str &aType,
7013 ComPtr<IProgress> &aProgress)
7014{
7015 LogFlowThisFuncEnter();
7016
7017 ComObjPtr<Progress> ptrProgress;
7018 HRESULT hrc = ptrProgress.createObject();
7019 if (SUCCEEDED(hrc))
7020 {
7021 com::Utf8Str strDefaultPath;
7022 if (aTargetPath.isEmpty())
7023 i_calculateFullPath(".", strDefaultPath);
7024
7025 /* Initialize our worker task */
7026 MachineMoveVM *pTask = NULL;
7027 try
7028 {
7029 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7030 }
7031 catch (std::bad_alloc &)
7032 {
7033 return E_OUTOFMEMORY;
7034 }
7035
7036 hrc = pTask->init();//no exceptions are thrown
7037
7038 if (SUCCEEDED(hrc))
7039 {
7040 hrc = pTask->createThread();
7041 pTask = NULL; /* Consumed by createThread(). */
7042 if (SUCCEEDED(hrc))
7043 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7044 else
7045 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7046 }
7047 else
7048 delete pTask;
7049 }
7050
7051 LogFlowThisFuncLeave();
7052 return hrc;
7053
7054}
7055
7056HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7057{
7058 NOREF(aProgress);
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060
7061 // This check should always fail.
7062 HRESULT rc = i_checkStateDependency(MutableStateDep);
7063 if (FAILED(rc)) return rc;
7064
7065 AssertFailedReturn(E_NOTIMPL);
7066}
7067
7068HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7069{
7070 NOREF(aSavedStateFile);
7071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7072
7073 // This check should always fail.
7074 HRESULT rc = i_checkStateDependency(MutableStateDep);
7075 if (FAILED(rc)) return rc;
7076
7077 AssertFailedReturn(E_NOTIMPL);
7078}
7079
7080HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7081{
7082 NOREF(aFRemoveFile);
7083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7084
7085 // This check should always fail.
7086 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7087 if (FAILED(rc)) return rc;
7088
7089 AssertFailedReturn(E_NOTIMPL);
7090}
7091
7092// public methods for internal purposes
7093/////////////////////////////////////////////////////////////////////////////
7094
7095/**
7096 * Adds the given IsModified_* flag to the dirty flags of the machine.
7097 * This must be called either during i_loadSettings or under the machine write lock.
7098 * @param fl Flag
7099 * @param fAllowStateModification If state modifications are allowed.
7100 */
7101void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7102{
7103 mData->flModifications |= fl;
7104 if (fAllowStateModification && i_isStateModificationAllowed())
7105 mData->mCurrentStateModified = true;
7106}
7107
7108/**
7109 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7110 * care of the write locking.
7111 *
7112 * @param fModification The flag to add.
7113 * @param fAllowStateModification If state modifications are allowed.
7114 */
7115void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7116{
7117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7118 i_setModified(fModification, fAllowStateModification);
7119}
7120
7121/**
7122 * Saves the registry entry of this machine to the given configuration node.
7123 *
7124 * @param data Machine registry data.
7125 *
7126 * @note locks this object for reading.
7127 */
7128HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7129{
7130 AutoLimitedCaller autoCaller(this);
7131 AssertComRCReturnRC(autoCaller.rc());
7132
7133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7134
7135 data.uuid = mData->mUuid;
7136 data.strSettingsFile = mData->m_strConfigFile;
7137
7138 return S_OK;
7139}
7140
7141/**
7142 * Calculates the absolute path of the given path taking the directory of the
7143 * machine settings file as the current directory.
7144 *
7145 * @param strPath Path to calculate the absolute path for.
7146 * @param aResult Where to put the result (used only on success, can be the
7147 * same Utf8Str instance as passed in @a aPath).
7148 * @return IPRT result.
7149 *
7150 * @note Locks this object for reading.
7151 */
7152int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7153{
7154 AutoCaller autoCaller(this);
7155 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7156
7157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7158
7159 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7160
7161 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7162
7163 strSettingsDir.stripFilename();
7164 char szFolder[RTPATH_MAX];
7165 size_t cbFolder = sizeof(szFolder);
7166 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7167 if (RT_SUCCESS(vrc))
7168 aResult = szFolder;
7169
7170 return vrc;
7171}
7172
7173/**
7174 * Copies strSource to strTarget, making it relative to the machine folder
7175 * if it is a subdirectory thereof, or simply copying it otherwise.
7176 *
7177 * @param strSource Path to evaluate and copy.
7178 * @param strTarget Buffer to receive target path.
7179 *
7180 * @note Locks this object for reading.
7181 */
7182void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7183 Utf8Str &strTarget)
7184{
7185 AutoCaller autoCaller(this);
7186 AssertComRCReturn(autoCaller.rc(), (void)0);
7187
7188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7189
7190 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7191 // use strTarget as a temporary buffer to hold the machine settings dir
7192 strTarget = mData->m_strConfigFileFull;
7193 strTarget.stripFilename();
7194 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7195 {
7196 // is relative: then append what's left
7197 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7198 // for empty paths (only possible for subdirs) use "." to avoid
7199 // triggering default settings for not present config attributes.
7200 if (strTarget.isEmpty())
7201 strTarget = ".";
7202 }
7203 else
7204 // is not relative: then overwrite
7205 strTarget = strSource;
7206}
7207
7208/**
7209 * Returns the full path to the machine's log folder in the
7210 * \a aLogFolder argument.
7211 */
7212void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7213{
7214 AutoCaller autoCaller(this);
7215 AssertComRCReturnVoid(autoCaller.rc());
7216
7217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7218
7219 char szTmp[RTPATH_MAX];
7220 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7221 if (RT_SUCCESS(vrc))
7222 {
7223 if (szTmp[0] && !mUserData.isNull())
7224 {
7225 char szTmp2[RTPATH_MAX];
7226 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7227 if (RT_SUCCESS(vrc))
7228 aLogFolder.printf("%s%c%s",
7229 szTmp2,
7230 RTPATH_DELIMITER,
7231 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7232 }
7233 else
7234 vrc = VERR_PATH_IS_RELATIVE;
7235 }
7236
7237 if (RT_FAILURE(vrc))
7238 {
7239 // fallback if VBOX_USER_LOGHOME is not set or invalid
7240 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7241 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7242 aLogFolder.append(RTPATH_DELIMITER);
7243 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7244 }
7245}
7246
7247/**
7248 * Returns the full path to the machine's log file for an given index.
7249 */
7250Utf8Str Machine::i_getLogFilename(ULONG idx)
7251{
7252 Utf8Str logFolder;
7253 getLogFolder(logFolder);
7254 Assert(logFolder.length());
7255
7256 Utf8Str log;
7257 if (idx == 0)
7258 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7259#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7260 else if (idx == 1)
7261 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7262 else
7263 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7264#else
7265 else
7266 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7267#endif
7268 return log;
7269}
7270
7271/**
7272 * Returns the full path to the machine's hardened log file.
7273 */
7274Utf8Str Machine::i_getHardeningLogFilename(void)
7275{
7276 Utf8Str strFilename;
7277 getLogFolder(strFilename);
7278 Assert(strFilename.length());
7279 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7280 return strFilename;
7281}
7282
7283/**
7284 * Returns the default NVRAM filename based on the location of the VM config.
7285 * Note that this is a relative path.
7286 */
7287Utf8Str Machine::i_getDefaultNVRAMFilename()
7288{
7289 AutoCaller autoCaller(this);
7290 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7291
7292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7293
7294 if (i_isSnapshotMachine())
7295 return Utf8Str::Empty;
7296
7297 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7298 strNVRAMFilePath.stripPath();
7299 strNVRAMFilePath.stripSuffix();
7300 strNVRAMFilePath += ".nvram";
7301
7302 return strNVRAMFilePath;
7303}
7304
7305/**
7306 * Returns the NVRAM filename for a new snapshot. This intentionally works
7307 * similarly to the saved state file naming. Note that this is usually
7308 * a relative path, unless the snapshot folder is absolute.
7309 */
7310Utf8Str Machine::i_getSnapshotNVRAMFilename()
7311{
7312 AutoCaller autoCaller(this);
7313 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7314
7315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7316
7317 RTTIMESPEC ts;
7318 RTTimeNow(&ts);
7319 RTTIME time;
7320 RTTimeExplode(&time, &ts);
7321
7322 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7323 strNVRAMFilePath += RTPATH_DELIMITER;
7324 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7325 time.i32Year, time.u8Month, time.u8MonthDay,
7326 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7327
7328 return strNVRAMFilePath;
7329}
7330
7331/**
7332 * Returns the version of the settings file.
7333 */
7334SettingsVersion_T Machine::i_getSettingsVersion(void)
7335{
7336 AutoCaller autoCaller(this);
7337 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7338
7339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7340
7341 return mData->pMachineConfigFile->getSettingsVersion();
7342}
7343
7344/**
7345 * Composes a unique saved state filename based on the current system time. The filename is
7346 * granular to the second so this will work so long as no more than one snapshot is taken on
7347 * a machine per second.
7348 *
7349 * Before version 4.1, we used this formula for saved state files:
7350 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7351 * which no longer works because saved state files can now be shared between the saved state of the
7352 * "saved" machine and an online snapshot, and the following would cause problems:
7353 * 1) save machine
7354 * 2) create online snapshot from that machine state --> reusing saved state file
7355 * 3) save machine again --> filename would be reused, breaking the online snapshot
7356 *
7357 * So instead we now use a timestamp.
7358 *
7359 * @param strStateFilePath
7360 */
7361
7362void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7363{
7364 AutoCaller autoCaller(this);
7365 AssertComRCReturnVoid(autoCaller.rc());
7366
7367 {
7368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7369 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7370 }
7371
7372 RTTIMESPEC ts;
7373 RTTimeNow(&ts);
7374 RTTIME time;
7375 RTTimeExplode(&time, &ts);
7376
7377 strStateFilePath += RTPATH_DELIMITER;
7378 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7379 time.i32Year, time.u8Month, time.u8MonthDay,
7380 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7381}
7382
7383/**
7384 * Returns whether at least one USB controller is present for the VM.
7385 */
7386bool Machine::i_isUSBControllerPresent()
7387{
7388 AutoCaller autoCaller(this);
7389 AssertComRCReturn(autoCaller.rc(), false);
7390
7391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7392
7393 return (mUSBControllers->size() > 0);
7394}
7395
7396
7397/**
7398 * @note Locks this object for writing, calls the client process
7399 * (inside the lock).
7400 */
7401HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7402 const Utf8Str &strFrontend,
7403 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7404 ProgressProxy *aProgress)
7405{
7406 LogFlowThisFuncEnter();
7407
7408 AssertReturn(aControl, E_FAIL);
7409 AssertReturn(aProgress, E_FAIL);
7410 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7411
7412 AutoCaller autoCaller(this);
7413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7414
7415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7416
7417 if (!mData->mRegistered)
7418 return setError(E_UNEXPECTED,
7419 tr("The machine '%s' is not registered"),
7420 mUserData->s.strName.c_str());
7421
7422 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7423
7424 /* The process started when launching a VM with separate UI/VM processes is always
7425 * the UI process, i.e. needs special handling as it won't claim the session. */
7426 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7427
7428 if (fSeparate)
7429 {
7430 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7431 return setError(VBOX_E_INVALID_OBJECT_STATE,
7432 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7433 mUserData->s.strName.c_str());
7434 }
7435 else
7436 {
7437 if ( mData->mSession.mState == SessionState_Locked
7438 || mData->mSession.mState == SessionState_Spawning
7439 || mData->mSession.mState == SessionState_Unlocking)
7440 return setError(VBOX_E_INVALID_OBJECT_STATE,
7441 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7442 mUserData->s.strName.c_str());
7443
7444 /* may not be busy */
7445 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7446 }
7447
7448 /* Hardening logging */
7449#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7450 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7451 {
7452 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7453 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7454 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7455 {
7456 Utf8Str strStartupLogDir = strHardeningLogFile;
7457 strStartupLogDir.stripFilename();
7458 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7459 file without stripping the file. */
7460 }
7461 strSupHardeningLogArg.append(strHardeningLogFile);
7462
7463 /* Remove legacy log filename to avoid confusion. */
7464 Utf8Str strOldStartupLogFile;
7465 getLogFolder(strOldStartupLogFile);
7466 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7467 RTFileDelete(strOldStartupLogFile.c_str());
7468 }
7469#else
7470 Utf8Str strSupHardeningLogArg;
7471#endif
7472
7473 Utf8Str strAppOverride;
7474#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7475 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7476#endif
7477
7478 bool fUseVBoxSDS = false;
7479 Utf8Str strCanonicalName;
7480 if (false)
7481 { }
7482#ifdef VBOX_WITH_QTGUI
7483 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7484 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7485 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7486 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7487 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7488 {
7489 strCanonicalName = "GUI/Qt";
7490 fUseVBoxSDS = true;
7491 }
7492#endif
7493#ifdef VBOX_WITH_VBOXSDL
7494 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7495 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7496 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7497 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7498 {
7499 strCanonicalName = "GUI/SDL";
7500 fUseVBoxSDS = true;
7501 }
7502#endif
7503#ifdef VBOX_WITH_HEADLESS
7504 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7505 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7506 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7507 {
7508 strCanonicalName = "headless";
7509 }
7510#endif
7511 else
7512 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7513
7514 Utf8Str idStr = mData->mUuid.toString();
7515 Utf8Str const &strMachineName = mUserData->s.strName;
7516 RTPROCESS pid = NIL_RTPROCESS;
7517
7518#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7519 RT_NOREF(fUseVBoxSDS);
7520#else
7521 DWORD idCallerSession = ~(DWORD)0;
7522 if (fUseVBoxSDS)
7523 {
7524 /*
7525 * The VBoxSDS should be used for process launching the VM with
7526 * GUI only if the caller and the VBoxSDS are in different Windows
7527 * sessions and the caller in the interactive one.
7528 */
7529 fUseVBoxSDS = false;
7530
7531 /* Get windows session of the current process. The process token used
7532 due to several reasons:
7533 1. The token is absent for the current thread except someone set it
7534 for us.
7535 2. Needs to get the id of the session where the process is started.
7536 We only need to do this once, though. */
7537 static DWORD s_idCurrentSession = ~(DWORD)0;
7538 DWORD idCurrentSession = s_idCurrentSession;
7539 if (idCurrentSession == ~(DWORD)0)
7540 {
7541 HANDLE hCurrentProcessToken = NULL;
7542 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7543 {
7544 DWORD cbIgn = 0;
7545 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7546 s_idCurrentSession = idCurrentSession;
7547 else
7548 {
7549 idCurrentSession = ~(DWORD)0;
7550 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7551 }
7552 CloseHandle(hCurrentProcessToken);
7553 }
7554 else
7555 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7556 }
7557
7558 /* get the caller's session */
7559 HRESULT hrc = CoImpersonateClient();
7560 if (SUCCEEDED(hrc))
7561 {
7562 HANDLE hCallerThreadToken;
7563 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7564 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7565 &hCallerThreadToken))
7566 {
7567 SetLastError(NO_ERROR);
7568 DWORD cbIgn = 0;
7569 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7570 {
7571 /* Only need to use SDS if the session ID differs: */
7572 if (idCurrentSession != idCallerSession)
7573 {
7574 fUseVBoxSDS = false;
7575
7576 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7577 DWORD cbTokenGroups = 0;
7578 PTOKEN_GROUPS pTokenGroups = NULL;
7579 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7580 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7581 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7582 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7583 {
7584 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7585 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7586 PSID pInteractiveSid = NULL;
7587 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7588 {
7589 /* Iterate over the groups looking for the interactive SID: */
7590 fUseVBoxSDS = false;
7591 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7592 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7593 {
7594 fUseVBoxSDS = true;
7595 break;
7596 }
7597 FreeSid(pInteractiveSid);
7598 }
7599 }
7600 else
7601 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7602 RTMemTmpFree(pTokenGroups);
7603 }
7604 }
7605 else
7606 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7607 CloseHandle(hCallerThreadToken);
7608 }
7609 else
7610 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7611 CoRevertToSelf();
7612 }
7613 else
7614 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7615 }
7616 if (fUseVBoxSDS)
7617 {
7618 /* connect to VBoxSDS */
7619 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7620 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7621 if (FAILED(rc))
7622 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7623 strMachineName.c_str());
7624
7625 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7626 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7627 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7628 service to access the files. */
7629 rc = CoSetProxyBlanket(pVBoxSDS,
7630 RPC_C_AUTHN_DEFAULT,
7631 RPC_C_AUTHZ_DEFAULT,
7632 COLE_DEFAULT_PRINCIPAL,
7633 RPC_C_AUTHN_LEVEL_DEFAULT,
7634 RPC_C_IMP_LEVEL_IMPERSONATE,
7635 NULL,
7636 EOAC_DEFAULT);
7637 if (FAILED(rc))
7638 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7639
7640 size_t const cEnvVars = aEnvironmentChanges.size();
7641 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7642 for (size_t i = 0; i < cEnvVars; i++)
7643 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7644
7645 ULONG uPid = 0;
7646 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7647 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7648 idCallerSession, &uPid);
7649 if (FAILED(rc))
7650 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7651 pid = (RTPROCESS)uPid;
7652 }
7653 else
7654#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7655 {
7656 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7657 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7658 if (RT_FAILURE(vrc))
7659 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7660 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7661 }
7662
7663 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7664 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7665
7666 if (!fSeparate)
7667 {
7668 /*
7669 * Note that we don't release the lock here before calling the client,
7670 * because it doesn't need to call us back if called with a NULL argument.
7671 * Releasing the lock here is dangerous because we didn't prepare the
7672 * launch data yet, but the client we've just started may happen to be
7673 * too fast and call LockMachine() that will fail (because of PID, etc.),
7674 * so that the Machine will never get out of the Spawning session state.
7675 */
7676
7677 /* inform the session that it will be a remote one */
7678 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7679#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7680 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7681#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7682 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7683#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7684 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7685
7686 if (FAILED(rc))
7687 {
7688 /* restore the session state */
7689 mData->mSession.mState = SessionState_Unlocked;
7690 alock.release();
7691 mParent->i_addProcessToReap(pid);
7692 /* The failure may occur w/o any error info (from RPC), so provide one */
7693 return setError(VBOX_E_VM_ERROR,
7694 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7695 }
7696
7697 /* attach launch data to the machine */
7698 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7699 mData->mSession.mRemoteControls.push_back(aControl);
7700 mData->mSession.mProgress = aProgress;
7701 mData->mSession.mPID = pid;
7702 mData->mSession.mState = SessionState_Spawning;
7703 Assert(strCanonicalName.isNotEmpty());
7704 mData->mSession.mName = strCanonicalName;
7705 }
7706 else
7707 {
7708 /* For separate UI process we declare the launch as completed instantly, as the
7709 * actual headless VM start may or may not come. No point in remembering anything
7710 * yet, as what matters for us is when the headless VM gets started. */
7711 aProgress->i_notifyComplete(S_OK);
7712 }
7713
7714 alock.release();
7715 mParent->i_addProcessToReap(pid);
7716
7717 LogFlowThisFuncLeave();
7718 return S_OK;
7719}
7720
7721/**
7722 * Returns @c true if the given session machine instance has an open direct
7723 * session (and optionally also for direct sessions which are closing) and
7724 * returns the session control machine instance if so.
7725 *
7726 * Note that when the method returns @c false, the arguments remain unchanged.
7727 *
7728 * @param aMachine Session machine object.
7729 * @param aControl Direct session control object (optional).
7730 * @param aRequireVM If true then only allow VM sessions.
7731 * @param aAllowClosing If true then additionally a session which is currently
7732 * being closed will also be allowed.
7733 *
7734 * @note locks this object for reading.
7735 */
7736bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7737 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7738 bool aRequireVM /*= false*/,
7739 bool aAllowClosing /*= false*/)
7740{
7741 AutoLimitedCaller autoCaller(this);
7742 AssertComRCReturn(autoCaller.rc(), false);
7743
7744 /* just return false for inaccessible machines */
7745 if (getObjectState().getState() != ObjectState::Ready)
7746 return false;
7747
7748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7749
7750 if ( ( mData->mSession.mState == SessionState_Locked
7751 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7752 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7753 )
7754 {
7755 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7756
7757 aMachine = mData->mSession.mMachine;
7758
7759 if (aControl != NULL)
7760 *aControl = mData->mSession.mDirectControl;
7761
7762 return true;
7763 }
7764
7765 return false;
7766}
7767
7768/**
7769 * Returns @c true if the given machine has an spawning direct session.
7770 *
7771 * @note locks this object for reading.
7772 */
7773bool Machine::i_isSessionSpawning()
7774{
7775 AutoLimitedCaller autoCaller(this);
7776 AssertComRCReturn(autoCaller.rc(), false);
7777
7778 /* just return false for inaccessible machines */
7779 if (getObjectState().getState() != ObjectState::Ready)
7780 return false;
7781
7782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7783
7784 if (mData->mSession.mState == SessionState_Spawning)
7785 return true;
7786
7787 return false;
7788}
7789
7790/**
7791 * Called from the client watcher thread to check for unexpected client process
7792 * death during Session_Spawning state (e.g. before it successfully opened a
7793 * direct session).
7794 *
7795 * On Win32 and on OS/2, this method is called only when we've got the
7796 * direct client's process termination notification, so it always returns @c
7797 * true.
7798 *
7799 * On other platforms, this method returns @c true if the client process is
7800 * terminated and @c false if it's still alive.
7801 *
7802 * @note Locks this object for writing.
7803 */
7804bool Machine::i_checkForSpawnFailure()
7805{
7806 AutoCaller autoCaller(this);
7807 if (!autoCaller.isOk())
7808 {
7809 /* nothing to do */
7810 LogFlowThisFunc(("Already uninitialized!\n"));
7811 return true;
7812 }
7813
7814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7815
7816 if (mData->mSession.mState != SessionState_Spawning)
7817 {
7818 /* nothing to do */
7819 LogFlowThisFunc(("Not spawning any more!\n"));
7820 return true;
7821 }
7822
7823 HRESULT rc = S_OK;
7824
7825 /* PID not yet initialized, skip check. */
7826 if (mData->mSession.mPID == NIL_RTPROCESS)
7827 return false;
7828
7829 RTPROCSTATUS status;
7830 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7831
7832 if (vrc != VERR_PROCESS_RUNNING)
7833 {
7834 Utf8Str strExtraInfo;
7835
7836#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7837 /* If the startup logfile exists and is of non-zero length, tell the
7838 user to look there for more details to encourage them to attach it
7839 when reporting startup issues. */
7840 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7841 uint64_t cbStartupLogFile = 0;
7842 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7843 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7844 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7845#endif
7846
7847 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7848 rc = setError(E_FAIL,
7849 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7850 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7851 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7852 rc = setError(E_FAIL,
7853 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7854 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7855 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7856 rc = setError(E_FAIL,
7857 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7858 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7859 else
7860 rc = setErrorBoth(E_FAIL, vrc,
7861 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7862 i_getName().c_str(), vrc, strExtraInfo.c_str());
7863 }
7864
7865 if (FAILED(rc))
7866 {
7867 /* Close the remote session, remove the remote control from the list
7868 * and reset session state to Closed (@note keep the code in sync with
7869 * the relevant part in LockMachine()). */
7870
7871 Assert(mData->mSession.mRemoteControls.size() == 1);
7872 if (mData->mSession.mRemoteControls.size() == 1)
7873 {
7874 ErrorInfoKeeper eik;
7875 mData->mSession.mRemoteControls.front()->Uninitialize();
7876 }
7877
7878 mData->mSession.mRemoteControls.clear();
7879 mData->mSession.mState = SessionState_Unlocked;
7880
7881 /* finalize the progress after setting the state */
7882 if (!mData->mSession.mProgress.isNull())
7883 {
7884 mData->mSession.mProgress->notifyComplete(rc);
7885 mData->mSession.mProgress.setNull();
7886 }
7887
7888 mData->mSession.mPID = NIL_RTPROCESS;
7889
7890 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7891 return true;
7892 }
7893
7894 return false;
7895}
7896
7897/**
7898 * Checks whether the machine can be registered. If so, commits and saves
7899 * all settings.
7900 *
7901 * @note Must be called from mParent's write lock. Locks this object and
7902 * children for writing.
7903 */
7904HRESULT Machine::i_prepareRegister()
7905{
7906 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7907
7908 AutoLimitedCaller autoCaller(this);
7909 AssertComRCReturnRC(autoCaller.rc());
7910
7911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7912
7913 /* wait for state dependents to drop to zero */
7914 i_ensureNoStateDependencies(alock);
7915
7916 if (!mData->mAccessible)
7917 return setError(VBOX_E_INVALID_OBJECT_STATE,
7918 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7919 mUserData->s.strName.c_str(),
7920 mData->mUuid.toString().c_str());
7921
7922 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7923
7924 if (mData->mRegistered)
7925 return setError(VBOX_E_INVALID_OBJECT_STATE,
7926 tr("The machine '%s' with UUID {%s} is already registered"),
7927 mUserData->s.strName.c_str(),
7928 mData->mUuid.toString().c_str());
7929
7930 HRESULT rc = S_OK;
7931
7932 // Ensure the settings are saved. If we are going to be registered and
7933 // no config file exists yet, create it by calling i_saveSettings() too.
7934 if ( (mData->flModifications)
7935 || (!mData->pMachineConfigFile->fileExists())
7936 )
7937 {
7938 rc = i_saveSettings(NULL, alock);
7939 // no need to check whether VirtualBox.xml needs saving too since
7940 // we can't have a machine XML file rename pending
7941 if (FAILED(rc)) return rc;
7942 }
7943
7944 /* more config checking goes here */
7945
7946 if (SUCCEEDED(rc))
7947 {
7948 /* we may have had implicit modifications we want to fix on success */
7949 i_commit();
7950
7951 mData->mRegistered = true;
7952 }
7953 else
7954 {
7955 /* we may have had implicit modifications we want to cancel on failure*/
7956 i_rollback(false /* aNotify */);
7957 }
7958
7959 return rc;
7960}
7961
7962/**
7963 * Increases the number of objects dependent on the machine state or on the
7964 * registered state. Guarantees that these two states will not change at least
7965 * until #i_releaseStateDependency() is called.
7966 *
7967 * Depending on the @a aDepType value, additional state checks may be made.
7968 * These checks will set extended error info on failure. See
7969 * #i_checkStateDependency() for more info.
7970 *
7971 * If this method returns a failure, the dependency is not added and the caller
7972 * is not allowed to rely on any particular machine state or registration state
7973 * value and may return the failed result code to the upper level.
7974 *
7975 * @param aDepType Dependency type to add.
7976 * @param aState Current machine state (NULL if not interested).
7977 * @param aRegistered Current registered state (NULL if not interested).
7978 *
7979 * @note Locks this object for writing.
7980 */
7981HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7982 MachineState_T *aState /* = NULL */,
7983 BOOL *aRegistered /* = NULL */)
7984{
7985 AutoCaller autoCaller(this);
7986 AssertComRCReturnRC(autoCaller.rc());
7987
7988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7989
7990 HRESULT rc = i_checkStateDependency(aDepType);
7991 if (FAILED(rc)) return rc;
7992
7993 {
7994 if (mData->mMachineStateChangePending != 0)
7995 {
7996 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7997 * drop to zero so don't add more. It may make sense to wait a bit
7998 * and retry before reporting an error (since the pending state
7999 * transition should be really quick) but let's just assert for
8000 * now to see if it ever happens on practice. */
8001
8002 AssertFailed();
8003
8004 return setError(E_ACCESSDENIED,
8005 tr("Machine state change is in progress. Please retry the operation later."));
8006 }
8007
8008 ++mData->mMachineStateDeps;
8009 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8010 }
8011
8012 if (aState)
8013 *aState = mData->mMachineState;
8014 if (aRegistered)
8015 *aRegistered = mData->mRegistered;
8016
8017 return S_OK;
8018}
8019
8020/**
8021 * Decreases the number of objects dependent on the machine state.
8022 * Must always complete the #i_addStateDependency() call after the state
8023 * dependency is no more necessary.
8024 */
8025void Machine::i_releaseStateDependency()
8026{
8027 AutoCaller autoCaller(this);
8028 AssertComRCReturnVoid(autoCaller.rc());
8029
8030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8031
8032 /* releaseStateDependency() w/o addStateDependency()? */
8033 AssertReturnVoid(mData->mMachineStateDeps != 0);
8034 -- mData->mMachineStateDeps;
8035
8036 if (mData->mMachineStateDeps == 0)
8037 {
8038 /* inform i_ensureNoStateDependencies() that there are no more deps */
8039 if (mData->mMachineStateChangePending != 0)
8040 {
8041 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8042 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8043 }
8044 }
8045}
8046
8047Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8048{
8049 /* start with nothing found */
8050 Utf8Str strResult("");
8051
8052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8053
8054 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8055 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8056 // found:
8057 strResult = it->second; // source is a Utf8Str
8058
8059 return strResult;
8060}
8061
8062// protected methods
8063/////////////////////////////////////////////////////////////////////////////
8064
8065/**
8066 * Performs machine state checks based on the @a aDepType value. If a check
8067 * fails, this method will set extended error info, otherwise it will return
8068 * S_OK. It is supposed, that on failure, the caller will immediately return
8069 * the return value of this method to the upper level.
8070 *
8071 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8072 *
8073 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8074 * current state of this machine object allows to change settings of the
8075 * machine (i.e. the machine is not registered, or registered but not running
8076 * and not saved). It is useful to call this method from Machine setters
8077 * before performing any change.
8078 *
8079 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8080 * as for MutableStateDep except that if the machine is saved, S_OK is also
8081 * returned. This is useful in setters which allow changing machine
8082 * properties when it is in the saved state.
8083 *
8084 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8085 * if the current state of this machine object allows to change runtime
8086 * changeable settings of the machine (i.e. the machine is not registered, or
8087 * registered but either running or not running and not saved). It is useful
8088 * to call this method from Machine setters before performing any changes to
8089 * runtime changeable settings.
8090 *
8091 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8092 * the same as for MutableOrRunningStateDep except that if the machine is
8093 * saved, S_OK is also returned. This is useful in setters which allow
8094 * changing runtime and saved state changeable machine properties.
8095 *
8096 * @param aDepType Dependency type to check.
8097 *
8098 * @note Non Machine based classes should use #i_addStateDependency() and
8099 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8100 * template.
8101 *
8102 * @note This method must be called from under this object's read or write
8103 * lock.
8104 */
8105HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8106{
8107 switch (aDepType)
8108 {
8109 case AnyStateDep:
8110 {
8111 break;
8112 }
8113 case MutableStateDep:
8114 {
8115 if ( mData->mRegistered
8116 && ( !i_isSessionMachine()
8117 || ( mData->mMachineState != MachineState_Aborted
8118 && mData->mMachineState != MachineState_Teleported
8119 && mData->mMachineState != MachineState_PoweredOff
8120 )
8121 )
8122 )
8123 return setError(VBOX_E_INVALID_VM_STATE,
8124 tr("The machine is not mutable (state is %s)"),
8125 Global::stringifyMachineState(mData->mMachineState));
8126 break;
8127 }
8128 case MutableOrSavedStateDep:
8129 {
8130 if ( mData->mRegistered
8131 && ( !i_isSessionMachine()
8132 || ( mData->mMachineState != MachineState_Aborted
8133 && mData->mMachineState != MachineState_Teleported
8134 && mData->mMachineState != MachineState_Saved
8135 && mData->mMachineState != MachineState_AbortedSaved
8136 && mData->mMachineState != MachineState_PoweredOff
8137 )
8138 )
8139 )
8140 return setError(VBOX_E_INVALID_VM_STATE,
8141 tr("The machine is not mutable or saved (state is %s)"),
8142 Global::stringifyMachineState(mData->mMachineState));
8143 break;
8144 }
8145 case MutableOrRunningStateDep:
8146 {
8147 if ( mData->mRegistered
8148 && ( !i_isSessionMachine()
8149 || ( mData->mMachineState != MachineState_Aborted
8150 && mData->mMachineState != MachineState_Teleported
8151 && mData->mMachineState != MachineState_PoweredOff
8152 && !Global::IsOnline(mData->mMachineState)
8153 )
8154 )
8155 )
8156 return setError(VBOX_E_INVALID_VM_STATE,
8157 tr("The machine is not mutable or running (state is %s)"),
8158 Global::stringifyMachineState(mData->mMachineState));
8159 break;
8160 }
8161 case MutableOrSavedOrRunningStateDep:
8162 {
8163 if ( mData->mRegistered
8164 && ( !i_isSessionMachine()
8165 || ( mData->mMachineState != MachineState_Aborted
8166 && mData->mMachineState != MachineState_Teleported
8167 && mData->mMachineState != MachineState_Saved
8168 && mData->mMachineState != MachineState_AbortedSaved
8169 && mData->mMachineState != MachineState_PoweredOff
8170 && !Global::IsOnline(mData->mMachineState)
8171 )
8172 )
8173 )
8174 return setError(VBOX_E_INVALID_VM_STATE,
8175 tr("The machine is not mutable, saved or running (state is %s)"),
8176 Global::stringifyMachineState(mData->mMachineState));
8177 break;
8178 }
8179 }
8180
8181 return S_OK;
8182}
8183
8184/**
8185 * Helper to initialize all associated child objects and allocate data
8186 * structures.
8187 *
8188 * This method must be called as a part of the object's initialization procedure
8189 * (usually done in the #init() method).
8190 *
8191 * @note Must be called only from #init() or from #i_registeredInit().
8192 */
8193HRESULT Machine::initDataAndChildObjects()
8194{
8195 AutoCaller autoCaller(this);
8196 AssertComRCReturnRC(autoCaller.rc());
8197 AssertReturn( getObjectState().getState() == ObjectState::InInit
8198 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8199
8200 AssertReturn(!mData->mAccessible, E_FAIL);
8201
8202 /* allocate data structures */
8203 mSSData.allocate();
8204 mUserData.allocate();
8205 mHWData.allocate();
8206 mMediumAttachments.allocate();
8207 mStorageControllers.allocate();
8208 mUSBControllers.allocate();
8209
8210 /* initialize mOSTypeId */
8211 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8212
8213/** @todo r=bird: init() methods never fails, right? Why don't we make them
8214 * return void then! */
8215
8216 /* create associated BIOS settings object */
8217 unconst(mBIOSSettings).createObject();
8218 mBIOSSettings->init(this);
8219
8220 /* create associated trusted platform module object */
8221 unconst(mTrustedPlatformModule).createObject();
8222 mTrustedPlatformModule->init(this);
8223
8224 /* create associated NVRAM store object */
8225 unconst(mNvramStore).createObject();
8226 mNvramStore->init(this);
8227
8228 /* create associated record settings object */
8229 unconst(mRecordingSettings).createObject();
8230 mRecordingSettings->init(this);
8231
8232 /* create the graphics adapter object (always present) */
8233 unconst(mGraphicsAdapter).createObject();
8234 mGraphicsAdapter->init(this);
8235
8236 /* create an associated VRDE object (default is disabled) */
8237 unconst(mVRDEServer).createObject();
8238 mVRDEServer->init(this);
8239
8240 /* create associated serial port objects */
8241 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8242 {
8243 unconst(mSerialPorts[slot]).createObject();
8244 mSerialPorts[slot]->init(this, slot);
8245 }
8246
8247 /* create associated parallel port objects */
8248 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8249 {
8250 unconst(mParallelPorts[slot]).createObject();
8251 mParallelPorts[slot]->init(this, slot);
8252 }
8253
8254 /* create the audio adapter object (always present, default is disabled) */
8255 unconst(mAudioAdapter).createObject();
8256 mAudioAdapter->init(this);
8257
8258 /* create the USB device filters object (always present) */
8259 unconst(mUSBDeviceFilters).createObject();
8260 mUSBDeviceFilters->init(this);
8261
8262 /* create associated network adapter objects */
8263 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8264 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8265 {
8266 unconst(mNetworkAdapters[slot]).createObject();
8267 mNetworkAdapters[slot]->init(this, slot);
8268 }
8269
8270 /* create the bandwidth control */
8271 unconst(mBandwidthControl).createObject();
8272 mBandwidthControl->init(this);
8273
8274 return S_OK;
8275}
8276
8277/**
8278 * Helper to uninitialize all associated child objects and to free all data
8279 * structures.
8280 *
8281 * This method must be called as a part of the object's uninitialization
8282 * procedure (usually done in the #uninit() method).
8283 *
8284 * @note Must be called only from #uninit() or from #i_registeredInit().
8285 */
8286void Machine::uninitDataAndChildObjects()
8287{
8288 AutoCaller autoCaller(this);
8289 AssertComRCReturnVoid(autoCaller.rc());
8290 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8291 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8292 || getObjectState().getState() == ObjectState::InUninit
8293 || getObjectState().getState() == ObjectState::Limited);
8294
8295 /* tell all our other child objects we've been uninitialized */
8296 if (mBandwidthControl)
8297 {
8298 mBandwidthControl->uninit();
8299 unconst(mBandwidthControl).setNull();
8300 }
8301
8302 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8303 {
8304 if (mNetworkAdapters[slot])
8305 {
8306 mNetworkAdapters[slot]->uninit();
8307 unconst(mNetworkAdapters[slot]).setNull();
8308 }
8309 }
8310
8311 if (mUSBDeviceFilters)
8312 {
8313 mUSBDeviceFilters->uninit();
8314 unconst(mUSBDeviceFilters).setNull();
8315 }
8316
8317 if (mAudioAdapter)
8318 {
8319 mAudioAdapter->uninit();
8320 unconst(mAudioAdapter).setNull();
8321 }
8322
8323 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8324 {
8325 if (mParallelPorts[slot])
8326 {
8327 mParallelPorts[slot]->uninit();
8328 unconst(mParallelPorts[slot]).setNull();
8329 }
8330 }
8331
8332 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8333 {
8334 if (mSerialPorts[slot])
8335 {
8336 mSerialPorts[slot]->uninit();
8337 unconst(mSerialPorts[slot]).setNull();
8338 }
8339 }
8340
8341 if (mVRDEServer)
8342 {
8343 mVRDEServer->uninit();
8344 unconst(mVRDEServer).setNull();
8345 }
8346
8347 if (mGraphicsAdapter)
8348 {
8349 mGraphicsAdapter->uninit();
8350 unconst(mGraphicsAdapter).setNull();
8351 }
8352
8353 if (mBIOSSettings)
8354 {
8355 mBIOSSettings->uninit();
8356 unconst(mBIOSSettings).setNull();
8357 }
8358
8359 if (mTrustedPlatformModule)
8360 {
8361 mTrustedPlatformModule->uninit();
8362 unconst(mTrustedPlatformModule).setNull();
8363 }
8364
8365 if (mNvramStore)
8366 {
8367 mNvramStore->uninit();
8368 unconst(mNvramStore).setNull();
8369 }
8370
8371 if (mRecordingSettings)
8372 {
8373 mRecordingSettings->uninit();
8374 unconst(mRecordingSettings).setNull();
8375 }
8376
8377 /* Deassociate media (only when a real Machine or a SnapshotMachine
8378 * instance is uninitialized; SessionMachine instances refer to real
8379 * Machine media). This is necessary for a clean re-initialization of
8380 * the VM after successfully re-checking the accessibility state. Note
8381 * that in case of normal Machine or SnapshotMachine uninitialization (as
8382 * a result of unregistering or deleting the snapshot), outdated media
8383 * attachments will already be uninitialized and deleted, so this
8384 * code will not affect them. */
8385 if ( !mMediumAttachments.isNull()
8386 && !i_isSessionMachine()
8387 )
8388 {
8389 for (MediumAttachmentList::const_iterator
8390 it = mMediumAttachments->begin();
8391 it != mMediumAttachments->end();
8392 ++it)
8393 {
8394 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8395 if (pMedium.isNull())
8396 continue;
8397 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8398 AssertComRC(rc);
8399 }
8400 }
8401
8402 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8403 {
8404 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8405 if (mData->mFirstSnapshot)
8406 {
8407 // snapshots tree is protected by machine write lock; strictly
8408 // this isn't necessary here since we're deleting the entire
8409 // machine, but otherwise we assert in Snapshot::uninit()
8410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8411 mData->mFirstSnapshot->uninit();
8412 mData->mFirstSnapshot.setNull();
8413 }
8414
8415 mData->mCurrentSnapshot.setNull();
8416 }
8417
8418 /* free data structures (the essential mData structure is not freed here
8419 * since it may be still in use) */
8420 mMediumAttachments.free();
8421 mStorageControllers.free();
8422 mUSBControllers.free();
8423 mHWData.free();
8424 mUserData.free();
8425 mSSData.free();
8426}
8427
8428/**
8429 * Returns a pointer to the Machine object for this machine that acts like a
8430 * parent for complex machine data objects such as shared folders, etc.
8431 *
8432 * For primary Machine objects and for SnapshotMachine objects, returns this
8433 * object's pointer itself. For SessionMachine objects, returns the peer
8434 * (primary) machine pointer.
8435 */
8436Machine *Machine::i_getMachine()
8437{
8438 if (i_isSessionMachine())
8439 return (Machine*)mPeer;
8440 return this;
8441}
8442
8443/**
8444 * Makes sure that there are no machine state dependents. If necessary, waits
8445 * for the number of dependents to drop to zero.
8446 *
8447 * Make sure this method is called from under this object's write lock to
8448 * guarantee that no new dependents may be added when this method returns
8449 * control to the caller.
8450 *
8451 * @note Receives a lock to this object for writing. The lock will be released
8452 * while waiting (if necessary).
8453 *
8454 * @warning To be used only in methods that change the machine state!
8455 */
8456void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8457{
8458 AssertReturnVoid(isWriteLockOnCurrentThread());
8459
8460 /* Wait for all state dependents if necessary */
8461 if (mData->mMachineStateDeps != 0)
8462 {
8463 /* lazy semaphore creation */
8464 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8465 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8466
8467 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8468 mData->mMachineStateDeps));
8469
8470 ++mData->mMachineStateChangePending;
8471
8472 /* reset the semaphore before waiting, the last dependent will signal
8473 * it */
8474 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8475
8476 alock.release();
8477
8478 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8479
8480 alock.acquire();
8481
8482 -- mData->mMachineStateChangePending;
8483 }
8484}
8485
8486/**
8487 * Changes the machine state and informs callbacks.
8488 *
8489 * This method is not intended to fail so it either returns S_OK or asserts (and
8490 * returns a failure).
8491 *
8492 * @note Locks this object for writing.
8493 */
8494HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8495{
8496 LogFlowThisFuncEnter();
8497 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8498 Assert(aMachineState != MachineState_Null);
8499
8500 AutoCaller autoCaller(this);
8501 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8502
8503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8504
8505 /* wait for state dependents to drop to zero */
8506 i_ensureNoStateDependencies(alock);
8507
8508 MachineState_T const enmOldState = mData->mMachineState;
8509 if (enmOldState != aMachineState)
8510 {
8511 mData->mMachineState = aMachineState;
8512 RTTimeNow(&mData->mLastStateChange);
8513
8514#ifdef VBOX_WITH_DTRACE_R3_MAIN
8515 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8516#endif
8517 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8518 }
8519
8520 LogFlowThisFuncLeave();
8521 return S_OK;
8522}
8523
8524/**
8525 * Searches for a shared folder with the given logical name
8526 * in the collection of shared folders.
8527 *
8528 * @param aName logical name of the shared folder
8529 * @param aSharedFolder where to return the found object
8530 * @param aSetError whether to set the error info if the folder is
8531 * not found
8532 * @return
8533 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8534 *
8535 * @note
8536 * must be called from under the object's lock!
8537 */
8538HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8539 ComObjPtr<SharedFolder> &aSharedFolder,
8540 bool aSetError /* = false */)
8541{
8542 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8543 for (HWData::SharedFolderList::const_iterator
8544 it = mHWData->mSharedFolders.begin();
8545 it != mHWData->mSharedFolders.end();
8546 ++it)
8547 {
8548 SharedFolder *pSF = *it;
8549 AutoCaller autoCaller(pSF);
8550 if (pSF->i_getName() == aName)
8551 {
8552 aSharedFolder = pSF;
8553 rc = S_OK;
8554 break;
8555 }
8556 }
8557
8558 if (aSetError && FAILED(rc))
8559 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8560
8561 return rc;
8562}
8563
8564/**
8565 * Initializes all machine instance data from the given settings structures
8566 * from XML. The exception is the machine UUID which needs special handling
8567 * depending on the caller's use case, so the caller needs to set that herself.
8568 *
8569 * This gets called in several contexts during machine initialization:
8570 *
8571 * -- When machine XML exists on disk already and needs to be loaded into memory,
8572 * for example, from #i_registeredInit() to load all registered machines on
8573 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8574 * attached to the machine should be part of some media registry already.
8575 *
8576 * -- During OVF import, when a machine config has been constructed from an
8577 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8578 * ensure that the media listed as attachments in the config (which have
8579 * been imported from the OVF) receive the correct registry ID.
8580 *
8581 * -- During VM cloning.
8582 *
8583 * @param config Machine settings from XML.
8584 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8585 * for each attached medium in the config.
8586 * @return
8587 */
8588HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8589 const Guid *puuidRegistry)
8590{
8591 // copy name, description, OS type, teleporter, UTC etc.
8592 mUserData->s = config.machineUserData;
8593
8594 // look up the object by Id to check it is valid
8595 ComObjPtr<GuestOSType> pGuestOSType;
8596 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8597 if (!pGuestOSType.isNull())
8598 mUserData->s.strOsType = pGuestOSType->i_id();
8599
8600 // stateFile (optional)
8601 if (config.strStateFile.isEmpty())
8602 mSSData->strStateFilePath.setNull();
8603 else
8604 {
8605 Utf8Str stateFilePathFull(config.strStateFile);
8606 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8607 if (RT_FAILURE(vrc))
8608 return setErrorBoth(E_FAIL, vrc,
8609 tr("Invalid saved state file path '%s' (%Rrc)"),
8610 config.strStateFile.c_str(),
8611 vrc);
8612 mSSData->strStateFilePath = stateFilePathFull;
8613 }
8614
8615 // snapshot folder needs special processing so set it again
8616 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8617 if (FAILED(rc)) return rc;
8618
8619 /* Copy the extra data items (config may or may not be the same as
8620 * mData->pMachineConfigFile) if necessary. When loading the XML files
8621 * from disk they are the same, but not for OVF import. */
8622 if (mData->pMachineConfigFile != &config)
8623 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8624
8625 /* currentStateModified (optional, default is true) */
8626 mData->mCurrentStateModified = config.fCurrentStateModified;
8627
8628 mData->mLastStateChange = config.timeLastStateChange;
8629
8630 /*
8631 * note: all mUserData members must be assigned prior this point because
8632 * we need to commit changes in order to let mUserData be shared by all
8633 * snapshot machine instances.
8634 */
8635 mUserData.commitCopy();
8636
8637 // machine registry, if present (must be loaded before snapshots)
8638 if (config.canHaveOwnMediaRegistry())
8639 {
8640 // determine machine folder
8641 Utf8Str strMachineFolder = i_getSettingsFileFull();
8642 strMachineFolder.stripFilename();
8643 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8644 config.mediaRegistry,
8645 strMachineFolder);
8646 if (FAILED(rc)) return rc;
8647 }
8648
8649 /* Snapshot node (optional) */
8650 size_t cRootSnapshots;
8651 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8652 {
8653 // there must be only one root snapshot
8654 Assert(cRootSnapshots == 1);
8655
8656 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8657
8658 rc = i_loadSnapshot(snap,
8659 config.uuidCurrentSnapshot,
8660 NULL); // no parent == first snapshot
8661 if (FAILED(rc)) return rc;
8662 }
8663
8664 // hardware data
8665 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8666 if (FAILED(rc)) return rc;
8667
8668 /*
8669 * NOTE: the assignment below must be the last thing to do,
8670 * otherwise it will be not possible to change the settings
8671 * somewhere in the code above because all setters will be
8672 * blocked by i_checkStateDependency(MutableStateDep).
8673 */
8674
8675 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8676 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8677 {
8678 /* no need to use i_setMachineState() during init() */
8679 mData->mMachineState = MachineState_AbortedSaved;
8680 }
8681 else if (config.fAborted)
8682 {
8683 mSSData->strStateFilePath.setNull();
8684
8685 /* no need to use i_setMachineState() during init() */
8686 mData->mMachineState = MachineState_Aborted;
8687 }
8688 else if (!mSSData->strStateFilePath.isEmpty())
8689 {
8690 /* no need to use i_setMachineState() during init() */
8691 mData->mMachineState = MachineState_Saved;
8692 }
8693
8694 // after loading settings, we are no longer different from the XML on disk
8695 mData->flModifications = 0;
8696
8697 return S_OK;
8698}
8699
8700/**
8701 * Recursively loads all snapshots starting from the given.
8702 *
8703 * @param data snapshot settings.
8704 * @param aCurSnapshotId Current snapshot ID from the settings file.
8705 * @param aParentSnapshot Parent snapshot.
8706 */
8707HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8708 const Guid &aCurSnapshotId,
8709 Snapshot *aParentSnapshot)
8710{
8711 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8712 AssertReturn(!i_isSessionMachine(), E_FAIL);
8713
8714 HRESULT rc = S_OK;
8715
8716 Utf8Str strStateFile;
8717 if (!data.strStateFile.isEmpty())
8718 {
8719 /* optional */
8720 strStateFile = data.strStateFile;
8721 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8722 if (RT_FAILURE(vrc))
8723 return setErrorBoth(E_FAIL, vrc,
8724 tr("Invalid saved state file path '%s' (%Rrc)"),
8725 strStateFile.c_str(),
8726 vrc);
8727 }
8728
8729 /* create a snapshot machine object */
8730 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8731 pSnapshotMachine.createObject();
8732 rc = pSnapshotMachine->initFromSettings(this,
8733 data.hardware,
8734 &data.debugging,
8735 &data.autostart,
8736 data.uuid.ref(),
8737 strStateFile);
8738 if (FAILED(rc)) return rc;
8739
8740 /* create a snapshot object */
8741 ComObjPtr<Snapshot> pSnapshot;
8742 pSnapshot.createObject();
8743 /* initialize the snapshot */
8744 rc = pSnapshot->init(mParent, // VirtualBox object
8745 data.uuid,
8746 data.strName,
8747 data.strDescription,
8748 data.timestamp,
8749 pSnapshotMachine,
8750 aParentSnapshot);
8751 if (FAILED(rc)) return rc;
8752
8753 /* memorize the first snapshot if necessary */
8754 if (!mData->mFirstSnapshot)
8755 mData->mFirstSnapshot = pSnapshot;
8756
8757 /* memorize the current snapshot when appropriate */
8758 if ( !mData->mCurrentSnapshot
8759 && pSnapshot->i_getId() == aCurSnapshotId
8760 )
8761 mData->mCurrentSnapshot = pSnapshot;
8762
8763 // now create the children
8764 for (settings::SnapshotsList::const_iterator
8765 it = data.llChildSnapshots.begin();
8766 it != data.llChildSnapshots.end();
8767 ++it)
8768 {
8769 const settings::Snapshot &childData = *it;
8770 // recurse
8771 rc = i_loadSnapshot(childData,
8772 aCurSnapshotId,
8773 pSnapshot); // parent = the one we created above
8774 if (FAILED(rc)) return rc;
8775 }
8776
8777 return rc;
8778}
8779
8780/**
8781 * Loads settings into mHWData.
8782 *
8783 * @param puuidRegistry Registry ID.
8784 * @param puuidSnapshot Snapshot ID
8785 * @param data Reference to the hardware settings.
8786 * @param pDbg Pointer to the debugging settings.
8787 * @param pAutostart Pointer to the autostart settings.
8788 */
8789HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8790 const Guid *puuidSnapshot,
8791 const settings::Hardware &data,
8792 const settings::Debugging *pDbg,
8793 const settings::Autostart *pAutostart)
8794{
8795 AssertReturn(!i_isSessionMachine(), E_FAIL);
8796
8797 HRESULT rc = S_OK;
8798
8799 try
8800 {
8801 ComObjPtr<GuestOSType> pGuestOSType;
8802 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8803
8804 /* The hardware version attribute (optional). */
8805 mHWData->mHWVersion = data.strVersion;
8806 mHWData->mHardwareUUID = data.uuid;
8807
8808 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8809 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8810 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8811 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8812 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8813 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8814 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8815 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8816 mHWData->mPAEEnabled = data.fPAE;
8817 mHWData->mLongMode = data.enmLongMode;
8818 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8819 mHWData->mAPIC = data.fAPIC;
8820 mHWData->mX2APIC = data.fX2APIC;
8821 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8822 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8823 mHWData->mSpecCtrl = data.fSpecCtrl;
8824 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8825 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8826 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8827 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8828 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8829 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8830 mHWData->mCPUCount = data.cCPUs;
8831 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8832 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8833 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8834 mHWData->mCpuProfile = data.strCpuProfile;
8835
8836 // cpu
8837 if (mHWData->mCPUHotPlugEnabled)
8838 {
8839 for (settings::CpuList::const_iterator
8840 it = data.llCpus.begin();
8841 it != data.llCpus.end();
8842 ++it)
8843 {
8844 const settings::Cpu &cpu = *it;
8845
8846 mHWData->mCPUAttached[cpu.ulId] = true;
8847 }
8848 }
8849
8850 // cpuid leafs
8851 for (settings::CpuIdLeafsList::const_iterator
8852 it = data.llCpuIdLeafs.begin();
8853 it != data.llCpuIdLeafs.end();
8854 ++it)
8855 {
8856 const settings::CpuIdLeaf &rLeaf= *it;
8857 if ( rLeaf.idx < UINT32_C(0x20)
8858 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8859 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8860 mHWData->mCpuIdLeafList.push_back(rLeaf);
8861 /* else: just ignore */
8862 }
8863
8864 mHWData->mMemorySize = data.ulMemorySizeMB;
8865 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8866
8867 // boot order
8868 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8869 {
8870 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8871 if (it == data.mapBootOrder.end())
8872 mHWData->mBootOrder[i] = DeviceType_Null;
8873 else
8874 mHWData->mBootOrder[i] = it->second;
8875 }
8876
8877 mHWData->mFirmwareType = data.firmwareType;
8878 mHWData->mPointingHIDType = data.pointingHIDType;
8879 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8880 mHWData->mChipsetType = data.chipsetType;
8881 mHWData->mIommuType = data.iommuType;
8882 mHWData->mParavirtProvider = data.paravirtProvider;
8883 mHWData->mParavirtDebug = data.strParavirtDebug;
8884 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8885 mHWData->mHPETEnabled = data.fHPETEnabled;
8886
8887 /* GraphicsAdapter */
8888 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8889 if (FAILED(rc)) return rc;
8890
8891 /* VRDEServer */
8892 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8893 if (FAILED(rc)) return rc;
8894
8895 /* BIOS */
8896 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8897 if (FAILED(rc)) return rc;
8898
8899 /* Trusted Platform Module */
8900 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8901 if (FAILED(rc)) return rc;
8902
8903 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8904 if (FAILED(rc)) return rc;
8905
8906 /* Recording settings */
8907 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8908 if (FAILED(rc)) return rc;
8909
8910 // Bandwidth control (must come before network adapters)
8911 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8912 if (FAILED(rc)) return rc;
8913
8914 /* USB controllers */
8915 for (settings::USBControllerList::const_iterator
8916 it = data.usbSettings.llUSBControllers.begin();
8917 it != data.usbSettings.llUSBControllers.end();
8918 ++it)
8919 {
8920 const settings::USBController &settingsCtrl = *it;
8921 ComObjPtr<USBController> newCtrl;
8922
8923 newCtrl.createObject();
8924 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8925 mUSBControllers->push_back(newCtrl);
8926 }
8927
8928 /* USB device filters */
8929 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8930 if (FAILED(rc)) return rc;
8931
8932 // network adapters (establish array size first and apply defaults, to
8933 // ensure reading the same settings as we saved, since the list skips
8934 // adapters having defaults)
8935 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8936 size_t oldCount = mNetworkAdapters.size();
8937 if (newCount > oldCount)
8938 {
8939 mNetworkAdapters.resize(newCount);
8940 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8941 {
8942 unconst(mNetworkAdapters[slot]).createObject();
8943 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8944 }
8945 }
8946 else if (newCount < oldCount)
8947 mNetworkAdapters.resize(newCount);
8948 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8949 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8950 for (settings::NetworkAdaptersList::const_iterator
8951 it = data.llNetworkAdapters.begin();
8952 it != data.llNetworkAdapters.end();
8953 ++it)
8954 {
8955 const settings::NetworkAdapter &nic = *it;
8956
8957 /* slot uniqueness is guaranteed by XML Schema */
8958 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8959 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8960 if (FAILED(rc)) return rc;
8961 }
8962
8963 // serial ports (establish defaults first, to ensure reading the same
8964 // settings as we saved, since the list skips ports having defaults)
8965 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8966 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8967 for (settings::SerialPortsList::const_iterator
8968 it = data.llSerialPorts.begin();
8969 it != data.llSerialPorts.end();
8970 ++it)
8971 {
8972 const settings::SerialPort &s = *it;
8973
8974 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8975 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8976 if (FAILED(rc)) return rc;
8977 }
8978
8979 // parallel ports (establish defaults first, to ensure reading the same
8980 // settings as we saved, since the list skips ports having defaults)
8981 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8982 mParallelPorts[i]->i_applyDefaults();
8983 for (settings::ParallelPortsList::const_iterator
8984 it = data.llParallelPorts.begin();
8985 it != data.llParallelPorts.end();
8986 ++it)
8987 {
8988 const settings::ParallelPort &p = *it;
8989
8990 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8991 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8992 if (FAILED(rc)) return rc;
8993 }
8994
8995 /* AudioAdapter */
8996 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8997 if (FAILED(rc)) return rc;
8998
8999 /* storage controllers */
9000 rc = i_loadStorageControllers(data.storage,
9001 puuidRegistry,
9002 puuidSnapshot);
9003 if (FAILED(rc)) return rc;
9004
9005 /* Shared folders */
9006 for (settings::SharedFoldersList::const_iterator
9007 it = data.llSharedFolders.begin();
9008 it != data.llSharedFolders.end();
9009 ++it)
9010 {
9011 const settings::SharedFolder &sf = *it;
9012
9013 ComObjPtr<SharedFolder> sharedFolder;
9014 /* Check for double entries. Not allowed! */
9015 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9016 if (SUCCEEDED(rc))
9017 return setError(VBOX_E_OBJECT_IN_USE,
9018 tr("Shared folder named '%s' already exists"),
9019 sf.strName.c_str());
9020
9021 /* Create the new shared folder. Don't break on error. This will be
9022 * reported when the machine starts. */
9023 sharedFolder.createObject();
9024 rc = sharedFolder->init(i_getMachine(),
9025 sf.strName,
9026 sf.strHostPath,
9027 RT_BOOL(sf.fWritable),
9028 RT_BOOL(sf.fAutoMount),
9029 sf.strAutoMountPoint,
9030 false /* fFailOnError */);
9031 if (FAILED(rc)) return rc;
9032 mHWData->mSharedFolders.push_back(sharedFolder);
9033 }
9034
9035 // Clipboard
9036 mHWData->mClipboardMode = data.clipboardMode;
9037 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9038
9039 // drag'n'drop
9040 mHWData->mDnDMode = data.dndMode;
9041
9042 // guest settings
9043 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9044
9045 // IO settings
9046 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9047 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9048
9049 // Host PCI devices
9050 for (settings::HostPCIDeviceAttachmentList::const_iterator
9051 it = data.pciAttachments.begin();
9052 it != data.pciAttachments.end();
9053 ++it)
9054 {
9055 const settings::HostPCIDeviceAttachment &hpda = *it;
9056 ComObjPtr<PCIDeviceAttachment> pda;
9057
9058 pda.createObject();
9059 pda->i_loadSettings(this, hpda);
9060 mHWData->mPCIDeviceAssignments.push_back(pda);
9061 }
9062
9063 /*
9064 * (The following isn't really real hardware, but it lives in HWData
9065 * for reasons of convenience.)
9066 */
9067
9068#ifdef VBOX_WITH_GUEST_PROPS
9069 /* Guest properties (optional) */
9070
9071 /* Only load transient guest properties for configs which have saved
9072 * state, because there shouldn't be any for powered off VMs. The same
9073 * logic applies for snapshots, as offline snapshots shouldn't have
9074 * any such properties. They confuse the code in various places.
9075 * Note: can't rely on the machine state, as it isn't set yet. */
9076 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9077 /* apologies for the hacky unconst() usage, but this needs hacking
9078 * actually inconsistent settings into consistency, otherwise there
9079 * will be some corner cases where the inconsistency survives
9080 * surprisingly long without getting fixed, especially for snapshots
9081 * as there are no config changes. */
9082 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9083 for (settings::GuestPropertiesList::iterator
9084 it = llGuestProperties.begin();
9085 it != llGuestProperties.end();
9086 /*nothing*/)
9087 {
9088 const settings::GuestProperty &prop = *it;
9089 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9090 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9091 if ( fSkipTransientGuestProperties
9092 && ( fFlags & GUEST_PROP_F_TRANSIENT
9093 || fFlags & GUEST_PROP_F_TRANSRESET))
9094 {
9095 it = llGuestProperties.erase(it);
9096 continue;
9097 }
9098 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9099 mHWData->mGuestProperties[prop.strName] = property;
9100 ++it;
9101 }
9102#endif /* VBOX_WITH_GUEST_PROPS defined */
9103
9104 rc = i_loadDebugging(pDbg);
9105 if (FAILED(rc))
9106 return rc;
9107
9108 mHWData->mAutostart = *pAutostart;
9109
9110 /* default frontend */
9111 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9112 }
9113 catch (std::bad_alloc &)
9114 {
9115 return E_OUTOFMEMORY;
9116 }
9117
9118 AssertComRC(rc);
9119 return rc;
9120}
9121
9122/**
9123 * Called from i_loadHardware() to load the debugging settings of the
9124 * machine.
9125 *
9126 * @param pDbg Pointer to the settings.
9127 */
9128HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9129{
9130 mHWData->mDebugging = *pDbg;
9131 /* no more processing currently required, this will probably change. */
9132 return S_OK;
9133}
9134
9135/**
9136 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9137 *
9138 * @param data storage settings.
9139 * @param puuidRegistry media registry ID to set media to or NULL;
9140 * see Machine::i_loadMachineDataFromSettings()
9141 * @param puuidSnapshot snapshot ID
9142 * @return
9143 */
9144HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9145 const Guid *puuidRegistry,
9146 const Guid *puuidSnapshot)
9147{
9148 AssertReturn(!i_isSessionMachine(), E_FAIL);
9149
9150 HRESULT rc = S_OK;
9151
9152 for (settings::StorageControllersList::const_iterator
9153 it = data.llStorageControllers.begin();
9154 it != data.llStorageControllers.end();
9155 ++it)
9156 {
9157 const settings::StorageController &ctlData = *it;
9158
9159 ComObjPtr<StorageController> pCtl;
9160 /* Try to find one with the name first. */
9161 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9162 if (SUCCEEDED(rc))
9163 return setError(VBOX_E_OBJECT_IN_USE,
9164 tr("Storage controller named '%s' already exists"),
9165 ctlData.strName.c_str());
9166
9167 pCtl.createObject();
9168 rc = pCtl->init(this,
9169 ctlData.strName,
9170 ctlData.storageBus,
9171 ctlData.ulInstance,
9172 ctlData.fBootable);
9173 if (FAILED(rc)) return rc;
9174
9175 mStorageControllers->push_back(pCtl);
9176
9177 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9178 if (FAILED(rc)) return rc;
9179
9180 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9181 if (FAILED(rc)) return rc;
9182
9183 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9184 if (FAILED(rc)) return rc;
9185
9186 /* Load the attached devices now. */
9187 rc = i_loadStorageDevices(pCtl,
9188 ctlData,
9189 puuidRegistry,
9190 puuidSnapshot);
9191 if (FAILED(rc)) return rc;
9192 }
9193
9194 return S_OK;
9195}
9196
9197/**
9198 * Called from i_loadStorageControllers for a controller's devices.
9199 *
9200 * @param aStorageController
9201 * @param data
9202 * @param puuidRegistry media registry ID to set media to or NULL; see
9203 * Machine::i_loadMachineDataFromSettings()
9204 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9205 * @return
9206 */
9207HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9208 const settings::StorageController &data,
9209 const Guid *puuidRegistry,
9210 const Guid *puuidSnapshot)
9211{
9212 HRESULT rc = S_OK;
9213
9214 /* paranoia: detect duplicate attachments */
9215 for (settings::AttachedDevicesList::const_iterator
9216 it = data.llAttachedDevices.begin();
9217 it != data.llAttachedDevices.end();
9218 ++it)
9219 {
9220 const settings::AttachedDevice &ad = *it;
9221
9222 for (settings::AttachedDevicesList::const_iterator it2 = it;
9223 it2 != data.llAttachedDevices.end();
9224 ++it2)
9225 {
9226 if (it == it2)
9227 continue;
9228
9229 const settings::AttachedDevice &ad2 = *it2;
9230
9231 if ( ad.lPort == ad2.lPort
9232 && ad.lDevice == ad2.lDevice)
9233 {
9234 return setError(E_FAIL,
9235 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9236 aStorageController->i_getName().c_str(),
9237 ad.lPort,
9238 ad.lDevice,
9239 mUserData->s.strName.c_str());
9240 }
9241 }
9242 }
9243
9244 for (settings::AttachedDevicesList::const_iterator
9245 it = data.llAttachedDevices.begin();
9246 it != data.llAttachedDevices.end();
9247 ++it)
9248 {
9249 const settings::AttachedDevice &dev = *it;
9250 ComObjPtr<Medium> medium;
9251
9252 switch (dev.deviceType)
9253 {
9254 case DeviceType_Floppy:
9255 case DeviceType_DVD:
9256 if (dev.strHostDriveSrc.isNotEmpty())
9257 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9258 false /* fRefresh */, medium);
9259 else
9260 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9261 dev.uuid,
9262 false /* fRefresh */,
9263 false /* aSetError */,
9264 medium);
9265 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9266 // This is not an error. The host drive or UUID might have vanished, so just go
9267 // ahead without this removeable medium attachment
9268 rc = S_OK;
9269 break;
9270
9271 case DeviceType_HardDisk:
9272 {
9273 /* find a hard disk by UUID */
9274 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9275 if (FAILED(rc))
9276 {
9277 if (i_isSnapshotMachine())
9278 {
9279 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9280 // so the user knows that the bad disk is in a snapshot somewhere
9281 com::ErrorInfo info;
9282 return setError(E_FAIL,
9283 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9284 puuidSnapshot->raw(),
9285 info.getText().raw());
9286 }
9287 else
9288 return rc;
9289 }
9290
9291 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9292
9293 if (medium->i_getType() == MediumType_Immutable)
9294 {
9295 if (i_isSnapshotMachine())
9296 return setError(E_FAIL,
9297 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9298 "of the virtual machine '%s' ('%s')"),
9299 medium->i_getLocationFull().c_str(),
9300 dev.uuid.raw(),
9301 puuidSnapshot->raw(),
9302 mUserData->s.strName.c_str(),
9303 mData->m_strConfigFileFull.c_str());
9304
9305 return setError(E_FAIL,
9306 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9307 medium->i_getLocationFull().c_str(),
9308 dev.uuid.raw(),
9309 mUserData->s.strName.c_str(),
9310 mData->m_strConfigFileFull.c_str());
9311 }
9312
9313 if (medium->i_getType() == MediumType_MultiAttach)
9314 {
9315 if (i_isSnapshotMachine())
9316 return setError(E_FAIL,
9317 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9318 "of the virtual machine '%s' ('%s')"),
9319 medium->i_getLocationFull().c_str(),
9320 dev.uuid.raw(),
9321 puuidSnapshot->raw(),
9322 mUserData->s.strName.c_str(),
9323 mData->m_strConfigFileFull.c_str());
9324
9325 return setError(E_FAIL,
9326 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9327 medium->i_getLocationFull().c_str(),
9328 dev.uuid.raw(),
9329 mUserData->s.strName.c_str(),
9330 mData->m_strConfigFileFull.c_str());
9331 }
9332
9333 if ( !i_isSnapshotMachine()
9334 && medium->i_getChildren().size() != 0
9335 )
9336 return setError(E_FAIL,
9337 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9338 "because it has %d differencing child hard disks"),
9339 medium->i_getLocationFull().c_str(),
9340 dev.uuid.raw(),
9341 mUserData->s.strName.c_str(),
9342 mData->m_strConfigFileFull.c_str(),
9343 medium->i_getChildren().size());
9344
9345 if (i_findAttachment(*mMediumAttachments.data(),
9346 medium))
9347 return setError(E_FAIL,
9348 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9349 medium->i_getLocationFull().c_str(),
9350 dev.uuid.raw(),
9351 mUserData->s.strName.c_str(),
9352 mData->m_strConfigFileFull.c_str());
9353
9354 break;
9355 }
9356
9357 default:
9358 return setError(E_FAIL,
9359 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9360 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9361 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9362 }
9363
9364 if (FAILED(rc))
9365 break;
9366
9367 /* Bandwidth groups are loaded at this point. */
9368 ComObjPtr<BandwidthGroup> pBwGroup;
9369
9370 if (!dev.strBwGroup.isEmpty())
9371 {
9372 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9373 if (FAILED(rc))
9374 return setError(E_FAIL,
9375 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9376 medium->i_getLocationFull().c_str(),
9377 dev.strBwGroup.c_str(),
9378 mUserData->s.strName.c_str(),
9379 mData->m_strConfigFileFull.c_str());
9380 pBwGroup->i_reference();
9381 }
9382
9383 const Utf8Str controllerName = aStorageController->i_getName();
9384 ComObjPtr<MediumAttachment> pAttachment;
9385 pAttachment.createObject();
9386 rc = pAttachment->init(this,
9387 medium,
9388 controllerName,
9389 dev.lPort,
9390 dev.lDevice,
9391 dev.deviceType,
9392 false,
9393 dev.fPassThrough,
9394 dev.fTempEject,
9395 dev.fNonRotational,
9396 dev.fDiscard,
9397 dev.fHotPluggable,
9398 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9399 if (FAILED(rc)) break;
9400
9401 /* associate the medium with this machine and snapshot */
9402 if (!medium.isNull())
9403 {
9404 AutoCaller medCaller(medium);
9405 if (FAILED(medCaller.rc())) return medCaller.rc();
9406 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9407
9408 if (i_isSnapshotMachine())
9409 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9410 else
9411 rc = medium->i_addBackReference(mData->mUuid);
9412 /* If the medium->addBackReference fails it sets an appropriate
9413 * error message, so no need to do any guesswork here. */
9414
9415 if (puuidRegistry)
9416 // caller wants registry ID to be set on all attached media (OVF import case)
9417 medium->i_addRegistry(*puuidRegistry);
9418 }
9419
9420 if (FAILED(rc))
9421 break;
9422
9423 /* back up mMediumAttachments to let registeredInit() properly rollback
9424 * on failure (= limited accessibility) */
9425 i_setModified(IsModified_Storage);
9426 mMediumAttachments.backup();
9427 mMediumAttachments->push_back(pAttachment);
9428 }
9429
9430 return rc;
9431}
9432
9433/**
9434 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9435 *
9436 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9437 * @param aSnapshot where to return the found snapshot
9438 * @param aSetError true to set extended error info on failure
9439 */
9440HRESULT Machine::i_findSnapshotById(const Guid &aId,
9441 ComObjPtr<Snapshot> &aSnapshot,
9442 bool aSetError /* = false */)
9443{
9444 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9445
9446 if (!mData->mFirstSnapshot)
9447 {
9448 if (aSetError)
9449 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9450 return E_FAIL;
9451 }
9452
9453 if (aId.isZero())
9454 aSnapshot = mData->mFirstSnapshot;
9455 else
9456 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9457
9458 if (!aSnapshot)
9459 {
9460 if (aSetError)
9461 return setError(E_FAIL,
9462 tr("Could not find a snapshot with UUID {%s}"),
9463 aId.toString().c_str());
9464 return E_FAIL;
9465 }
9466
9467 return S_OK;
9468}
9469
9470/**
9471 * Returns the snapshot with the given name or fails of no such snapshot.
9472 *
9473 * @param strName snapshot name to find
9474 * @param aSnapshot where to return the found snapshot
9475 * @param aSetError true to set extended error info on failure
9476 */
9477HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9478 ComObjPtr<Snapshot> &aSnapshot,
9479 bool aSetError /* = false */)
9480{
9481 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9482
9483 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9484
9485 if (!mData->mFirstSnapshot)
9486 {
9487 if (aSetError)
9488 return setError(VBOX_E_OBJECT_NOT_FOUND,
9489 tr("This machine does not have any snapshots"));
9490 return VBOX_E_OBJECT_NOT_FOUND;
9491 }
9492
9493 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9494
9495 if (!aSnapshot)
9496 {
9497 if (aSetError)
9498 return setError(VBOX_E_OBJECT_NOT_FOUND,
9499 tr("Could not find a snapshot named '%s'"), strName.c_str());
9500 return VBOX_E_OBJECT_NOT_FOUND;
9501 }
9502
9503 return S_OK;
9504}
9505
9506/**
9507 * Returns a storage controller object with the given name.
9508 *
9509 * @param aName storage controller name to find
9510 * @param aStorageController where to return the found storage controller
9511 * @param aSetError true to set extended error info on failure
9512 */
9513HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9514 ComObjPtr<StorageController> &aStorageController,
9515 bool aSetError /* = false */)
9516{
9517 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9518
9519 for (StorageControllerList::const_iterator
9520 it = mStorageControllers->begin();
9521 it != mStorageControllers->end();
9522 ++it)
9523 {
9524 if ((*it)->i_getName() == aName)
9525 {
9526 aStorageController = (*it);
9527 return S_OK;
9528 }
9529 }
9530
9531 if (aSetError)
9532 return setError(VBOX_E_OBJECT_NOT_FOUND,
9533 tr("Could not find a storage controller named '%s'"),
9534 aName.c_str());
9535 return VBOX_E_OBJECT_NOT_FOUND;
9536}
9537
9538/**
9539 * Returns a USB controller object with the given name.
9540 *
9541 * @param aName USB controller name to find
9542 * @param aUSBController where to return the found USB controller
9543 * @param aSetError true to set extended error info on failure
9544 */
9545HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9546 ComObjPtr<USBController> &aUSBController,
9547 bool aSetError /* = false */)
9548{
9549 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9550
9551 for (USBControllerList::const_iterator
9552 it = mUSBControllers->begin();
9553 it != mUSBControllers->end();
9554 ++it)
9555 {
9556 if ((*it)->i_getName() == aName)
9557 {
9558 aUSBController = (*it);
9559 return S_OK;
9560 }
9561 }
9562
9563 if (aSetError)
9564 return setError(VBOX_E_OBJECT_NOT_FOUND,
9565 tr("Could not find a storage controller named '%s'"),
9566 aName.c_str());
9567 return VBOX_E_OBJECT_NOT_FOUND;
9568}
9569
9570/**
9571 * Returns the number of USB controller instance of the given type.
9572 *
9573 * @param enmType USB controller type.
9574 */
9575ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9576{
9577 ULONG cCtrls = 0;
9578
9579 for (USBControllerList::const_iterator
9580 it = mUSBControllers->begin();
9581 it != mUSBControllers->end();
9582 ++it)
9583 {
9584 if ((*it)->i_getControllerType() == enmType)
9585 cCtrls++;
9586 }
9587
9588 return cCtrls;
9589}
9590
9591HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9592 MediumAttachmentList &atts)
9593{
9594 AutoCaller autoCaller(this);
9595 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9596
9597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9598
9599 for (MediumAttachmentList::const_iterator
9600 it = mMediumAttachments->begin();
9601 it != mMediumAttachments->end();
9602 ++it)
9603 {
9604 const ComObjPtr<MediumAttachment> &pAtt = *it;
9605 // should never happen, but deal with NULL pointers in the list.
9606 AssertContinue(!pAtt.isNull());
9607
9608 // getControllerName() needs caller+read lock
9609 AutoCaller autoAttCaller(pAtt);
9610 if (FAILED(autoAttCaller.rc()))
9611 {
9612 atts.clear();
9613 return autoAttCaller.rc();
9614 }
9615 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9616
9617 if (pAtt->i_getControllerName() == aName)
9618 atts.push_back(pAtt);
9619 }
9620
9621 return S_OK;
9622}
9623
9624
9625/**
9626 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9627 * file if the machine name was changed and about creating a new settings file
9628 * if this is a new machine.
9629 *
9630 * @note Must be never called directly but only from #saveSettings().
9631 */
9632HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9633 bool *pfSettingsFileIsNew)
9634{
9635 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9636
9637 HRESULT rc = S_OK;
9638
9639 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9640 /// @todo need to handle primary group change, too
9641
9642 /* attempt to rename the settings file if machine name is changed */
9643 if ( mUserData->s.fNameSync
9644 && mUserData.isBackedUp()
9645 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9646 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9647 )
9648 {
9649 bool dirRenamed = false;
9650 bool fileRenamed = false;
9651
9652 Utf8Str configFile, newConfigFile;
9653 Utf8Str configFilePrev, newConfigFilePrev;
9654 Utf8Str NVRAMFile, newNVRAMFile;
9655 Utf8Str configDir, newConfigDir;
9656
9657 do
9658 {
9659 int vrc = VINF_SUCCESS;
9660
9661 Utf8Str name = mUserData.backedUpData()->s.strName;
9662 Utf8Str newName = mUserData->s.strName;
9663 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9664 if (group == "/")
9665 group.setNull();
9666 Utf8Str newGroup = mUserData->s.llGroups.front();
9667 if (newGroup == "/")
9668 newGroup.setNull();
9669
9670 configFile = mData->m_strConfigFileFull;
9671
9672 /* first, rename the directory if it matches the group and machine name */
9673 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9674 /** @todo hack, make somehow use of ComposeMachineFilename */
9675 if (mUserData->s.fDirectoryIncludesUUID)
9676 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9677 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9678 /** @todo hack, make somehow use of ComposeMachineFilename */
9679 if (mUserData->s.fDirectoryIncludesUUID)
9680 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9681 configDir = configFile;
9682 configDir.stripFilename();
9683 newConfigDir = configDir;
9684 if ( configDir.length() >= groupPlusName.length()
9685 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9686 groupPlusName.c_str()))
9687 {
9688 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9689 Utf8Str newConfigBaseDir(newConfigDir);
9690 newConfigDir.append(newGroupPlusName);
9691 /* consistency: use \ if appropriate on the platform */
9692 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9693 /* new dir and old dir cannot be equal here because of 'if'
9694 * above and because name != newName */
9695 Assert(configDir != newConfigDir);
9696 if (!fSettingsFileIsNew)
9697 {
9698 /* perform real rename only if the machine is not new */
9699 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9700 if ( vrc == VERR_FILE_NOT_FOUND
9701 || vrc == VERR_PATH_NOT_FOUND)
9702 {
9703 /* create the parent directory, then retry renaming */
9704 Utf8Str parent(newConfigDir);
9705 parent.stripFilename();
9706 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9707 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9708 }
9709 if (RT_FAILURE(vrc))
9710 {
9711 rc = setErrorBoth(E_FAIL, vrc,
9712 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9713 configDir.c_str(),
9714 newConfigDir.c_str(),
9715 vrc);
9716 break;
9717 }
9718 /* delete subdirectories which are no longer needed */
9719 Utf8Str dir(configDir);
9720 dir.stripFilename();
9721 while (dir != newConfigBaseDir && dir != ".")
9722 {
9723 vrc = RTDirRemove(dir.c_str());
9724 if (RT_FAILURE(vrc))
9725 break;
9726 dir.stripFilename();
9727 }
9728 dirRenamed = true;
9729 }
9730 }
9731
9732 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9733
9734 /* then try to rename the settings file itself */
9735 if (newConfigFile != configFile)
9736 {
9737 /* get the path to old settings file in renamed directory */
9738 Assert(mData->m_strConfigFileFull == configFile);
9739 configFile.printf("%s%c%s",
9740 newConfigDir.c_str(),
9741 RTPATH_DELIMITER,
9742 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9743 if (!fSettingsFileIsNew)
9744 {
9745 /* perform real rename only if the machine is not new */
9746 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9747 if (RT_FAILURE(vrc))
9748 {
9749 rc = setErrorBoth(E_FAIL, vrc,
9750 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9751 configFile.c_str(),
9752 newConfigFile.c_str(),
9753 vrc);
9754 break;
9755 }
9756 fileRenamed = true;
9757 configFilePrev = configFile;
9758 configFilePrev += "-prev";
9759 newConfigFilePrev = newConfigFile;
9760 newConfigFilePrev += "-prev";
9761 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9762 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9763 if (NVRAMFile.isNotEmpty())
9764 {
9765 // in the NVRAM file path, replace the old directory with the new directory
9766 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9767 {
9768 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9769 NVRAMFile = newConfigDir + strNVRAMFile;
9770 }
9771 newNVRAMFile = newConfigFile;
9772 newNVRAMFile.stripSuffix();
9773 newNVRAMFile += ".nvram";
9774 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9775 }
9776 }
9777 }
9778
9779 // update m_strConfigFileFull amd mConfigFile
9780 mData->m_strConfigFileFull = newConfigFile;
9781 // compute the relative path too
9782 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9783
9784 // store the old and new so that VirtualBox::i_saveSettings() can update
9785 // the media registry
9786 if ( mData->mRegistered
9787 && (configDir != newConfigDir || configFile != newConfigFile))
9788 {
9789 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9790
9791 if (pfNeedsGlobalSaveSettings)
9792 *pfNeedsGlobalSaveSettings = true;
9793 }
9794
9795 // in the saved state file path, replace the old directory with the new directory
9796 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9797 {
9798 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9799 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9800 }
9801 if (newNVRAMFile.isNotEmpty())
9802 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9803
9804 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9805 if (mData->mFirstSnapshot)
9806 {
9807 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9808 newConfigDir.c_str());
9809 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9810 newConfigDir.c_str());
9811 }
9812 }
9813 while (0);
9814
9815 if (FAILED(rc))
9816 {
9817 /* silently try to rename everything back */
9818 if (fileRenamed)
9819 {
9820 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9821 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9822 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9823 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9824 }
9825 if (dirRenamed)
9826 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9827 }
9828
9829 if (FAILED(rc)) return rc;
9830 }
9831
9832 if (fSettingsFileIsNew)
9833 {
9834 /* create a virgin config file */
9835 int vrc = VINF_SUCCESS;
9836
9837 /* ensure the settings directory exists */
9838 Utf8Str path(mData->m_strConfigFileFull);
9839 path.stripFilename();
9840 if (!RTDirExists(path.c_str()))
9841 {
9842 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9843 if (RT_FAILURE(vrc))
9844 {
9845 return setErrorBoth(E_FAIL, vrc,
9846 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9847 path.c_str(),
9848 vrc);
9849 }
9850 }
9851
9852 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9853 path = mData->m_strConfigFileFull;
9854 RTFILE f = NIL_RTFILE;
9855 vrc = RTFileOpen(&f, path.c_str(),
9856 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9857 if (RT_FAILURE(vrc))
9858 return setErrorBoth(E_FAIL, vrc,
9859 tr("Could not create the settings file '%s' (%Rrc)"),
9860 path.c_str(),
9861 vrc);
9862 RTFileClose(f);
9863 }
9864 if (pfSettingsFileIsNew)
9865 *pfSettingsFileIsNew = fSettingsFileIsNew;
9866
9867 return rc;
9868}
9869
9870/**
9871 * Saves and commits machine data, user data and hardware data.
9872 *
9873 * Note that on failure, the data remains uncommitted.
9874 *
9875 * @a aFlags may combine the following flags:
9876 *
9877 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9878 * Used when saving settings after an operation that makes them 100%
9879 * correspond to the settings from the current snapshot.
9880 * - SaveS_Force: settings will be saved without doing a deep compare of the
9881 * settings structures. This is used when this is called because snapshots
9882 * have changed to avoid the overhead of the deep compare.
9883 *
9884 * @note Must be called from under this object's write lock. Locks children for
9885 * writing.
9886 *
9887 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9888 * initialized to false and that will be set to true by this function if
9889 * the caller must invoke VirtualBox::i_saveSettings() because the global
9890 * settings have changed. This will happen if a machine rename has been
9891 * saved and the global machine and media registries will therefore need
9892 * updating.
9893 * @param alock Reference to the lock for this machine object.
9894 * @param aFlags Flags.
9895 */
9896HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9897 AutoWriteLock &alock,
9898 int aFlags /*= 0*/)
9899{
9900 LogFlowThisFuncEnter();
9901
9902 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9903
9904 /* make sure child objects are unable to modify the settings while we are
9905 * saving them */
9906 i_ensureNoStateDependencies(alock);
9907
9908 AssertReturn(!i_isSnapshotMachine(),
9909 E_FAIL);
9910
9911 if (!mData->mAccessible)
9912 return setError(VBOX_E_INVALID_VM_STATE,
9913 tr("The machine is not accessible, so cannot save settings"));
9914
9915 HRESULT rc = S_OK;
9916 bool fNeedsWrite = false;
9917 bool fSettingsFileIsNew = false;
9918
9919 /* First, prepare to save settings. It will care about renaming the
9920 * settings directory and file if the machine name was changed and about
9921 * creating a new settings file if this is a new machine. */
9922 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
9923 &fSettingsFileIsNew);
9924 if (FAILED(rc)) return rc;
9925
9926 // keep a pointer to the current settings structures
9927 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9928 settings::MachineConfigFile *pNewConfig = NULL;
9929
9930 try
9931 {
9932 // make a fresh one to have everyone write stuff into
9933 pNewConfig = new settings::MachineConfigFile(NULL);
9934 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9935
9936 // now go and copy all the settings data from COM to the settings structures
9937 // (this calls i_saveSettings() on all the COM objects in the machine)
9938 i_copyMachineDataToSettings(*pNewConfig);
9939
9940 if (aFlags & SaveS_ResetCurStateModified)
9941 {
9942 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9943 mData->mCurrentStateModified = FALSE;
9944 fNeedsWrite = true; // always, no need to compare
9945 }
9946 else if (aFlags & SaveS_Force)
9947 {
9948 fNeedsWrite = true; // always, no need to compare
9949 }
9950 else
9951 {
9952 if (!mData->mCurrentStateModified)
9953 {
9954 // do a deep compare of the settings that we just saved with the settings
9955 // previously stored in the config file; this invokes MachineConfigFile::operator==
9956 // which does a deep compare of all the settings, which is expensive but less expensive
9957 // than writing out XML in vain
9958 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9959
9960 // could still be modified if any settings changed
9961 mData->mCurrentStateModified = fAnySettingsChanged;
9962
9963 fNeedsWrite = fAnySettingsChanged;
9964 }
9965 else
9966 fNeedsWrite = true;
9967 }
9968
9969 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9970
9971 if (fNeedsWrite)
9972 // now spit it all out!
9973 pNewConfig->write(mData->m_strConfigFileFull);
9974
9975 mData->pMachineConfigFile = pNewConfig;
9976 delete pOldConfig;
9977 i_commit();
9978
9979 // after saving settings, we are no longer different from the XML on disk
9980 mData->flModifications = 0;
9981 }
9982 catch (HRESULT err)
9983 {
9984 // we assume that error info is set by the thrower
9985 rc = err;
9986
9987 // delete any newly created settings file
9988 if (fSettingsFileIsNew)
9989 RTFileDelete(mData->m_strConfigFileFull.c_str());
9990
9991 // restore old config
9992 delete pNewConfig;
9993 mData->pMachineConfigFile = pOldConfig;
9994 }
9995 catch (...)
9996 {
9997 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9998 }
9999
10000 if (fNeedsWrite)
10001 {
10002 /* Fire the data change event, even on failure (since we've already
10003 * committed all data). This is done only for SessionMachines because
10004 * mutable Machine instances are always not registered (i.e. private
10005 * to the client process that creates them) and thus don't need to
10006 * inform callbacks. */
10007 if (i_isSessionMachine())
10008 mParent->i_onMachineDataChanged(mData->mUuid);
10009 }
10010
10011 LogFlowThisFunc(("rc=%08X\n", rc));
10012 LogFlowThisFuncLeave();
10013 return rc;
10014}
10015
10016/**
10017 * Implementation for saving the machine settings into the given
10018 * settings::MachineConfigFile instance. This copies machine extradata
10019 * from the previous machine config file in the instance data, if any.
10020 *
10021 * This gets called from two locations:
10022 *
10023 * -- Machine::i_saveSettings(), during the regular XML writing;
10024 *
10025 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10026 * exported to OVF and we write the VirtualBox proprietary XML
10027 * into a <vbox:Machine> tag.
10028 *
10029 * This routine fills all the fields in there, including snapshots, *except*
10030 * for the following:
10031 *
10032 * -- fCurrentStateModified. There is some special logic associated with that.
10033 *
10034 * The caller can then call MachineConfigFile::write() or do something else
10035 * with it.
10036 *
10037 * Caller must hold the machine lock!
10038 *
10039 * This throws XML errors and HRESULT, so the caller must have a catch block!
10040 */
10041void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10042{
10043 // deep copy extradata, being extra careful with self assignment (the STL
10044 // map assignment on Mac OS X clang based Xcode isn't checking)
10045 if (&config != mData->pMachineConfigFile)
10046 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10047
10048 config.uuid = mData->mUuid;
10049
10050 // copy name, description, OS type, teleport, UTC etc.
10051 config.machineUserData = mUserData->s;
10052
10053 if ( mData->mMachineState == MachineState_Saved
10054 || mData->mMachineState == MachineState_AbortedSaved
10055 || mData->mMachineState == MachineState_Restoring
10056 // when doing certain snapshot operations we may or may not have
10057 // a saved state in the current state, so keep everything as is
10058 || ( ( mData->mMachineState == MachineState_Snapshotting
10059 || mData->mMachineState == MachineState_DeletingSnapshot
10060 || mData->mMachineState == MachineState_RestoringSnapshot)
10061 && (!mSSData->strStateFilePath.isEmpty())
10062 )
10063 )
10064 {
10065 Assert(!mSSData->strStateFilePath.isEmpty());
10066 /* try to make the file name relative to the settings file dir */
10067 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10068 }
10069 else
10070 {
10071 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10072 config.strStateFile.setNull();
10073 }
10074
10075 if (mData->mCurrentSnapshot)
10076 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10077 else
10078 config.uuidCurrentSnapshot.clear();
10079
10080 config.timeLastStateChange = mData->mLastStateChange;
10081 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10082 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10083
10084 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10085 if (FAILED(rc)) throw rc;
10086
10087 // save machine's media registry if this is VirtualBox 4.0 or later
10088 if (config.canHaveOwnMediaRegistry())
10089 {
10090 // determine machine folder
10091 Utf8Str strMachineFolder = i_getSettingsFileFull();
10092 strMachineFolder.stripFilename();
10093 mParent->i_saveMediaRegistry(config.mediaRegistry,
10094 i_getId(), // only media with registry ID == machine UUID
10095 strMachineFolder);
10096 // this throws HRESULT
10097 }
10098
10099 // save snapshots
10100 rc = i_saveAllSnapshots(config);
10101 if (FAILED(rc)) throw rc;
10102}
10103
10104/**
10105 * Saves all snapshots of the machine into the given machine config file. Called
10106 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10107 * @param config
10108 * @return
10109 */
10110HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10111{
10112 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10113
10114 HRESULT rc = S_OK;
10115
10116 try
10117 {
10118 config.llFirstSnapshot.clear();
10119
10120 if (mData->mFirstSnapshot)
10121 {
10122 // the settings use a list for "the first snapshot"
10123 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10124
10125 // get reference to the snapshot on the list and work on that
10126 // element straight in the list to avoid excessive copying later
10127 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10128 if (FAILED(rc)) throw rc;
10129 }
10130
10131// if (mType == IsSessionMachine)
10132// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10133
10134 }
10135 catch (HRESULT err)
10136 {
10137 /* we assume that error info is set by the thrower */
10138 rc = err;
10139 }
10140 catch (...)
10141 {
10142 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10143 }
10144
10145 return rc;
10146}
10147
10148/**
10149 * Saves the VM hardware configuration. It is assumed that the
10150 * given node is empty.
10151 *
10152 * @param data Reference to the settings object for the hardware config.
10153 * @param pDbg Pointer to the settings object for the debugging config
10154 * which happens to live in mHWData.
10155 * @param pAutostart Pointer to the settings object for the autostart config
10156 * which happens to live in mHWData.
10157 */
10158HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10159 settings::Autostart *pAutostart)
10160{
10161 HRESULT rc = S_OK;
10162
10163 try
10164 {
10165 /* The hardware version attribute (optional).
10166 Automatically upgrade from 1 to current default hardware version
10167 when there is no saved state. (ugly!) */
10168 if ( mHWData->mHWVersion == "1"
10169 && mSSData->strStateFilePath.isEmpty()
10170 )
10171 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10172
10173 data.strVersion = mHWData->mHWVersion;
10174 data.uuid = mHWData->mHardwareUUID;
10175
10176 // CPU
10177 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10178 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10179 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10180 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10181 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10182 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10183 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10184 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10185 data.fPAE = !!mHWData->mPAEEnabled;
10186 data.enmLongMode = mHWData->mLongMode;
10187 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10188 data.fAPIC = !!mHWData->mAPIC;
10189 data.fX2APIC = !!mHWData->mX2APIC;
10190 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10191 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10192 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10193 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10194 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10195 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10196 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10197 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10198 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10199 data.cCPUs = mHWData->mCPUCount;
10200 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10201 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10202 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10203 data.strCpuProfile = mHWData->mCpuProfile;
10204
10205 data.llCpus.clear();
10206 if (data.fCpuHotPlug)
10207 {
10208 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10209 {
10210 if (mHWData->mCPUAttached[idx])
10211 {
10212 settings::Cpu cpu;
10213 cpu.ulId = idx;
10214 data.llCpus.push_back(cpu);
10215 }
10216 }
10217 }
10218
10219 /* Standard and Extended CPUID leafs. */
10220 data.llCpuIdLeafs.clear();
10221 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10222
10223 // memory
10224 data.ulMemorySizeMB = mHWData->mMemorySize;
10225 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10226
10227 // firmware
10228 data.firmwareType = mHWData->mFirmwareType;
10229
10230 // HID
10231 data.pointingHIDType = mHWData->mPointingHIDType;
10232 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10233
10234 // chipset
10235 data.chipsetType = mHWData->mChipsetType;
10236
10237 // iommu
10238 data.iommuType = mHWData->mIommuType;
10239
10240 // paravirt
10241 data.paravirtProvider = mHWData->mParavirtProvider;
10242 data.strParavirtDebug = mHWData->mParavirtDebug;
10243
10244 // emulated USB card reader
10245 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10246
10247 // HPET
10248 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10249
10250 // boot order
10251 data.mapBootOrder.clear();
10252 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10253 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10254
10255 /* VRDEServer settings (optional) */
10256 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10257 if (FAILED(rc)) throw rc;
10258
10259 /* BIOS settings (required) */
10260 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10261 if (FAILED(rc)) throw rc;
10262
10263 /* Trusted Platform Module settings (required) */
10264 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10265 if (FAILED(rc)) throw rc;
10266
10267 /* NVRAM settings (required) */
10268 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10269 if (FAILED(rc)) throw rc;
10270
10271 /* Recording settings (required) */
10272 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10273 if (FAILED(rc)) throw rc;
10274
10275 /* GraphicsAdapter settings (required) */
10276 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10277 if (FAILED(rc)) throw rc;
10278
10279 /* USB Controller (required) */
10280 data.usbSettings.llUSBControllers.clear();
10281 for (USBControllerList::const_iterator
10282 it = mUSBControllers->begin();
10283 it != mUSBControllers->end();
10284 ++it)
10285 {
10286 ComObjPtr<USBController> ctrl = *it;
10287 settings::USBController settingsCtrl;
10288
10289 settingsCtrl.strName = ctrl->i_getName();
10290 settingsCtrl.enmType = ctrl->i_getControllerType();
10291
10292 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10293 }
10294
10295 /* USB device filters (required) */
10296 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10297 if (FAILED(rc)) throw rc;
10298
10299 /* Network adapters (required) */
10300 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10301 data.llNetworkAdapters.clear();
10302 /* Write out only the nominal number of network adapters for this
10303 * chipset type. Since Machine::commit() hasn't been called there
10304 * may be extra NIC settings in the vector. */
10305 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10306 {
10307 settings::NetworkAdapter nic;
10308 nic.ulSlot = (uint32_t)slot;
10309 /* paranoia check... must not be NULL, but must not crash either. */
10310 if (mNetworkAdapters[slot])
10311 {
10312 if (mNetworkAdapters[slot]->i_hasDefaults())
10313 continue;
10314
10315 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10316 if (FAILED(rc)) throw rc;
10317
10318 data.llNetworkAdapters.push_back(nic);
10319 }
10320 }
10321
10322 /* Serial ports */
10323 data.llSerialPorts.clear();
10324 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10325 {
10326 if (mSerialPorts[slot]->i_hasDefaults())
10327 continue;
10328
10329 settings::SerialPort s;
10330 s.ulSlot = slot;
10331 rc = mSerialPorts[slot]->i_saveSettings(s);
10332 if (FAILED(rc)) return rc;
10333
10334 data.llSerialPorts.push_back(s);
10335 }
10336
10337 /* Parallel ports */
10338 data.llParallelPorts.clear();
10339 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10340 {
10341 if (mParallelPorts[slot]->i_hasDefaults())
10342 continue;
10343
10344 settings::ParallelPort p;
10345 p.ulSlot = slot;
10346 rc = mParallelPorts[slot]->i_saveSettings(p);
10347 if (FAILED(rc)) return rc;
10348
10349 data.llParallelPorts.push_back(p);
10350 }
10351
10352 /* Audio adapter */
10353 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10354 if (FAILED(rc)) return rc;
10355
10356 rc = i_saveStorageControllers(data.storage);
10357 if (FAILED(rc)) return rc;
10358
10359 /* Shared folders */
10360 data.llSharedFolders.clear();
10361 for (HWData::SharedFolderList::const_iterator
10362 it = mHWData->mSharedFolders.begin();
10363 it != mHWData->mSharedFolders.end();
10364 ++it)
10365 {
10366 SharedFolder *pSF = *it;
10367 AutoCaller sfCaller(pSF);
10368 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10369 settings::SharedFolder sf;
10370 sf.strName = pSF->i_getName();
10371 sf.strHostPath = pSF->i_getHostPath();
10372 sf.fWritable = !!pSF->i_isWritable();
10373 sf.fAutoMount = !!pSF->i_isAutoMounted();
10374 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10375
10376 data.llSharedFolders.push_back(sf);
10377 }
10378
10379 // clipboard
10380 data.clipboardMode = mHWData->mClipboardMode;
10381 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10382
10383 // drag'n'drop
10384 data.dndMode = mHWData->mDnDMode;
10385
10386 /* Guest */
10387 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10388
10389 // IO settings
10390 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10391 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10392
10393 /* BandwidthControl (required) */
10394 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10395 if (FAILED(rc)) throw rc;
10396
10397 /* Host PCI devices */
10398 data.pciAttachments.clear();
10399 for (HWData::PCIDeviceAssignmentList::const_iterator
10400 it = mHWData->mPCIDeviceAssignments.begin();
10401 it != mHWData->mPCIDeviceAssignments.end();
10402 ++it)
10403 {
10404 ComObjPtr<PCIDeviceAttachment> pda = *it;
10405 settings::HostPCIDeviceAttachment hpda;
10406
10407 rc = pda->i_saveSettings(hpda);
10408 if (FAILED(rc)) throw rc;
10409
10410 data.pciAttachments.push_back(hpda);
10411 }
10412
10413 // guest properties
10414 data.llGuestProperties.clear();
10415#ifdef VBOX_WITH_GUEST_PROPS
10416 for (HWData::GuestPropertyMap::const_iterator
10417 it = mHWData->mGuestProperties.begin();
10418 it != mHWData->mGuestProperties.end();
10419 ++it)
10420 {
10421 HWData::GuestProperty property = it->second;
10422
10423 /* Remove transient guest properties at shutdown unless we
10424 * are saving state. Note that restoring snapshot intentionally
10425 * keeps them, they will be removed if appropriate once the final
10426 * machine state is set (as crashes etc. need to work). */
10427 if ( ( mData->mMachineState == MachineState_PoweredOff
10428 || mData->mMachineState == MachineState_Aborted
10429 || mData->mMachineState == MachineState_Teleported)
10430 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10431 continue;
10432 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10433 prop.strName = it->first;
10434 prop.strValue = property.strValue;
10435 prop.timestamp = (uint64_t)property.mTimestamp;
10436 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10437 GuestPropWriteFlags(property.mFlags, szFlags);
10438 prop.strFlags = szFlags;
10439
10440 data.llGuestProperties.push_back(prop);
10441 }
10442
10443 /* I presume this doesn't require a backup(). */
10444 mData->mGuestPropertiesModified = FALSE;
10445#endif /* VBOX_WITH_GUEST_PROPS defined */
10446
10447 *pDbg = mHWData->mDebugging;
10448 *pAutostart = mHWData->mAutostart;
10449
10450 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10451 }
10452 catch (std::bad_alloc &)
10453 {
10454 return E_OUTOFMEMORY;
10455 }
10456
10457 AssertComRC(rc);
10458 return rc;
10459}
10460
10461/**
10462 * Saves the storage controller configuration.
10463 *
10464 * @param data storage settings.
10465 */
10466HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10467{
10468 data.llStorageControllers.clear();
10469
10470 for (StorageControllerList::const_iterator
10471 it = mStorageControllers->begin();
10472 it != mStorageControllers->end();
10473 ++it)
10474 {
10475 HRESULT rc;
10476 ComObjPtr<StorageController> pCtl = *it;
10477
10478 settings::StorageController ctl;
10479 ctl.strName = pCtl->i_getName();
10480 ctl.controllerType = pCtl->i_getControllerType();
10481 ctl.storageBus = pCtl->i_getStorageBus();
10482 ctl.ulInstance = pCtl->i_getInstance();
10483 ctl.fBootable = pCtl->i_getBootable();
10484
10485 /* Save the port count. */
10486 ULONG portCount;
10487 rc = pCtl->COMGETTER(PortCount)(&portCount);
10488 ComAssertComRCRet(rc, rc);
10489 ctl.ulPortCount = portCount;
10490
10491 /* Save fUseHostIOCache */
10492 BOOL fUseHostIOCache;
10493 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10494 ComAssertComRCRet(rc, rc);
10495 ctl.fUseHostIOCache = !!fUseHostIOCache;
10496
10497 /* save the devices now. */
10498 rc = i_saveStorageDevices(pCtl, ctl);
10499 ComAssertComRCRet(rc, rc);
10500
10501 data.llStorageControllers.push_back(ctl);
10502 }
10503
10504 return S_OK;
10505}
10506
10507/**
10508 * Saves the hard disk configuration.
10509 */
10510HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10511 settings::StorageController &data)
10512{
10513 MediumAttachmentList atts;
10514
10515 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10516 if (FAILED(rc)) return rc;
10517
10518 data.llAttachedDevices.clear();
10519 for (MediumAttachmentList::const_iterator
10520 it = atts.begin();
10521 it != atts.end();
10522 ++it)
10523 {
10524 settings::AttachedDevice dev;
10525 IMediumAttachment *iA = *it;
10526 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10527 Medium *pMedium = pAttach->i_getMedium();
10528
10529 dev.deviceType = pAttach->i_getType();
10530 dev.lPort = pAttach->i_getPort();
10531 dev.lDevice = pAttach->i_getDevice();
10532 dev.fPassThrough = pAttach->i_getPassthrough();
10533 dev.fHotPluggable = pAttach->i_getHotPluggable();
10534 if (pMedium)
10535 {
10536 if (pMedium->i_isHostDrive())
10537 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10538 else
10539 dev.uuid = pMedium->i_getId();
10540 dev.fTempEject = pAttach->i_getTempEject();
10541 dev.fNonRotational = pAttach->i_getNonRotational();
10542 dev.fDiscard = pAttach->i_getDiscard();
10543 }
10544
10545 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10546
10547 data.llAttachedDevices.push_back(dev);
10548 }
10549
10550 return S_OK;
10551}
10552
10553/**
10554 * Saves machine state settings as defined by aFlags
10555 * (SaveSTS_* values).
10556 *
10557 * @param aFlags Combination of SaveSTS_* flags.
10558 *
10559 * @note Locks objects for writing.
10560 */
10561HRESULT Machine::i_saveStateSettings(int aFlags)
10562{
10563 if (aFlags == 0)
10564 return S_OK;
10565
10566 AutoCaller autoCaller(this);
10567 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10568
10569 /* This object's write lock is also necessary to serialize file access
10570 * (prevent concurrent reads and writes) */
10571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10572
10573 HRESULT rc = S_OK;
10574
10575 Assert(mData->pMachineConfigFile);
10576
10577 try
10578 {
10579 if (aFlags & SaveSTS_CurStateModified)
10580 mData->pMachineConfigFile->fCurrentStateModified = true;
10581
10582 if (aFlags & SaveSTS_StateFilePath)
10583 {
10584 if (!mSSData->strStateFilePath.isEmpty())
10585 /* try to make the file name relative to the settings file dir */
10586 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10587 else
10588 mData->pMachineConfigFile->strStateFile.setNull();
10589 }
10590
10591 if (aFlags & SaveSTS_StateTimeStamp)
10592 {
10593 Assert( mData->mMachineState != MachineState_Aborted
10594 || mSSData->strStateFilePath.isEmpty());
10595
10596 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10597
10598 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10599 || mData->mMachineState == MachineState_AbortedSaved);
10600/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10601 }
10602
10603 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10604 }
10605 catch (...)
10606 {
10607 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10608 }
10609
10610 return rc;
10611}
10612
10613/**
10614 * Ensures that the given medium is added to a media registry. If this machine
10615 * was created with 4.0 or later, then the machine registry is used. Otherwise
10616 * the global VirtualBox media registry is used.
10617 *
10618 * Caller must NOT hold machine lock, media tree or any medium locks!
10619 *
10620 * @param pMedium
10621 */
10622void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10623{
10624 /* Paranoia checks: do not hold machine or media tree locks. */
10625 AssertReturnVoid(!isWriteLockOnCurrentThread());
10626 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10627
10628 ComObjPtr<Medium> pBase;
10629 {
10630 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10631 pBase = pMedium->i_getBase();
10632 }
10633
10634 /* Paranoia checks: do not hold medium locks. */
10635 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10636 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10637
10638 // decide which medium registry to use now that the medium is attached:
10639 Guid uuid;
10640 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10641 if (fCanHaveOwnMediaRegistry)
10642 // machine XML is VirtualBox 4.0 or higher:
10643 uuid = i_getId(); // machine UUID
10644 else
10645 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10646
10647 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10648 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10649 if (pMedium->i_addRegistry(uuid))
10650 mParent->i_markRegistryModified(uuid);
10651
10652 /* For more complex hard disk structures it can happen that the base
10653 * medium isn't yet associated with any medium registry. Do that now. */
10654 if (pMedium != pBase)
10655 {
10656 /* Tree lock needed by Medium::addRegistry when recursing. */
10657 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10658 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10659 {
10660 treeLock.release();
10661 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10662 treeLock.acquire();
10663 }
10664 if (pBase->i_addRegistryRecursive(uuid))
10665 {
10666 treeLock.release();
10667 mParent->i_markRegistryModified(uuid);
10668 }
10669 }
10670}
10671
10672/**
10673 * Creates differencing hard disks for all normal hard disks attached to this
10674 * machine and a new set of attachments to refer to created disks.
10675 *
10676 * Used when taking a snapshot or when deleting the current state. Gets called
10677 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10678 *
10679 * This method assumes that mMediumAttachments contains the original hard disk
10680 * attachments it needs to create diffs for. On success, these attachments will
10681 * be replaced with the created diffs.
10682 *
10683 * Attachments with non-normal hard disks are left as is.
10684 *
10685 * If @a aOnline is @c false then the original hard disks that require implicit
10686 * diffs will be locked for reading. Otherwise it is assumed that they are
10687 * already locked for writing (when the VM was started). Note that in the latter
10688 * case it is responsibility of the caller to lock the newly created diffs for
10689 * writing if this method succeeds.
10690 *
10691 * @param aProgress Progress object to run (must contain at least as
10692 * many operations left as the number of hard disks
10693 * attached).
10694 * @param aWeight Weight of this operation.
10695 * @param aOnline Whether the VM was online prior to this operation.
10696 *
10697 * @note The progress object is not marked as completed, neither on success nor
10698 * on failure. This is a responsibility of the caller.
10699 *
10700 * @note Locks this object and the media tree for writing.
10701 */
10702HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10703 ULONG aWeight,
10704 bool aOnline)
10705{
10706 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10707
10708 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10709 AssertReturn(!!pProgressControl, E_INVALIDARG);
10710
10711 AutoCaller autoCaller(this);
10712 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10713
10714 AutoMultiWriteLock2 alock(this->lockHandle(),
10715 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10716
10717 /* must be in a protective state because we release the lock below */
10718 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10719 || mData->mMachineState == MachineState_OnlineSnapshotting
10720 || mData->mMachineState == MachineState_LiveSnapshotting
10721 || mData->mMachineState == MachineState_RestoringSnapshot
10722 || mData->mMachineState == MachineState_DeletingSnapshot
10723 , E_FAIL);
10724
10725 HRESULT rc = S_OK;
10726
10727 // use appropriate locked media map (online or offline)
10728 MediumLockListMap lockedMediaOffline;
10729 MediumLockListMap *lockedMediaMap;
10730 if (aOnline)
10731 lockedMediaMap = &mData->mSession.mLockedMedia;
10732 else
10733 lockedMediaMap = &lockedMediaOffline;
10734
10735 try
10736 {
10737 if (!aOnline)
10738 {
10739 /* lock all attached hard disks early to detect "in use"
10740 * situations before creating actual diffs */
10741 for (MediumAttachmentList::const_iterator
10742 it = mMediumAttachments->begin();
10743 it != mMediumAttachments->end();
10744 ++it)
10745 {
10746 MediumAttachment *pAtt = *it;
10747 if (pAtt->i_getType() == DeviceType_HardDisk)
10748 {
10749 Medium *pMedium = pAtt->i_getMedium();
10750 Assert(pMedium);
10751
10752 MediumLockList *pMediumLockList(new MediumLockList());
10753 alock.release();
10754 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10755 NULL /* pToLockWrite */,
10756 false /* fMediumLockWriteAll */,
10757 NULL,
10758 *pMediumLockList);
10759 alock.acquire();
10760 if (FAILED(rc))
10761 {
10762 delete pMediumLockList;
10763 throw rc;
10764 }
10765 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10766 if (FAILED(rc))
10767 {
10768 throw setError(rc,
10769 tr("Collecting locking information for all attached media failed"));
10770 }
10771 }
10772 }
10773
10774 /* Now lock all media. If this fails, nothing is locked. */
10775 alock.release();
10776 rc = lockedMediaMap->Lock();
10777 alock.acquire();
10778 if (FAILED(rc))
10779 {
10780 throw setError(rc,
10781 tr("Locking of attached media failed"));
10782 }
10783 }
10784
10785 /* remember the current list (note that we don't use backup() since
10786 * mMediumAttachments may be already backed up) */
10787 MediumAttachmentList atts = *mMediumAttachments.data();
10788
10789 /* start from scratch */
10790 mMediumAttachments->clear();
10791
10792 /* go through remembered attachments and create diffs for normal hard
10793 * disks and attach them */
10794 for (MediumAttachmentList::const_iterator
10795 it = atts.begin();
10796 it != atts.end();
10797 ++it)
10798 {
10799 MediumAttachment *pAtt = *it;
10800
10801 DeviceType_T devType = pAtt->i_getType();
10802 Medium *pMedium = pAtt->i_getMedium();
10803
10804 if ( devType != DeviceType_HardDisk
10805 || pMedium == NULL
10806 || pMedium->i_getType() != MediumType_Normal)
10807 {
10808 /* copy the attachment as is */
10809
10810 /** @todo the progress object created in SessionMachine::TakeSnaphot
10811 * only expects operations for hard disks. Later other
10812 * device types need to show up in the progress as well. */
10813 if (devType == DeviceType_HardDisk)
10814 {
10815 if (pMedium == NULL)
10816 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10817 aWeight); // weight
10818 else
10819 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10820 pMedium->i_getBase()->i_getName().c_str()).raw(),
10821 aWeight); // weight
10822 }
10823
10824 mMediumAttachments->push_back(pAtt);
10825 continue;
10826 }
10827
10828 /* need a diff */
10829 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10830 pMedium->i_getBase()->i_getName().c_str()).raw(),
10831 aWeight); // weight
10832
10833 Utf8Str strFullSnapshotFolder;
10834 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10835
10836 ComObjPtr<Medium> diff;
10837 diff.createObject();
10838 // store the diff in the same registry as the parent
10839 // (this cannot fail here because we can't create implicit diffs for
10840 // unregistered images)
10841 Guid uuidRegistryParent;
10842 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10843 Assert(fInRegistry); NOREF(fInRegistry);
10844 rc = diff->init(mParent,
10845 pMedium->i_getPreferredDiffFormat(),
10846 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10847 uuidRegistryParent,
10848 DeviceType_HardDisk);
10849 if (FAILED(rc)) throw rc;
10850
10851 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10852 * the push_back? Looks like we're going to release medium with the
10853 * wrong kind of lock (general issue with if we fail anywhere at all)
10854 * and an orphaned VDI in the snapshots folder. */
10855
10856 /* update the appropriate lock list */
10857 MediumLockList *pMediumLockList;
10858 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10859 AssertComRCThrowRC(rc);
10860 if (aOnline)
10861 {
10862 alock.release();
10863 /* The currently attached medium will be read-only, change
10864 * the lock type to read. */
10865 rc = pMediumLockList->Update(pMedium, false);
10866 alock.acquire();
10867 AssertComRCThrowRC(rc);
10868 }
10869
10870 /* release the locks before the potentially lengthy operation */
10871 alock.release();
10872 rc = pMedium->i_createDiffStorage(diff,
10873 pMedium->i_getPreferredDiffVariant(),
10874 pMediumLockList,
10875 NULL /* aProgress */,
10876 true /* aWait */,
10877 false /* aNotify */);
10878 alock.acquire();
10879 if (FAILED(rc)) throw rc;
10880
10881 /* actual lock list update is done in Machine::i_commitMedia */
10882
10883 rc = diff->i_addBackReference(mData->mUuid);
10884 AssertComRCThrowRC(rc);
10885
10886 /* add a new attachment */
10887 ComObjPtr<MediumAttachment> attachment;
10888 attachment.createObject();
10889 rc = attachment->init(this,
10890 diff,
10891 pAtt->i_getControllerName(),
10892 pAtt->i_getPort(),
10893 pAtt->i_getDevice(),
10894 DeviceType_HardDisk,
10895 true /* aImplicit */,
10896 false /* aPassthrough */,
10897 false /* aTempEject */,
10898 pAtt->i_getNonRotational(),
10899 pAtt->i_getDiscard(),
10900 pAtt->i_getHotPluggable(),
10901 pAtt->i_getBandwidthGroup());
10902 if (FAILED(rc)) throw rc;
10903
10904 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10905 AssertComRCThrowRC(rc);
10906 mMediumAttachments->push_back(attachment);
10907 }
10908 }
10909 catch (HRESULT aRC) { rc = aRC; }
10910
10911 /* unlock all hard disks we locked when there is no VM */
10912 if (!aOnline)
10913 {
10914 ErrorInfoKeeper eik;
10915
10916 HRESULT rc1 = lockedMediaMap->Clear();
10917 AssertComRC(rc1);
10918 }
10919
10920 return rc;
10921}
10922
10923/**
10924 * Deletes implicit differencing hard disks created either by
10925 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10926 * mMediumAttachments.
10927 *
10928 * Note that to delete hard disks created by #attachDevice() this method is
10929 * called from #i_rollbackMedia() when the changes are rolled back.
10930 *
10931 * @note Locks this object and the media tree for writing.
10932 */
10933HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10934{
10935 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10936
10937 AutoCaller autoCaller(this);
10938 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10939
10940 AutoMultiWriteLock2 alock(this->lockHandle(),
10941 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10942
10943 /* We absolutely must have backed up state. */
10944 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10945
10946 /* Check if there are any implicitly created diff images. */
10947 bool fImplicitDiffs = false;
10948 for (MediumAttachmentList::const_iterator
10949 it = mMediumAttachments->begin();
10950 it != mMediumAttachments->end();
10951 ++it)
10952 {
10953 const ComObjPtr<MediumAttachment> &pAtt = *it;
10954 if (pAtt->i_isImplicit())
10955 {
10956 fImplicitDiffs = true;
10957 break;
10958 }
10959 }
10960 /* If there is nothing to do, leave early. This saves lots of image locking
10961 * effort. It also avoids a MachineStateChanged event without real reason.
10962 * This is important e.g. when loading a VM config, because there should be
10963 * no events. Otherwise API clients can become thoroughly confused for
10964 * inaccessible VMs (the code for loading VM configs uses this method for
10965 * cleanup if the config makes no sense), as they take such events as an
10966 * indication that the VM is alive, and they would force the VM config to
10967 * be reread, leading to an endless loop. */
10968 if (!fImplicitDiffs)
10969 return S_OK;
10970
10971 HRESULT rc = S_OK;
10972 MachineState_T oldState = mData->mMachineState;
10973
10974 /* will release the lock before the potentially lengthy operation,
10975 * so protect with the special state (unless already protected) */
10976 if ( oldState != MachineState_Snapshotting
10977 && oldState != MachineState_OnlineSnapshotting
10978 && oldState != MachineState_LiveSnapshotting
10979 && oldState != MachineState_RestoringSnapshot
10980 && oldState != MachineState_DeletingSnapshot
10981 && oldState != MachineState_DeletingSnapshotOnline
10982 && oldState != MachineState_DeletingSnapshotPaused
10983 )
10984 i_setMachineState(MachineState_SettingUp);
10985
10986 // use appropriate locked media map (online or offline)
10987 MediumLockListMap lockedMediaOffline;
10988 MediumLockListMap *lockedMediaMap;
10989 if (aOnline)
10990 lockedMediaMap = &mData->mSession.mLockedMedia;
10991 else
10992 lockedMediaMap = &lockedMediaOffline;
10993
10994 try
10995 {
10996 if (!aOnline)
10997 {
10998 /* lock all attached hard disks early to detect "in use"
10999 * situations before deleting actual diffs */
11000 for (MediumAttachmentList::const_iterator
11001 it = mMediumAttachments->begin();
11002 it != mMediumAttachments->end();
11003 ++it)
11004 {
11005 MediumAttachment *pAtt = *it;
11006 if (pAtt->i_getType() == DeviceType_HardDisk)
11007 {
11008 Medium *pMedium = pAtt->i_getMedium();
11009 Assert(pMedium);
11010
11011 MediumLockList *pMediumLockList(new MediumLockList());
11012 alock.release();
11013 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11014 NULL /* pToLockWrite */,
11015 false /* fMediumLockWriteAll */,
11016 NULL,
11017 *pMediumLockList);
11018 alock.acquire();
11019
11020 if (FAILED(rc))
11021 {
11022 delete pMediumLockList;
11023 throw rc;
11024 }
11025
11026 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11027 if (FAILED(rc))
11028 throw rc;
11029 }
11030 }
11031
11032 if (FAILED(rc))
11033 throw rc;
11034 } // end of offline
11035
11036 /* Lock lists are now up to date and include implicitly created media */
11037
11038 /* Go through remembered attachments and delete all implicitly created
11039 * diffs and fix up the attachment information */
11040 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11041 MediumAttachmentList implicitAtts;
11042 for (MediumAttachmentList::const_iterator
11043 it = mMediumAttachments->begin();
11044 it != mMediumAttachments->end();
11045 ++it)
11046 {
11047 ComObjPtr<MediumAttachment> pAtt = *it;
11048 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11049 if (pMedium.isNull())
11050 continue;
11051
11052 // Implicit attachments go on the list for deletion and back references are removed.
11053 if (pAtt->i_isImplicit())
11054 {
11055 /* Deassociate and mark for deletion */
11056 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11057 rc = pMedium->i_removeBackReference(mData->mUuid);
11058 if (FAILED(rc))
11059 throw rc;
11060 implicitAtts.push_back(pAtt);
11061 continue;
11062 }
11063
11064 /* Was this medium attached before? */
11065 if (!i_findAttachment(oldAtts, pMedium))
11066 {
11067 /* no: de-associate */
11068 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11069 rc = pMedium->i_removeBackReference(mData->mUuid);
11070 if (FAILED(rc))
11071 throw rc;
11072 continue;
11073 }
11074 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11075 }
11076
11077 /* If there are implicit attachments to delete, throw away the lock
11078 * map contents (which will unlock all media) since the medium
11079 * attachments will be rolled back. Below we need to completely
11080 * recreate the lock map anyway since it is infinitely complex to
11081 * do this incrementally (would need reconstructing each attachment
11082 * change, which would be extremely hairy). */
11083 if (implicitAtts.size() != 0)
11084 {
11085 ErrorInfoKeeper eik;
11086
11087 HRESULT rc1 = lockedMediaMap->Clear();
11088 AssertComRC(rc1);
11089 }
11090
11091 /* rollback hard disk changes */
11092 mMediumAttachments.rollback();
11093
11094 MultiResult mrc(S_OK);
11095
11096 // Delete unused implicit diffs.
11097 if (implicitAtts.size() != 0)
11098 {
11099 alock.release();
11100
11101 for (MediumAttachmentList::const_iterator
11102 it = implicitAtts.begin();
11103 it != implicitAtts.end();
11104 ++it)
11105 {
11106 // Remove medium associated with this attachment.
11107 ComObjPtr<MediumAttachment> pAtt = *it;
11108 Assert(pAtt);
11109 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11110 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11111 Assert(pMedium);
11112
11113 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11114 // continue on delete failure, just collect error messages
11115 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11116 pMedium->i_getLocationFull().c_str() ));
11117 mrc = rc;
11118 }
11119 // Clear the list of deleted implicit attachments now, while not
11120 // holding the lock, as it will ultimately trigger Medium::uninit()
11121 // calls which assume that the media tree lock isn't held.
11122 implicitAtts.clear();
11123
11124 alock.acquire();
11125
11126 /* if there is a VM recreate media lock map as mentioned above,
11127 * otherwise it is a waste of time and we leave things unlocked */
11128 if (aOnline)
11129 {
11130 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11131 /* must never be NULL, but better safe than sorry */
11132 if (!pMachine.isNull())
11133 {
11134 alock.release();
11135 rc = mData->mSession.mMachine->i_lockMedia();
11136 alock.acquire();
11137 if (FAILED(rc))
11138 throw rc;
11139 }
11140 }
11141 }
11142 }
11143 catch (HRESULT aRC) {rc = aRC;}
11144
11145 if (mData->mMachineState == MachineState_SettingUp)
11146 i_setMachineState(oldState);
11147
11148 /* unlock all hard disks we locked when there is no VM */
11149 if (!aOnline)
11150 {
11151 ErrorInfoKeeper eik;
11152
11153 HRESULT rc1 = lockedMediaMap->Clear();
11154 AssertComRC(rc1);
11155 }
11156
11157 return rc;
11158}
11159
11160
11161/**
11162 * Looks through the given list of media attachments for one with the given parameters
11163 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11164 * can be searched as well if needed.
11165 *
11166 * @param ll
11167 * @param aControllerName
11168 * @param aControllerPort
11169 * @param aDevice
11170 * @return
11171 */
11172MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11173 const Utf8Str &aControllerName,
11174 LONG aControllerPort,
11175 LONG aDevice)
11176{
11177 for (MediumAttachmentList::const_iterator
11178 it = ll.begin();
11179 it != ll.end();
11180 ++it)
11181 {
11182 MediumAttachment *pAttach = *it;
11183 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11184 return pAttach;
11185 }
11186
11187 return NULL;
11188}
11189
11190/**
11191 * Looks through the given list of media attachments for one with the given parameters
11192 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11193 * can be searched as well if needed.
11194 *
11195 * @param ll
11196 * @param pMedium
11197 * @return
11198 */
11199MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11200 ComObjPtr<Medium> pMedium)
11201{
11202 for (MediumAttachmentList::const_iterator
11203 it = ll.begin();
11204 it != ll.end();
11205 ++it)
11206 {
11207 MediumAttachment *pAttach = *it;
11208 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11209 if (pMediumThis == pMedium)
11210 return pAttach;
11211 }
11212
11213 return NULL;
11214}
11215
11216/**
11217 * Looks through the given list of media attachments for one with the given parameters
11218 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11219 * can be searched as well if needed.
11220 *
11221 * @param ll
11222 * @param id
11223 * @return
11224 */
11225MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11226 Guid &id)
11227{
11228 for (MediumAttachmentList::const_iterator
11229 it = ll.begin();
11230 it != ll.end();
11231 ++it)
11232 {
11233 MediumAttachment *pAttach = *it;
11234 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11235 if (pMediumThis->i_getId() == id)
11236 return pAttach;
11237 }
11238
11239 return NULL;
11240}
11241
11242/**
11243 * Main implementation for Machine::DetachDevice. This also gets called
11244 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11245 *
11246 * @param pAttach Medium attachment to detach.
11247 * @param writeLock Machine write lock which the caller must have locked once.
11248 * This may be released temporarily in here.
11249 * @param pSnapshot If NULL, then the detachment is for the current machine.
11250 * Otherwise this is for a SnapshotMachine, and this must be
11251 * its snapshot.
11252 * @return
11253 */
11254HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11255 AutoWriteLock &writeLock,
11256 Snapshot *pSnapshot)
11257{
11258 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11259 DeviceType_T mediumType = pAttach->i_getType();
11260
11261 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11262
11263 if (pAttach->i_isImplicit())
11264 {
11265 /* attempt to implicitly delete the implicitly created diff */
11266
11267 /// @todo move the implicit flag from MediumAttachment to Medium
11268 /// and forbid any hard disk operation when it is implicit. Or maybe
11269 /// a special media state for it to make it even more simple.
11270
11271 Assert(mMediumAttachments.isBackedUp());
11272
11273 /* will release the lock before the potentially lengthy operation, so
11274 * protect with the special state */
11275 MachineState_T oldState = mData->mMachineState;
11276 i_setMachineState(MachineState_SettingUp);
11277
11278 writeLock.release();
11279
11280 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11281 true /*aWait*/,
11282 false /*aNotify*/);
11283
11284 writeLock.acquire();
11285
11286 i_setMachineState(oldState);
11287
11288 if (FAILED(rc)) return rc;
11289 }
11290
11291 i_setModified(IsModified_Storage);
11292 mMediumAttachments.backup();
11293 mMediumAttachments->remove(pAttach);
11294
11295 if (!oldmedium.isNull())
11296 {
11297 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11298 if (pSnapshot)
11299 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11300 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11301 else if (mediumType != DeviceType_HardDisk)
11302 oldmedium->i_removeBackReference(mData->mUuid);
11303 }
11304
11305 return S_OK;
11306}
11307
11308/**
11309 * Goes thru all media of the given list and
11310 *
11311 * 1) calls i_detachDevice() on each of them for this machine and
11312 * 2) adds all Medium objects found in the process to the given list,
11313 * depending on cleanupMode.
11314 *
11315 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11316 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11317 * media to the list.
11318 *
11319 * This gets called from Machine::Unregister, both for the actual Machine and
11320 * the SnapshotMachine objects that might be found in the snapshots.
11321 *
11322 * Requires caller and locking. The machine lock must be passed in because it
11323 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11324 *
11325 * @param writeLock Machine lock from top-level caller; this gets passed to
11326 * i_detachDevice.
11327 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11328 * object if called for a SnapshotMachine.
11329 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11330 * added to llMedia; if Full, then all media get added;
11331 * otherwise no media get added.
11332 * @param llMedia Caller's list to receive Medium objects which got detached so
11333 * caller can close() them, depending on cleanupMode.
11334 * @return
11335 */
11336HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11337 Snapshot *pSnapshot,
11338 CleanupMode_T cleanupMode,
11339 MediaList &llMedia)
11340{
11341 Assert(isWriteLockOnCurrentThread());
11342
11343 HRESULT rc;
11344
11345 // make a temporary list because i_detachDevice invalidates iterators into
11346 // mMediumAttachments
11347 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11348
11349 for (MediumAttachmentList::iterator
11350 it = llAttachments2.begin();
11351 it != llAttachments2.end();
11352 ++it)
11353 {
11354 ComObjPtr<MediumAttachment> &pAttach = *it;
11355 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11356
11357 if (!pMedium.isNull())
11358 {
11359 AutoCaller mac(pMedium);
11360 if (FAILED(mac.rc())) return mac.rc();
11361 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11362 DeviceType_T devType = pMedium->i_getDeviceType();
11363 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11364 && devType == DeviceType_HardDisk)
11365 || (cleanupMode == CleanupMode_Full)
11366 )
11367 {
11368 llMedia.push_back(pMedium);
11369 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11370 /* Not allowed to keep this lock as below we need the parent
11371 * medium lock, and the lock order is parent to child. */
11372 lock.release();
11373 /*
11374 * Search for medias which are not attached to any machine, but
11375 * in the chain to an attached disk. Mediums are only consided
11376 * if they are:
11377 * - have only one child
11378 * - no references to any machines
11379 * - are of normal medium type
11380 */
11381 while (!pParent.isNull())
11382 {
11383 AutoCaller mac1(pParent);
11384 if (FAILED(mac1.rc())) return mac1.rc();
11385 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11386 if (pParent->i_getChildren().size() == 1)
11387 {
11388 if ( pParent->i_getMachineBackRefCount() == 0
11389 && pParent->i_getType() == MediumType_Normal
11390 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11391 llMedia.push_back(pParent);
11392 }
11393 else
11394 break;
11395 pParent = pParent->i_getParent();
11396 }
11397 }
11398 }
11399
11400 // real machine: then we need to use the proper method
11401 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11402
11403 if (FAILED(rc))
11404 return rc;
11405 }
11406
11407 return S_OK;
11408}
11409
11410/**
11411 * Perform deferred hard disk detachments.
11412 *
11413 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11414 * changed (not backed up).
11415 *
11416 * If @a aOnline is @c true then this method will also unlock the old hard
11417 * disks for which the new implicit diffs were created and will lock these new
11418 * diffs for writing.
11419 *
11420 * @param aOnline Whether the VM was online prior to this operation.
11421 *
11422 * @note Locks this object for writing!
11423 */
11424void Machine::i_commitMedia(bool aOnline /*= false*/)
11425{
11426 AutoCaller autoCaller(this);
11427 AssertComRCReturnVoid(autoCaller.rc());
11428
11429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11430
11431 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11432
11433 HRESULT rc = S_OK;
11434
11435 /* no attach/detach operations -- nothing to do */
11436 if (!mMediumAttachments.isBackedUp())
11437 return;
11438
11439 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11440 bool fMediaNeedsLocking = false;
11441
11442 /* enumerate new attachments */
11443 for (MediumAttachmentList::const_iterator
11444 it = mMediumAttachments->begin();
11445 it != mMediumAttachments->end();
11446 ++it)
11447 {
11448 MediumAttachment *pAttach = *it;
11449
11450 pAttach->i_commit();
11451
11452 Medium *pMedium = pAttach->i_getMedium();
11453 bool fImplicit = pAttach->i_isImplicit();
11454
11455 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11456 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11457 fImplicit));
11458
11459 /** @todo convert all this Machine-based voodoo to MediumAttachment
11460 * based commit logic. */
11461 if (fImplicit)
11462 {
11463 /* convert implicit attachment to normal */
11464 pAttach->i_setImplicit(false);
11465
11466 if ( aOnline
11467 && pMedium
11468 && pAttach->i_getType() == DeviceType_HardDisk
11469 )
11470 {
11471 /* update the appropriate lock list */
11472 MediumLockList *pMediumLockList;
11473 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11474 AssertComRC(rc);
11475 if (pMediumLockList)
11476 {
11477 /* unlock if there's a need to change the locking */
11478 if (!fMediaNeedsLocking)
11479 {
11480 rc = mData->mSession.mLockedMedia.Unlock();
11481 AssertComRC(rc);
11482 fMediaNeedsLocking = true;
11483 }
11484 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11485 AssertComRC(rc);
11486 rc = pMediumLockList->Append(pMedium, true);
11487 AssertComRC(rc);
11488 }
11489 }
11490
11491 continue;
11492 }
11493
11494 if (pMedium)
11495 {
11496 /* was this medium attached before? */
11497 for (MediumAttachmentList::iterator
11498 oldIt = oldAtts.begin();
11499 oldIt != oldAtts.end();
11500 ++oldIt)
11501 {
11502 MediumAttachment *pOldAttach = *oldIt;
11503 if (pOldAttach->i_getMedium() == pMedium)
11504 {
11505 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11506
11507 /* yes: remove from old to avoid de-association */
11508 oldAtts.erase(oldIt);
11509 break;
11510 }
11511 }
11512 }
11513 }
11514
11515 /* enumerate remaining old attachments and de-associate from the
11516 * current machine state */
11517 for (MediumAttachmentList::const_iterator
11518 it = oldAtts.begin();
11519 it != oldAtts.end();
11520 ++it)
11521 {
11522 MediumAttachment *pAttach = *it;
11523 Medium *pMedium = pAttach->i_getMedium();
11524
11525 /* Detach only hard disks, since DVD/floppy media is detached
11526 * instantly in MountMedium. */
11527 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11528 {
11529 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11530
11531 /* now de-associate from the current machine state */
11532 rc = pMedium->i_removeBackReference(mData->mUuid);
11533 AssertComRC(rc);
11534
11535 if (aOnline)
11536 {
11537 /* unlock since medium is not used anymore */
11538 MediumLockList *pMediumLockList;
11539 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11540 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11541 {
11542 /* this happens for online snapshots, there the attachment
11543 * is changing, but only to a diff image created under
11544 * the old one, so there is no separate lock list */
11545 Assert(!pMediumLockList);
11546 }
11547 else
11548 {
11549 AssertComRC(rc);
11550 if (pMediumLockList)
11551 {
11552 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11553 AssertComRC(rc);
11554 }
11555 }
11556 }
11557 }
11558 }
11559
11560 /* take media locks again so that the locking state is consistent */
11561 if (fMediaNeedsLocking)
11562 {
11563 Assert(aOnline);
11564 rc = mData->mSession.mLockedMedia.Lock();
11565 AssertComRC(rc);
11566 }
11567
11568 /* commit the hard disk changes */
11569 mMediumAttachments.commit();
11570
11571 if (i_isSessionMachine())
11572 {
11573 /*
11574 * Update the parent machine to point to the new owner.
11575 * This is necessary because the stored parent will point to the
11576 * session machine otherwise and cause crashes or errors later
11577 * when the session machine gets invalid.
11578 */
11579 /** @todo Change the MediumAttachment class to behave like any other
11580 * class in this regard by creating peer MediumAttachment
11581 * objects for session machines and share the data with the peer
11582 * machine.
11583 */
11584 for (MediumAttachmentList::const_iterator
11585 it = mMediumAttachments->begin();
11586 it != mMediumAttachments->end();
11587 ++it)
11588 (*it)->i_updateParentMachine(mPeer);
11589
11590 /* attach new data to the primary machine and reshare it */
11591 mPeer->mMediumAttachments.attach(mMediumAttachments);
11592 }
11593
11594 return;
11595}
11596
11597/**
11598 * Perform deferred deletion of implicitly created diffs.
11599 *
11600 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11601 * changed (not backed up).
11602 *
11603 * @note Locks this object for writing!
11604 */
11605void Machine::i_rollbackMedia()
11606{
11607 AutoCaller autoCaller(this);
11608 AssertComRCReturnVoid(autoCaller.rc());
11609
11610 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11611 LogFlowThisFunc(("Entering rollbackMedia\n"));
11612
11613 HRESULT rc = S_OK;
11614
11615 /* no attach/detach operations -- nothing to do */
11616 if (!mMediumAttachments.isBackedUp())
11617 return;
11618
11619 /* enumerate new attachments */
11620 for (MediumAttachmentList::const_iterator
11621 it = mMediumAttachments->begin();
11622 it != mMediumAttachments->end();
11623 ++it)
11624 {
11625 MediumAttachment *pAttach = *it;
11626 /* Fix up the backrefs for DVD/floppy media. */
11627 if (pAttach->i_getType() != DeviceType_HardDisk)
11628 {
11629 Medium *pMedium = pAttach->i_getMedium();
11630 if (pMedium)
11631 {
11632 rc = pMedium->i_removeBackReference(mData->mUuid);
11633 AssertComRC(rc);
11634 }
11635 }
11636
11637 (*it)->i_rollback();
11638
11639 pAttach = *it;
11640 /* Fix up the backrefs for DVD/floppy media. */
11641 if (pAttach->i_getType() != DeviceType_HardDisk)
11642 {
11643 Medium *pMedium = pAttach->i_getMedium();
11644 if (pMedium)
11645 {
11646 rc = pMedium->i_addBackReference(mData->mUuid);
11647 AssertComRC(rc);
11648 }
11649 }
11650 }
11651
11652 /** @todo convert all this Machine-based voodoo to MediumAttachment
11653 * based rollback logic. */
11654 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11655
11656 return;
11657}
11658
11659/**
11660 * Returns true if the settings file is located in the directory named exactly
11661 * as the machine; this means, among other things, that the machine directory
11662 * should be auto-renamed.
11663 *
11664 * @param aSettingsDir if not NULL, the full machine settings file directory
11665 * name will be assigned there.
11666 *
11667 * @note Doesn't lock anything.
11668 * @note Not thread safe (must be called from this object's lock).
11669 */
11670bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11671{
11672 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11673 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11674 if (aSettingsDir)
11675 *aSettingsDir = strMachineDirName;
11676 strMachineDirName.stripPath(); // vmname
11677 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11678 strConfigFileOnly.stripPath() // vmname.vbox
11679 .stripSuffix(); // vmname
11680 /** @todo hack, make somehow use of ComposeMachineFilename */
11681 if (mUserData->s.fDirectoryIncludesUUID)
11682 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11683
11684 AssertReturn(!strMachineDirName.isEmpty(), false);
11685 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11686
11687 return strMachineDirName == strConfigFileOnly;
11688}
11689
11690/**
11691 * Discards all changes to machine settings.
11692 *
11693 * @param aNotify Whether to notify the direct session about changes or not.
11694 *
11695 * @note Locks objects for writing!
11696 */
11697void Machine::i_rollback(bool aNotify)
11698{
11699 AutoCaller autoCaller(this);
11700 AssertComRCReturn(autoCaller.rc(), (void)0);
11701
11702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11703
11704 if (!mStorageControllers.isNull())
11705 {
11706 if (mStorageControllers.isBackedUp())
11707 {
11708 /* unitialize all new devices (absent in the backed up list). */
11709 StorageControllerList *backedList = mStorageControllers.backedUpData();
11710 for (StorageControllerList::const_iterator
11711 it = mStorageControllers->begin();
11712 it != mStorageControllers->end();
11713 ++it)
11714 {
11715 if ( std::find(backedList->begin(), backedList->end(), *it)
11716 == backedList->end()
11717 )
11718 {
11719 (*it)->uninit();
11720 }
11721 }
11722
11723 /* restore the list */
11724 mStorageControllers.rollback();
11725 }
11726
11727 /* rollback any changes to devices after restoring the list */
11728 if (mData->flModifications & IsModified_Storage)
11729 {
11730 for (StorageControllerList::const_iterator
11731 it = mStorageControllers->begin();
11732 it != mStorageControllers->end();
11733 ++it)
11734 {
11735 (*it)->i_rollback();
11736 }
11737 }
11738 }
11739
11740 if (!mUSBControllers.isNull())
11741 {
11742 if (mUSBControllers.isBackedUp())
11743 {
11744 /* unitialize all new devices (absent in the backed up list). */
11745 USBControllerList *backedList = mUSBControllers.backedUpData();
11746 for (USBControllerList::const_iterator
11747 it = mUSBControllers->begin();
11748 it != mUSBControllers->end();
11749 ++it)
11750 {
11751 if ( std::find(backedList->begin(), backedList->end(), *it)
11752 == backedList->end()
11753 )
11754 {
11755 (*it)->uninit();
11756 }
11757 }
11758
11759 /* restore the list */
11760 mUSBControllers.rollback();
11761 }
11762
11763 /* rollback any changes to devices after restoring the list */
11764 if (mData->flModifications & IsModified_USB)
11765 {
11766 for (USBControllerList::const_iterator
11767 it = mUSBControllers->begin();
11768 it != mUSBControllers->end();
11769 ++it)
11770 {
11771 (*it)->i_rollback();
11772 }
11773 }
11774 }
11775
11776 mUserData.rollback();
11777
11778 mHWData.rollback();
11779
11780 if (mData->flModifications & IsModified_Storage)
11781 i_rollbackMedia();
11782
11783 if (mBIOSSettings)
11784 mBIOSSettings->i_rollback();
11785
11786 if (mTrustedPlatformModule)
11787 mTrustedPlatformModule->i_rollback();
11788
11789 if (mNvramStore)
11790 mNvramStore->i_rollback();
11791
11792 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11793 mRecordingSettings->i_rollback();
11794
11795 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11796 mGraphicsAdapter->i_rollback();
11797
11798 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11799 mVRDEServer->i_rollback();
11800
11801 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11802 mAudioAdapter->i_rollback();
11803
11804 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11805 mUSBDeviceFilters->i_rollback();
11806
11807 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11808 mBandwidthControl->i_rollback();
11809
11810 if (!mHWData.isNull())
11811 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11812 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11813 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11814 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11815
11816 if (mData->flModifications & IsModified_NetworkAdapters)
11817 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11818 if ( mNetworkAdapters[slot]
11819 && mNetworkAdapters[slot]->i_isModified())
11820 {
11821 mNetworkAdapters[slot]->i_rollback();
11822 networkAdapters[slot] = mNetworkAdapters[slot];
11823 }
11824
11825 if (mData->flModifications & IsModified_SerialPorts)
11826 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11827 if ( mSerialPorts[slot]
11828 && mSerialPorts[slot]->i_isModified())
11829 {
11830 mSerialPorts[slot]->i_rollback();
11831 serialPorts[slot] = mSerialPorts[slot];
11832 }
11833
11834 if (mData->flModifications & IsModified_ParallelPorts)
11835 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11836 if ( mParallelPorts[slot]
11837 && mParallelPorts[slot]->i_isModified())
11838 {
11839 mParallelPorts[slot]->i_rollback();
11840 parallelPorts[slot] = mParallelPorts[slot];
11841 }
11842
11843 if (aNotify)
11844 {
11845 /* inform the direct session about changes */
11846
11847 ComObjPtr<Machine> that = this;
11848 uint32_t flModifications = mData->flModifications;
11849 alock.release();
11850
11851 if (flModifications & IsModified_SharedFolders)
11852 that->i_onSharedFolderChange();
11853
11854 if (flModifications & IsModified_VRDEServer)
11855 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11856 if (flModifications & IsModified_USB)
11857 that->i_onUSBControllerChange();
11858
11859 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11860 if (networkAdapters[slot])
11861 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11862 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11863 if (serialPorts[slot])
11864 that->i_onSerialPortChange(serialPorts[slot]);
11865 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11866 if (parallelPorts[slot])
11867 that->i_onParallelPortChange(parallelPorts[slot]);
11868
11869 if (flModifications & IsModified_Storage)
11870 {
11871 for (StorageControllerList::const_iterator
11872 it = mStorageControllers->begin();
11873 it != mStorageControllers->end();
11874 ++it)
11875 {
11876 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11877 }
11878 }
11879
11880
11881#if 0
11882 if (flModifications & IsModified_BandwidthControl)
11883 that->onBandwidthControlChange();
11884#endif
11885 }
11886}
11887
11888/**
11889 * Commits all the changes to machine settings.
11890 *
11891 * Note that this operation is supposed to never fail.
11892 *
11893 * @note Locks this object and children for writing.
11894 */
11895void Machine::i_commit()
11896{
11897 AutoCaller autoCaller(this);
11898 AssertComRCReturnVoid(autoCaller.rc());
11899
11900 AutoCaller peerCaller(mPeer);
11901 AssertComRCReturnVoid(peerCaller.rc());
11902
11903 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11904
11905 /*
11906 * use safe commit to ensure Snapshot machines (that share mUserData)
11907 * will still refer to a valid memory location
11908 */
11909 mUserData.commitCopy();
11910
11911 mHWData.commit();
11912
11913 if (mMediumAttachments.isBackedUp())
11914 i_commitMedia(Global::IsOnline(mData->mMachineState));
11915
11916 mBIOSSettings->i_commit();
11917 mTrustedPlatformModule->i_commit();
11918 mNvramStore->i_commit();
11919 mRecordingSettings->i_commit();
11920 mGraphicsAdapter->i_commit();
11921 mVRDEServer->i_commit();
11922 mAudioAdapter->i_commit();
11923 mUSBDeviceFilters->i_commit();
11924 mBandwidthControl->i_commit();
11925
11926 /* Since mNetworkAdapters is a list which might have been changed (resized)
11927 * without using the Backupable<> template we need to handle the copying
11928 * of the list entries manually, including the creation of peers for the
11929 * new objects. */
11930 bool commitNetworkAdapters = false;
11931 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11932 if (mPeer)
11933 {
11934 /* commit everything, even the ones which will go away */
11935 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11936 mNetworkAdapters[slot]->i_commit();
11937 /* copy over the new entries, creating a peer and uninit the original */
11938 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11939 for (size_t slot = 0; slot < newSize; slot++)
11940 {
11941 /* look if this adapter has a peer device */
11942 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11943 if (!peer)
11944 {
11945 /* no peer means the adapter is a newly created one;
11946 * create a peer owning data this data share it with */
11947 peer.createObject();
11948 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11949 }
11950 mPeer->mNetworkAdapters[slot] = peer;
11951 }
11952 /* uninit any no longer needed network adapters */
11953 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11954 mNetworkAdapters[slot]->uninit();
11955 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11956 {
11957 if (mPeer->mNetworkAdapters[slot])
11958 mPeer->mNetworkAdapters[slot]->uninit();
11959 }
11960 /* Keep the original network adapter count until this point, so that
11961 * discarding a chipset type change will not lose settings. */
11962 mNetworkAdapters.resize(newSize);
11963 mPeer->mNetworkAdapters.resize(newSize);
11964 }
11965 else
11966 {
11967 /* we have no peer (our parent is the newly created machine);
11968 * just commit changes to the network adapters */
11969 commitNetworkAdapters = true;
11970 }
11971 if (commitNetworkAdapters)
11972 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11973 mNetworkAdapters[slot]->i_commit();
11974
11975 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11976 mSerialPorts[slot]->i_commit();
11977 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11978 mParallelPorts[slot]->i_commit();
11979
11980 bool commitStorageControllers = false;
11981
11982 if (mStorageControllers.isBackedUp())
11983 {
11984 mStorageControllers.commit();
11985
11986 if (mPeer)
11987 {
11988 /* Commit all changes to new controllers (this will reshare data with
11989 * peers for those who have peers) */
11990 StorageControllerList *newList = new StorageControllerList();
11991 for (StorageControllerList::const_iterator
11992 it = mStorageControllers->begin();
11993 it != mStorageControllers->end();
11994 ++it)
11995 {
11996 (*it)->i_commit();
11997
11998 /* look if this controller has a peer device */
11999 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12000 if (!peer)
12001 {
12002 /* no peer means the device is a newly created one;
12003 * create a peer owning data this device share it with */
12004 peer.createObject();
12005 peer->init(mPeer, *it, true /* aReshare */);
12006 }
12007 else
12008 {
12009 /* remove peer from the old list */
12010 mPeer->mStorageControllers->remove(peer);
12011 }
12012 /* and add it to the new list */
12013 newList->push_back(peer);
12014 }
12015
12016 /* uninit old peer's controllers that are left */
12017 for (StorageControllerList::const_iterator
12018 it = mPeer->mStorageControllers->begin();
12019 it != mPeer->mStorageControllers->end();
12020 ++it)
12021 {
12022 (*it)->uninit();
12023 }
12024
12025 /* attach new list of controllers to our peer */
12026 mPeer->mStorageControllers.attach(newList);
12027 }
12028 else
12029 {
12030 /* we have no peer (our parent is the newly created machine);
12031 * just commit changes to devices */
12032 commitStorageControllers = true;
12033 }
12034 }
12035 else
12036 {
12037 /* the list of controllers itself is not changed,
12038 * just commit changes to controllers themselves */
12039 commitStorageControllers = true;
12040 }
12041
12042 if (commitStorageControllers)
12043 {
12044 for (StorageControllerList::const_iterator
12045 it = mStorageControllers->begin();
12046 it != mStorageControllers->end();
12047 ++it)
12048 {
12049 (*it)->i_commit();
12050 }
12051 }
12052
12053 bool commitUSBControllers = false;
12054
12055 if (mUSBControllers.isBackedUp())
12056 {
12057 mUSBControllers.commit();
12058
12059 if (mPeer)
12060 {
12061 /* Commit all changes to new controllers (this will reshare data with
12062 * peers for those who have peers) */
12063 USBControllerList *newList = new USBControllerList();
12064 for (USBControllerList::const_iterator
12065 it = mUSBControllers->begin();
12066 it != mUSBControllers->end();
12067 ++it)
12068 {
12069 (*it)->i_commit();
12070
12071 /* look if this controller has a peer device */
12072 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12073 if (!peer)
12074 {
12075 /* no peer means the device is a newly created one;
12076 * create a peer owning data this device share it with */
12077 peer.createObject();
12078 peer->init(mPeer, *it, true /* aReshare */);
12079 }
12080 else
12081 {
12082 /* remove peer from the old list */
12083 mPeer->mUSBControllers->remove(peer);
12084 }
12085 /* and add it to the new list */
12086 newList->push_back(peer);
12087 }
12088
12089 /* uninit old peer's controllers that are left */
12090 for (USBControllerList::const_iterator
12091 it = mPeer->mUSBControllers->begin();
12092 it != mPeer->mUSBControllers->end();
12093 ++it)
12094 {
12095 (*it)->uninit();
12096 }
12097
12098 /* attach new list of controllers to our peer */
12099 mPeer->mUSBControllers.attach(newList);
12100 }
12101 else
12102 {
12103 /* we have no peer (our parent is the newly created machine);
12104 * just commit changes to devices */
12105 commitUSBControllers = true;
12106 }
12107 }
12108 else
12109 {
12110 /* the list of controllers itself is not changed,
12111 * just commit changes to controllers themselves */
12112 commitUSBControllers = true;
12113 }
12114
12115 if (commitUSBControllers)
12116 {
12117 for (USBControllerList::const_iterator
12118 it = mUSBControllers->begin();
12119 it != mUSBControllers->end();
12120 ++it)
12121 {
12122 (*it)->i_commit();
12123 }
12124 }
12125
12126 if (i_isSessionMachine())
12127 {
12128 /* attach new data to the primary machine and reshare it */
12129 mPeer->mUserData.attach(mUserData);
12130 mPeer->mHWData.attach(mHWData);
12131 /* mmMediumAttachments is reshared by fixupMedia */
12132 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12133 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12134 }
12135}
12136
12137/**
12138 * Copies all the hardware data from the given machine.
12139 *
12140 * Currently, only called when the VM is being restored from a snapshot. In
12141 * particular, this implies that the VM is not running during this method's
12142 * call.
12143 *
12144 * @note This method must be called from under this object's lock.
12145 *
12146 * @note This method doesn't call #i_commit(), so all data remains backed up and
12147 * unsaved.
12148 */
12149void Machine::i_copyFrom(Machine *aThat)
12150{
12151 AssertReturnVoid(!i_isSnapshotMachine());
12152 AssertReturnVoid(aThat->i_isSnapshotMachine());
12153
12154 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12155
12156 mHWData.assignCopy(aThat->mHWData);
12157
12158 // create copies of all shared folders (mHWData after attaching a copy
12159 // contains just references to original objects)
12160 for (HWData::SharedFolderList::iterator
12161 it = mHWData->mSharedFolders.begin();
12162 it != mHWData->mSharedFolders.end();
12163 ++it)
12164 {
12165 ComObjPtr<SharedFolder> folder;
12166 folder.createObject();
12167 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12168 AssertComRC(rc);
12169 *it = folder;
12170 }
12171
12172 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12173 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12174 mNvramStore->i_copyFrom(aThat->mNvramStore);
12175 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12176 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12177 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12178 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12179 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12180 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12181
12182 /* create private copies of all controllers */
12183 mStorageControllers.backup();
12184 mStorageControllers->clear();
12185 for (StorageControllerList::const_iterator
12186 it = aThat->mStorageControllers->begin();
12187 it != aThat->mStorageControllers->end();
12188 ++it)
12189 {
12190 ComObjPtr<StorageController> ctrl;
12191 ctrl.createObject();
12192 ctrl->initCopy(this, *it);
12193 mStorageControllers->push_back(ctrl);
12194 }
12195
12196 /* create private copies of all USB controllers */
12197 mUSBControllers.backup();
12198 mUSBControllers->clear();
12199 for (USBControllerList::const_iterator
12200 it = aThat->mUSBControllers->begin();
12201 it != aThat->mUSBControllers->end();
12202 ++it)
12203 {
12204 ComObjPtr<USBController> ctrl;
12205 ctrl.createObject();
12206 ctrl->initCopy(this, *it);
12207 mUSBControllers->push_back(ctrl);
12208 }
12209
12210 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12211 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12212 {
12213 if (mNetworkAdapters[slot].isNotNull())
12214 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12215 else
12216 {
12217 unconst(mNetworkAdapters[slot]).createObject();
12218 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12219 }
12220 }
12221 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12222 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12223 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12224 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12225}
12226
12227/**
12228 * Returns whether the given storage controller is hotplug capable.
12229 *
12230 * @returns true if the controller supports hotplugging
12231 * false otherwise.
12232 * @param enmCtrlType The controller type to check for.
12233 */
12234bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12235{
12236 ComPtr<ISystemProperties> systemProperties;
12237 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12238 if (FAILED(rc))
12239 return false;
12240
12241 BOOL aHotplugCapable = FALSE;
12242 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12243
12244 return RT_BOOL(aHotplugCapable);
12245}
12246
12247#ifdef VBOX_WITH_RESOURCE_USAGE_API
12248
12249void Machine::i_getDiskList(MediaList &list)
12250{
12251 for (MediumAttachmentList::const_iterator
12252 it = mMediumAttachments->begin();
12253 it != mMediumAttachments->end();
12254 ++it)
12255 {
12256 MediumAttachment *pAttach = *it;
12257 /* just in case */
12258 AssertContinue(pAttach);
12259
12260 AutoCaller localAutoCallerA(pAttach);
12261 if (FAILED(localAutoCallerA.rc())) continue;
12262
12263 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12264
12265 if (pAttach->i_getType() == DeviceType_HardDisk)
12266 list.push_back(pAttach->i_getMedium());
12267 }
12268}
12269
12270void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12271{
12272 AssertReturnVoid(isWriteLockOnCurrentThread());
12273 AssertPtrReturnVoid(aCollector);
12274
12275 pm::CollectorHAL *hal = aCollector->getHAL();
12276 /* Create sub metrics */
12277 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12278 "Percentage of processor time spent in user mode by the VM process.");
12279 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12280 "Percentage of processor time spent in kernel mode by the VM process.");
12281 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12282 "Size of resident portion of VM process in memory.");
12283 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12284 "Actual size of all VM disks combined.");
12285 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12286 "Network receive rate.");
12287 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12288 "Network transmit rate.");
12289 /* Create and register base metrics */
12290 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12291 cpuLoadUser, cpuLoadKernel);
12292 aCollector->registerBaseMetric(cpuLoad);
12293 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12294 ramUsageUsed);
12295 aCollector->registerBaseMetric(ramUsage);
12296 MediaList disks;
12297 i_getDiskList(disks);
12298 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12299 diskUsageUsed);
12300 aCollector->registerBaseMetric(diskUsage);
12301
12302 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12303 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12304 new pm::AggregateAvg()));
12305 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12306 new pm::AggregateMin()));
12307 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12308 new pm::AggregateMax()));
12309 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12310 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12311 new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12313 new pm::AggregateMin()));
12314 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12315 new pm::AggregateMax()));
12316
12317 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12318 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12319 new pm::AggregateAvg()));
12320 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12321 new pm::AggregateMin()));
12322 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12323 new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12326 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12327 new pm::AggregateAvg()));
12328 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12329 new pm::AggregateMin()));
12330 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12331 new pm::AggregateMax()));
12332
12333
12334 /* Guest metrics collector */
12335 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12336 aCollector->registerGuest(mCollectorGuest);
12337 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12338
12339 /* Create sub metrics */
12340 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12341 "Percentage of processor time spent in user mode as seen by the guest.");
12342 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12343 "Percentage of processor time spent in kernel mode as seen by the guest.");
12344 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12345 "Percentage of processor time spent idling as seen by the guest.");
12346
12347 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12348 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12349 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12350 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12351 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12352 pm::SubMetric *guestMemCache = new pm::SubMetric(
12353 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12354
12355 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12356 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12357
12358 /* Create and register base metrics */
12359 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12360 machineNetRx, machineNetTx);
12361 aCollector->registerBaseMetric(machineNetRate);
12362
12363 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12364 guestLoadUser, guestLoadKernel, guestLoadIdle);
12365 aCollector->registerBaseMetric(guestCpuLoad);
12366
12367 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12368 guestMemTotal, guestMemFree,
12369 guestMemBalloon, guestMemShared,
12370 guestMemCache, guestPagedTotal);
12371 aCollector->registerBaseMetric(guestCpuMem);
12372
12373 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12374 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12375 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12376 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12377
12378 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12379 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12380 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12381 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12382
12383 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12384 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12386 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12387
12388 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12389 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12390 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12391 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12392
12393 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12394 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12395 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12396 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12397
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12400 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12401 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12402
12403 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12404 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12405 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12406 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12407
12408 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12409 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12410 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12411 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12412
12413 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12414 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12415 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12416 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12417
12418 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12419 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12420 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12421 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12422
12423 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12424 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12425 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12426 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12427}
12428
12429void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12430{
12431 AssertReturnVoid(isWriteLockOnCurrentThread());
12432
12433 if (aCollector)
12434 {
12435 aCollector->unregisterMetricsFor(aMachine);
12436 aCollector->unregisterBaseMetricsFor(aMachine);
12437 }
12438}
12439
12440#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12441
12442
12443////////////////////////////////////////////////////////////////////////////////
12444
12445DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12446
12447HRESULT SessionMachine::FinalConstruct()
12448{
12449 LogFlowThisFunc(("\n"));
12450
12451 mClientToken = NULL;
12452
12453 return BaseFinalConstruct();
12454}
12455
12456void SessionMachine::FinalRelease()
12457{
12458 LogFlowThisFunc(("\n"));
12459
12460 Assert(!mClientToken);
12461 /* paranoia, should not hang around any more */
12462 if (mClientToken)
12463 {
12464 delete mClientToken;
12465 mClientToken = NULL;
12466 }
12467
12468 uninit(Uninit::Unexpected);
12469
12470 BaseFinalRelease();
12471}
12472
12473/**
12474 * @note Must be called only by Machine::LockMachine() from its own write lock.
12475 */
12476HRESULT SessionMachine::init(Machine *aMachine)
12477{
12478 LogFlowThisFuncEnter();
12479 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12480
12481 AssertReturn(aMachine, E_INVALIDARG);
12482
12483 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12484
12485 /* Enclose the state transition NotReady->InInit->Ready */
12486 AutoInitSpan autoInitSpan(this);
12487 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12488
12489 HRESULT rc = S_OK;
12490
12491 RT_ZERO(mAuthLibCtx);
12492
12493 /* create the machine client token */
12494 try
12495 {
12496 mClientToken = new ClientToken(aMachine, this);
12497 if (!mClientToken->isReady())
12498 {
12499 delete mClientToken;
12500 mClientToken = NULL;
12501 rc = E_FAIL;
12502 }
12503 }
12504 catch (std::bad_alloc &)
12505 {
12506 rc = E_OUTOFMEMORY;
12507 }
12508 if (FAILED(rc))
12509 return rc;
12510
12511 /* memorize the peer Machine */
12512 unconst(mPeer) = aMachine;
12513 /* share the parent pointer */
12514 unconst(mParent) = aMachine->mParent;
12515
12516 /* take the pointers to data to share */
12517 mData.share(aMachine->mData);
12518 mSSData.share(aMachine->mSSData);
12519
12520 mUserData.share(aMachine->mUserData);
12521 mHWData.share(aMachine->mHWData);
12522 mMediumAttachments.share(aMachine->mMediumAttachments);
12523
12524 mStorageControllers.allocate();
12525 for (StorageControllerList::const_iterator
12526 it = aMachine->mStorageControllers->begin();
12527 it != aMachine->mStorageControllers->end();
12528 ++it)
12529 {
12530 ComObjPtr<StorageController> ctl;
12531 ctl.createObject();
12532 ctl->init(this, *it);
12533 mStorageControllers->push_back(ctl);
12534 }
12535
12536 mUSBControllers.allocate();
12537 for (USBControllerList::const_iterator
12538 it = aMachine->mUSBControllers->begin();
12539 it != aMachine->mUSBControllers->end();
12540 ++it)
12541 {
12542 ComObjPtr<USBController> ctl;
12543 ctl.createObject();
12544 ctl->init(this, *it);
12545 mUSBControllers->push_back(ctl);
12546 }
12547
12548 unconst(mBIOSSettings).createObject();
12549 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12550
12551 unconst(mTrustedPlatformModule).createObject();
12552 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12553
12554 unconst(mNvramStore).createObject();
12555 mNvramStore->init(this, aMachine->mNvramStore);
12556
12557 unconst(mRecordingSettings).createObject();
12558 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12559 /* create another GraphicsAdapter object that will be mutable */
12560 unconst(mGraphicsAdapter).createObject();
12561 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12562 /* create another VRDEServer object that will be mutable */
12563 unconst(mVRDEServer).createObject();
12564 mVRDEServer->init(this, aMachine->mVRDEServer);
12565 /* create another audio adapter object that will be mutable */
12566 unconst(mAudioAdapter).createObject();
12567 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12568 /* create a list of serial ports that will be mutable */
12569 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12570 {
12571 unconst(mSerialPorts[slot]).createObject();
12572 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12573 }
12574 /* create a list of parallel ports that will be mutable */
12575 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12576 {
12577 unconst(mParallelPorts[slot]).createObject();
12578 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12579 }
12580
12581 /* create another USB device filters object that will be mutable */
12582 unconst(mUSBDeviceFilters).createObject();
12583 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12584
12585 /* create a list of network adapters that will be mutable */
12586 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12587 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12588 {
12589 unconst(mNetworkAdapters[slot]).createObject();
12590 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12591 }
12592
12593 /* create another bandwidth control object that will be mutable */
12594 unconst(mBandwidthControl).createObject();
12595 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12596
12597 /* default is to delete saved state on Saved -> PoweredOff transition */
12598 mRemoveSavedState = true;
12599
12600 /* Confirm a successful initialization when it's the case */
12601 autoInitSpan.setSucceeded();
12602
12603 miNATNetworksStarted = 0;
12604
12605 LogFlowThisFuncLeave();
12606 return rc;
12607}
12608
12609/**
12610 * Uninitializes this session object. If the reason is other than
12611 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12612 * or the client watcher code.
12613 *
12614 * @param aReason uninitialization reason
12615 *
12616 * @note Locks mParent + this object for writing.
12617 */
12618void SessionMachine::uninit(Uninit::Reason aReason)
12619{
12620 LogFlowThisFuncEnter();
12621 LogFlowThisFunc(("reason=%d\n", aReason));
12622
12623 /*
12624 * Strongly reference ourselves to prevent this object deletion after
12625 * mData->mSession.mMachine.setNull() below (which can release the last
12626 * reference and call the destructor). Important: this must be done before
12627 * accessing any members (and before AutoUninitSpan that does it as well).
12628 * This self reference will be released as the very last step on return.
12629 */
12630 ComObjPtr<SessionMachine> selfRef;
12631 if (aReason != Uninit::Unexpected)
12632 selfRef = this;
12633
12634 /* Enclose the state transition Ready->InUninit->NotReady */
12635 AutoUninitSpan autoUninitSpan(this);
12636 if (autoUninitSpan.uninitDone())
12637 {
12638 LogFlowThisFunc(("Already uninitialized\n"));
12639 LogFlowThisFuncLeave();
12640 return;
12641 }
12642
12643 if (autoUninitSpan.initFailed())
12644 {
12645 /* We've been called by init() because it's failed. It's not really
12646 * necessary (nor it's safe) to perform the regular uninit sequence
12647 * below, the following is enough.
12648 */
12649 LogFlowThisFunc(("Initialization failed.\n"));
12650 /* destroy the machine client token */
12651 if (mClientToken)
12652 {
12653 delete mClientToken;
12654 mClientToken = NULL;
12655 }
12656 uninitDataAndChildObjects();
12657 mData.free();
12658 unconst(mParent) = NULL;
12659 unconst(mPeer) = NULL;
12660 LogFlowThisFuncLeave();
12661 return;
12662 }
12663
12664 MachineState_T lastState;
12665 {
12666 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12667 lastState = mData->mMachineState;
12668 }
12669 NOREF(lastState);
12670
12671#ifdef VBOX_WITH_USB
12672 // release all captured USB devices, but do this before requesting the locks below
12673 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12674 {
12675 /* Console::captureUSBDevices() is called in the VM process only after
12676 * setting the machine state to Starting or Restoring.
12677 * Console::detachAllUSBDevices() will be called upon successful
12678 * termination. So, we need to release USB devices only if there was
12679 * an abnormal termination of a running VM.
12680 *
12681 * This is identical to SessionMachine::DetachAllUSBDevices except
12682 * for the aAbnormal argument. */
12683 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12684 AssertComRC(rc);
12685 NOREF(rc);
12686
12687 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12688 if (service)
12689 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12690 }
12691#endif /* VBOX_WITH_USB */
12692
12693 // we need to lock this object in uninit() because the lock is shared
12694 // with mPeer (as well as data we modify below). mParent lock is needed
12695 // by several calls to it.
12696 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12697
12698#ifdef VBOX_WITH_RESOURCE_USAGE_API
12699 /*
12700 * It is safe to call Machine::i_unregisterMetrics() here because
12701 * PerformanceCollector::samplerCallback no longer accesses guest methods
12702 * holding the lock.
12703 */
12704 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12705 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12706 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12707 if (mCollectorGuest)
12708 {
12709 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12710 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12711 mCollectorGuest = NULL;
12712 }
12713#endif
12714
12715 if (aReason == Uninit::Abnormal)
12716 {
12717 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12718
12719 /*
12720 * Move the VM to the 'Aborted' machine state unless we are restoring a
12721 * VM that was in the 'Saved' machine state. In that case, if the VM
12722 * fails before reaching either the 'Restoring' machine state or the
12723 * 'Running' machine state then we set the machine state to
12724 * 'AbortedSaved' in order to preserve the saved state file so that the
12725 * VM can be restored in the future.
12726 */
12727 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12728 i_setMachineState(MachineState_AbortedSaved);
12729 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12730 i_setMachineState(MachineState_Aborted);
12731 }
12732
12733 // any machine settings modified?
12734 if (mData->flModifications)
12735 {
12736 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12737 i_rollback(false /* aNotify */);
12738 }
12739
12740 mData->mSession.mPID = NIL_RTPROCESS;
12741
12742 if (aReason == Uninit::Unexpected)
12743 {
12744 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12745 * client watcher thread to update the set of machines that have open
12746 * sessions. */
12747 mParent->i_updateClientWatcher();
12748 }
12749
12750 /* uninitialize all remote controls */
12751 if (mData->mSession.mRemoteControls.size())
12752 {
12753 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12754 mData->mSession.mRemoteControls.size()));
12755
12756 /* Always restart a the beginning, since the iterator is invalidated
12757 * by using erase(). */
12758 for (Data::Session::RemoteControlList::iterator
12759 it = mData->mSession.mRemoteControls.begin();
12760 it != mData->mSession.mRemoteControls.end();
12761 it = mData->mSession.mRemoteControls.begin())
12762 {
12763 ComPtr<IInternalSessionControl> pControl = *it;
12764 mData->mSession.mRemoteControls.erase(it);
12765 multilock.release();
12766 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12767 HRESULT rc = pControl->Uninitialize();
12768 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12769 if (FAILED(rc))
12770 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12771 multilock.acquire();
12772 }
12773 mData->mSession.mRemoteControls.clear();
12774 }
12775
12776 /* Remove all references to the NAT network service. The service will stop
12777 * if all references (also from other VMs) are removed. */
12778 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12779 {
12780 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12781 {
12782 BOOL enabled;
12783 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12784 if ( FAILED(hrc)
12785 || !enabled)
12786 continue;
12787
12788 NetworkAttachmentType_T type;
12789 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12790 if ( SUCCEEDED(hrc)
12791 && type == NetworkAttachmentType_NATNetwork)
12792 {
12793 Bstr name;
12794 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12795 if (SUCCEEDED(hrc))
12796 {
12797 multilock.release();
12798 Utf8Str strName(name);
12799 LogRel(("VM '%s' stops using NAT network '%s'\n",
12800 mUserData->s.strName.c_str(), strName.c_str()));
12801 mParent->i_natNetworkRefDec(strName);
12802 multilock.acquire();
12803 }
12804 }
12805 }
12806 }
12807
12808 /*
12809 * An expected uninitialization can come only from #i_checkForDeath().
12810 * Otherwise it means that something's gone really wrong (for example,
12811 * the Session implementation has released the VirtualBox reference
12812 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12813 * etc). However, it's also possible, that the client releases the IPC
12814 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12815 * but the VirtualBox release event comes first to the server process.
12816 * This case is practically possible, so we should not assert on an
12817 * unexpected uninit, just log a warning.
12818 */
12819
12820 if (aReason == Uninit::Unexpected)
12821 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12822
12823 if (aReason != Uninit::Normal)
12824 {
12825 mData->mSession.mDirectControl.setNull();
12826 }
12827 else
12828 {
12829 /* this must be null here (see #OnSessionEnd()) */
12830 Assert(mData->mSession.mDirectControl.isNull());
12831 Assert(mData->mSession.mState == SessionState_Unlocking);
12832 Assert(!mData->mSession.mProgress.isNull());
12833 }
12834 if (mData->mSession.mProgress)
12835 {
12836 if (aReason == Uninit::Normal)
12837 mData->mSession.mProgress->i_notifyComplete(S_OK);
12838 else
12839 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12840 COM_IIDOF(ISession),
12841 getComponentName(),
12842 tr("The VM session was aborted"));
12843 mData->mSession.mProgress.setNull();
12844 }
12845
12846 if (mConsoleTaskData.mProgress)
12847 {
12848 Assert(aReason == Uninit::Abnormal);
12849 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12850 COM_IIDOF(ISession),
12851 getComponentName(),
12852 tr("The VM session was aborted"));
12853 mConsoleTaskData.mProgress.setNull();
12854 }
12855
12856 /* remove the association between the peer machine and this session machine */
12857 Assert( (SessionMachine*)mData->mSession.mMachine == this
12858 || aReason == Uninit::Unexpected);
12859
12860 /* reset the rest of session data */
12861 mData->mSession.mLockType = LockType_Null;
12862 mData->mSession.mMachine.setNull();
12863 mData->mSession.mState = SessionState_Unlocked;
12864 mData->mSession.mName.setNull();
12865
12866 /* destroy the machine client token before leaving the exclusive lock */
12867 if (mClientToken)
12868 {
12869 delete mClientToken;
12870 mClientToken = NULL;
12871 }
12872
12873 /* fire an event */
12874 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12875
12876 uninitDataAndChildObjects();
12877
12878 /* free the essential data structure last */
12879 mData.free();
12880
12881 /* release the exclusive lock before setting the below two to NULL */
12882 multilock.release();
12883
12884 unconst(mParent) = NULL;
12885 unconst(mPeer) = NULL;
12886
12887 AuthLibUnload(&mAuthLibCtx);
12888
12889 LogFlowThisFuncLeave();
12890}
12891
12892// util::Lockable interface
12893////////////////////////////////////////////////////////////////////////////////
12894
12895/**
12896 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12897 * with the primary Machine instance (mPeer).
12898 */
12899RWLockHandle *SessionMachine::lockHandle() const
12900{
12901 AssertReturn(mPeer != NULL, NULL);
12902 return mPeer->lockHandle();
12903}
12904
12905// IInternalMachineControl methods
12906////////////////////////////////////////////////////////////////////////////////
12907
12908/**
12909 * Passes collected guest statistics to performance collector object
12910 */
12911HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12912 ULONG aCpuKernel, ULONG aCpuIdle,
12913 ULONG aMemTotal, ULONG aMemFree,
12914 ULONG aMemBalloon, ULONG aMemShared,
12915 ULONG aMemCache, ULONG aPageTotal,
12916 ULONG aAllocVMM, ULONG aFreeVMM,
12917 ULONG aBalloonedVMM, ULONG aSharedVMM,
12918 ULONG aVmNetRx, ULONG aVmNetTx)
12919{
12920#ifdef VBOX_WITH_RESOURCE_USAGE_API
12921 if (mCollectorGuest)
12922 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12923 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12924 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12925 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12926
12927 return S_OK;
12928#else
12929 NOREF(aValidStats);
12930 NOREF(aCpuUser);
12931 NOREF(aCpuKernel);
12932 NOREF(aCpuIdle);
12933 NOREF(aMemTotal);
12934 NOREF(aMemFree);
12935 NOREF(aMemBalloon);
12936 NOREF(aMemShared);
12937 NOREF(aMemCache);
12938 NOREF(aPageTotal);
12939 NOREF(aAllocVMM);
12940 NOREF(aFreeVMM);
12941 NOREF(aBalloonedVMM);
12942 NOREF(aSharedVMM);
12943 NOREF(aVmNetRx);
12944 NOREF(aVmNetTx);
12945 return E_NOTIMPL;
12946#endif
12947}
12948
12949////////////////////////////////////////////////////////////////////////////////
12950//
12951// SessionMachine task records
12952//
12953////////////////////////////////////////////////////////////////////////////////
12954
12955/**
12956 * Task record for saving the machine state.
12957 */
12958class SessionMachine::SaveStateTask
12959 : public Machine::Task
12960{
12961public:
12962 SaveStateTask(SessionMachine *m,
12963 Progress *p,
12964 const Utf8Str &t,
12965 Reason_T enmReason,
12966 const Utf8Str &strStateFilePath)
12967 : Task(m, p, t),
12968 m_enmReason(enmReason),
12969 m_strStateFilePath(strStateFilePath)
12970 {}
12971
12972private:
12973 void handler()
12974 {
12975 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12976 }
12977
12978 Reason_T m_enmReason;
12979 Utf8Str m_strStateFilePath;
12980
12981 friend class SessionMachine;
12982};
12983
12984/**
12985 * Task thread implementation for SessionMachine::SaveState(), called from
12986 * SessionMachine::taskHandler().
12987 *
12988 * @note Locks this object for writing.
12989 *
12990 * @param task
12991 * @return
12992 */
12993void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12994{
12995 LogFlowThisFuncEnter();
12996
12997 AutoCaller autoCaller(this);
12998 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12999 if (FAILED(autoCaller.rc()))
13000 {
13001 /* we might have been uninitialized because the session was accidentally
13002 * closed by the client, so don't assert */
13003 HRESULT rc = setError(E_FAIL,
13004 tr("The session has been accidentally closed"));
13005 task.m_pProgress->i_notifyComplete(rc);
13006 LogFlowThisFuncLeave();
13007 return;
13008 }
13009
13010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13011
13012 HRESULT rc = S_OK;
13013
13014 try
13015 {
13016 ComPtr<IInternalSessionControl> directControl;
13017 if (mData->mSession.mLockType == LockType_VM)
13018 directControl = mData->mSession.mDirectControl;
13019 if (directControl.isNull())
13020 throw setError(VBOX_E_INVALID_VM_STATE,
13021 tr("Trying to save state without a running VM"));
13022 alock.release();
13023 BOOL fSuspendedBySave;
13024 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13025 Assert(!fSuspendedBySave);
13026 alock.acquire();
13027
13028 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13029 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13030 throw E_FAIL);
13031
13032 if (SUCCEEDED(rc))
13033 {
13034 mSSData->strStateFilePath = task.m_strStateFilePath;
13035
13036 /* save all VM settings */
13037 rc = i_saveSettings(NULL, alock);
13038 // no need to check whether VirtualBox.xml needs saving also since
13039 // we can't have a name change pending at this point
13040 }
13041 else
13042 {
13043 // On failure, set the state to the state we had at the beginning.
13044 i_setMachineState(task.m_machineStateBackup);
13045 i_updateMachineStateOnClient();
13046
13047 // Delete the saved state file (might have been already created).
13048 // No need to check whether this is shared with a snapshot here
13049 // because we certainly created a fresh saved state file here.
13050 RTFileDelete(task.m_strStateFilePath.c_str());
13051 }
13052 }
13053 catch (HRESULT aRC) { rc = aRC; }
13054
13055 task.m_pProgress->i_notifyComplete(rc);
13056
13057 LogFlowThisFuncLeave();
13058}
13059
13060/**
13061 * @note Locks this object for writing.
13062 */
13063HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13064{
13065 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13066}
13067
13068HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13069{
13070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13071
13072 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13073 if (FAILED(rc)) return rc;
13074
13075 if ( mData->mMachineState != MachineState_Running
13076 && mData->mMachineState != MachineState_Paused
13077 )
13078 return setError(VBOX_E_INVALID_VM_STATE,
13079 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13080 Global::stringifyMachineState(mData->mMachineState));
13081
13082 ComObjPtr<Progress> pProgress;
13083 pProgress.createObject();
13084 rc = pProgress->init(i_getVirtualBox(),
13085 static_cast<IMachine *>(this) /* aInitiator */,
13086 tr("Saving the execution state of the virtual machine"),
13087 FALSE /* aCancelable */);
13088 if (FAILED(rc))
13089 return rc;
13090
13091 Utf8Str strStateFilePath;
13092 i_composeSavedStateFilename(strStateFilePath);
13093
13094 /* create and start the task on a separate thread (note that it will not
13095 * start working until we release alock) */
13096 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13097 rc = pTask->createThread();
13098 if (FAILED(rc))
13099 return rc;
13100
13101 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13102 i_setMachineState(MachineState_Saving);
13103 i_updateMachineStateOnClient();
13104
13105 pProgress.queryInterfaceTo(aProgress.asOutParam());
13106
13107 return S_OK;
13108}
13109
13110/**
13111 * @note Locks this object for writing.
13112 */
13113HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13114{
13115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13116
13117 HRESULT rc = i_checkStateDependency(MutableStateDep);
13118 if (FAILED(rc)) return rc;
13119
13120 if ( mData->mMachineState != MachineState_PoweredOff
13121 && mData->mMachineState != MachineState_Teleported
13122 && mData->mMachineState != MachineState_Aborted
13123 )
13124 return setError(VBOX_E_INVALID_VM_STATE,
13125 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13126 Global::stringifyMachineState(mData->mMachineState));
13127
13128 com::Utf8Str stateFilePathFull;
13129 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13130 if (RT_FAILURE(vrc))
13131 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13132 tr("Invalid saved state file path '%s' (%Rrc)"),
13133 aSavedStateFile.c_str(),
13134 vrc);
13135
13136 mSSData->strStateFilePath = stateFilePathFull;
13137
13138 /* The below i_setMachineState() will detect the state transition and will
13139 * update the settings file */
13140
13141 return i_setMachineState(MachineState_Saved);
13142}
13143
13144/**
13145 * @note Locks this object for writing.
13146 */
13147HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13148{
13149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13150
13151 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13152 if (FAILED(rc)) return rc;
13153
13154 if ( mData->mMachineState != MachineState_Saved
13155 && mData->mMachineState != MachineState_AbortedSaved)
13156 return setError(VBOX_E_INVALID_VM_STATE,
13157 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13158 Global::stringifyMachineState(mData->mMachineState));
13159
13160 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13161
13162 /*
13163 * Saved -> PoweredOff transition will be detected in the SessionMachine
13164 * and properly handled.
13165 */
13166 rc = i_setMachineState(MachineState_PoweredOff);
13167 return rc;
13168}
13169
13170
13171/**
13172 * @note Locks the same as #i_setMachineState() does.
13173 */
13174HRESULT SessionMachine::updateState(MachineState_T aState)
13175{
13176 return i_setMachineState(aState);
13177}
13178
13179/**
13180 * @note Locks this object for writing.
13181 */
13182HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13183{
13184 IProgress *pProgress(aProgress);
13185
13186 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13187
13188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13189
13190 if (mData->mSession.mState != SessionState_Locked)
13191 return VBOX_E_INVALID_OBJECT_STATE;
13192
13193 if (!mData->mSession.mProgress.isNull())
13194 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13195
13196 /* If we didn't reference the NAT network service yet, add a reference to
13197 * force a start */
13198 if (miNATNetworksStarted < 1)
13199 {
13200 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13201 {
13202 BOOL enabled;
13203 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13204 if ( FAILED(hrc)
13205 || !enabled)
13206 continue;
13207
13208 NetworkAttachmentType_T type;
13209 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13210 if ( SUCCEEDED(hrc)
13211 && type == NetworkAttachmentType_NATNetwork)
13212 {
13213 Bstr name;
13214 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13215 if (SUCCEEDED(hrc))
13216 {
13217 Utf8Str strName(name);
13218 LogRel(("VM '%s' starts using NAT network '%s'\n",
13219 mUserData->s.strName.c_str(), strName.c_str()));
13220 mPeer->lockHandle()->unlockWrite();
13221 mParent->i_natNetworkRefInc(strName);
13222#ifdef RT_LOCK_STRICT
13223 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13224#else
13225 mPeer->lockHandle()->lockWrite();
13226#endif
13227 }
13228 }
13229 }
13230 miNATNetworksStarted++;
13231 }
13232
13233 LogFlowThisFunc(("returns S_OK.\n"));
13234 return S_OK;
13235}
13236
13237/**
13238 * @note Locks this object for writing.
13239 */
13240HRESULT SessionMachine::endPowerUp(LONG aResult)
13241{
13242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13243
13244 if (mData->mSession.mState != SessionState_Locked)
13245 return VBOX_E_INVALID_OBJECT_STATE;
13246
13247 /* Finalize the LaunchVMProcess progress object. */
13248 if (mData->mSession.mProgress)
13249 {
13250 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13251 mData->mSession.mProgress.setNull();
13252 }
13253
13254 if (SUCCEEDED((HRESULT)aResult))
13255 {
13256#ifdef VBOX_WITH_RESOURCE_USAGE_API
13257 /* The VM has been powered up successfully, so it makes sense
13258 * now to offer the performance metrics for a running machine
13259 * object. Doing it earlier wouldn't be safe. */
13260 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13261 mData->mSession.mPID);
13262#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13263 }
13264
13265 return S_OK;
13266}
13267
13268/**
13269 * @note Locks this object for writing.
13270 */
13271HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13272{
13273 LogFlowThisFuncEnter();
13274
13275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13276
13277 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13278 E_FAIL);
13279
13280 /* create a progress object to track operation completion */
13281 ComObjPtr<Progress> pProgress;
13282 pProgress.createObject();
13283 pProgress->init(i_getVirtualBox(),
13284 static_cast<IMachine *>(this) /* aInitiator */,
13285 tr("Stopping the virtual machine"),
13286 FALSE /* aCancelable */);
13287
13288 /* fill in the console task data */
13289 mConsoleTaskData.mLastState = mData->mMachineState;
13290 mConsoleTaskData.mProgress = pProgress;
13291
13292 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13293 i_setMachineState(MachineState_Stopping);
13294
13295 pProgress.queryInterfaceTo(aProgress.asOutParam());
13296
13297 return S_OK;
13298}
13299
13300/**
13301 * @note Locks this object for writing.
13302 */
13303HRESULT SessionMachine::endPoweringDown(LONG aResult,
13304 const com::Utf8Str &aErrMsg)
13305{
13306 HRESULT const hrcResult = (HRESULT)aResult;
13307 LogFlowThisFuncEnter();
13308
13309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13310
13311 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13312 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13313 && mConsoleTaskData.mLastState != MachineState_Null,
13314 E_FAIL);
13315
13316 /*
13317 * On failure, set the state to the state we had when BeginPoweringDown()
13318 * was called (this is expected by Console::PowerDown() and the associated
13319 * task). On success the VM process already changed the state to
13320 * MachineState_PoweredOff, so no need to do anything.
13321 */
13322 if (FAILED(hrcResult))
13323 i_setMachineState(mConsoleTaskData.mLastState);
13324
13325 /* notify the progress object about operation completion */
13326 Assert(mConsoleTaskData.mProgress);
13327 if (SUCCEEDED(hrcResult))
13328 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13329 else
13330 {
13331 if (aErrMsg.length())
13332 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13333 COM_IIDOF(ISession),
13334 getComponentName(),
13335 aErrMsg.c_str());
13336 else
13337 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13338 }
13339
13340 /* clear out the temporary saved state data */
13341 mConsoleTaskData.mLastState = MachineState_Null;
13342 mConsoleTaskData.mProgress.setNull();
13343
13344 LogFlowThisFuncLeave();
13345 return S_OK;
13346}
13347
13348
13349/**
13350 * Goes through the USB filters of the given machine to see if the given
13351 * device matches any filter or not.
13352 *
13353 * @note Locks the same as USBController::hasMatchingFilter() does.
13354 */
13355HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13356 BOOL *aMatched,
13357 ULONG *aMaskedInterfaces)
13358{
13359 LogFlowThisFunc(("\n"));
13360
13361#ifdef VBOX_WITH_USB
13362 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13363#else
13364 NOREF(aDevice);
13365 NOREF(aMaskedInterfaces);
13366 *aMatched = FALSE;
13367#endif
13368
13369 return S_OK;
13370}
13371
13372/**
13373 * @note Locks the same as Host::captureUSBDevice() does.
13374 */
13375HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13376{
13377 LogFlowThisFunc(("\n"));
13378
13379#ifdef VBOX_WITH_USB
13380 /* if captureDeviceForVM() fails, it must have set extended error info */
13381 clearError();
13382 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13383 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13384 return rc;
13385
13386 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13387 AssertReturn(service, E_FAIL);
13388 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13389#else
13390 RT_NOREF(aId, aCaptureFilename);
13391 return E_NOTIMPL;
13392#endif
13393}
13394
13395/**
13396 * @note Locks the same as Host::detachUSBDevice() does.
13397 */
13398HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13399 BOOL aDone)
13400{
13401 LogFlowThisFunc(("\n"));
13402
13403#ifdef VBOX_WITH_USB
13404 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13405 AssertReturn(service, E_FAIL);
13406 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13407#else
13408 NOREF(aId);
13409 NOREF(aDone);
13410 return E_NOTIMPL;
13411#endif
13412}
13413
13414/**
13415 * Inserts all machine filters to the USB proxy service and then calls
13416 * Host::autoCaptureUSBDevices().
13417 *
13418 * Called by Console from the VM process upon VM startup.
13419 *
13420 * @note Locks what called methods lock.
13421 */
13422HRESULT SessionMachine::autoCaptureUSBDevices()
13423{
13424 LogFlowThisFunc(("\n"));
13425
13426#ifdef VBOX_WITH_USB
13427 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13428 AssertComRC(rc);
13429 NOREF(rc);
13430
13431 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13432 AssertReturn(service, E_FAIL);
13433 return service->autoCaptureDevicesForVM(this);
13434#else
13435 return S_OK;
13436#endif
13437}
13438
13439/**
13440 * Removes all machine filters from the USB proxy service and then calls
13441 * Host::detachAllUSBDevices().
13442 *
13443 * Called by Console from the VM process upon normal VM termination or by
13444 * SessionMachine::uninit() upon abnormal VM termination (from under the
13445 * Machine/SessionMachine lock).
13446 *
13447 * @note Locks what called methods lock.
13448 */
13449HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13450{
13451 LogFlowThisFunc(("\n"));
13452
13453#ifdef VBOX_WITH_USB
13454 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13455 AssertComRC(rc);
13456 NOREF(rc);
13457
13458 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13459 AssertReturn(service, E_FAIL);
13460 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13461#else
13462 NOREF(aDone);
13463 return S_OK;
13464#endif
13465}
13466
13467/**
13468 * @note Locks this object for writing.
13469 */
13470HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13471 ComPtr<IProgress> &aProgress)
13472{
13473 LogFlowThisFuncEnter();
13474
13475 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13476 /*
13477 * We don't assert below because it might happen that a non-direct session
13478 * informs us it is closed right after we've been uninitialized -- it's ok.
13479 */
13480
13481 /* get IInternalSessionControl interface */
13482 ComPtr<IInternalSessionControl> control(aSession);
13483
13484 ComAssertRet(!control.isNull(), E_INVALIDARG);
13485
13486 /* Creating a Progress object requires the VirtualBox lock, and
13487 * thus locking it here is required by the lock order rules. */
13488 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13489
13490 if (control == mData->mSession.mDirectControl)
13491 {
13492 /* The direct session is being normally closed by the client process
13493 * ----------------------------------------------------------------- */
13494
13495 /* go to the closing state (essential for all open*Session() calls and
13496 * for #i_checkForDeath()) */
13497 Assert(mData->mSession.mState == SessionState_Locked);
13498 mData->mSession.mState = SessionState_Unlocking;
13499
13500 /* set direct control to NULL to release the remote instance */
13501 mData->mSession.mDirectControl.setNull();
13502 LogFlowThisFunc(("Direct control is set to NULL\n"));
13503
13504 if (mData->mSession.mProgress)
13505 {
13506 /* finalize the progress, someone might wait if a frontend
13507 * closes the session before powering on the VM. */
13508 mData->mSession.mProgress->notifyComplete(E_FAIL,
13509 COM_IIDOF(ISession),
13510 getComponentName(),
13511 tr("The VM session was closed before any attempt to power it on"));
13512 mData->mSession.mProgress.setNull();
13513 }
13514
13515 /* Create the progress object the client will use to wait until
13516 * #i_checkForDeath() is called to uninitialize this session object after
13517 * it releases the IPC semaphore.
13518 * Note! Because we're "reusing" mProgress here, this must be a proxy
13519 * object just like for LaunchVMProcess. */
13520 Assert(mData->mSession.mProgress.isNull());
13521 ComObjPtr<ProgressProxy> progress;
13522 progress.createObject();
13523 ComPtr<IUnknown> pPeer(mPeer);
13524 progress->init(mParent, pPeer,
13525 Bstr(tr("Closing session")).raw(),
13526 FALSE /* aCancelable */);
13527 progress.queryInterfaceTo(aProgress.asOutParam());
13528 mData->mSession.mProgress = progress;
13529 }
13530 else
13531 {
13532 /* the remote session is being normally closed */
13533 bool found = false;
13534 for (Data::Session::RemoteControlList::iterator
13535 it = mData->mSession.mRemoteControls.begin();
13536 it != mData->mSession.mRemoteControls.end();
13537 ++it)
13538 {
13539 if (control == *it)
13540 {
13541 found = true;
13542 // This MUST be erase(it), not remove(*it) as the latter
13543 // triggers a very nasty use after free due to the place where
13544 // the value "lives".
13545 mData->mSession.mRemoteControls.erase(it);
13546 break;
13547 }
13548 }
13549 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13550 E_INVALIDARG);
13551 }
13552
13553 /* signal the client watcher thread, because the client is going away */
13554 mParent->i_updateClientWatcher();
13555
13556 LogFlowThisFuncLeave();
13557 return S_OK;
13558}
13559
13560HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13561 std::vector<com::Utf8Str> &aValues,
13562 std::vector<LONG64> &aTimestamps,
13563 std::vector<com::Utf8Str> &aFlags)
13564{
13565 LogFlowThisFunc(("\n"));
13566
13567#ifdef VBOX_WITH_GUEST_PROPS
13568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13569
13570 size_t cEntries = mHWData->mGuestProperties.size();
13571 aNames.resize(cEntries);
13572 aValues.resize(cEntries);
13573 aTimestamps.resize(cEntries);
13574 aFlags.resize(cEntries);
13575
13576 size_t i = 0;
13577 for (HWData::GuestPropertyMap::const_iterator
13578 it = mHWData->mGuestProperties.begin();
13579 it != mHWData->mGuestProperties.end();
13580 ++it, ++i)
13581 {
13582 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13583 aNames[i] = it->first;
13584 aValues[i] = it->second.strValue;
13585 aTimestamps[i] = it->second.mTimestamp;
13586
13587 /* If it is NULL, keep it NULL. */
13588 if (it->second.mFlags)
13589 {
13590 GuestPropWriteFlags(it->second.mFlags, szFlags);
13591 aFlags[i] = szFlags;
13592 }
13593 else
13594 aFlags[i] = "";
13595
13596 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13597 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13598 }
13599 return S_OK;
13600#else
13601 ReturnComNotImplemented();
13602#endif
13603}
13604
13605HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13606 const com::Utf8Str &aValue,
13607 LONG64 aTimestamp,
13608 const com::Utf8Str &aFlags,
13609 BOOL fWasDeleted)
13610{
13611 LogFlowThisFunc(("\n"));
13612
13613#ifdef VBOX_WITH_GUEST_PROPS
13614 try
13615 {
13616 /*
13617 * Convert input up front.
13618 */
13619 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13620 if (aFlags.length())
13621 {
13622 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13623 AssertRCReturn(vrc, E_INVALIDARG);
13624 }
13625
13626 /*
13627 * Now grab the object lock, validate the state and do the update.
13628 */
13629
13630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13631
13632 if (!Global::IsOnline(mData->mMachineState))
13633 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13634
13635 i_setModified(IsModified_MachineData);
13636 mHWData.backup();
13637
13638 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13639 if (it != mHWData->mGuestProperties.end())
13640 {
13641 if (!fWasDeleted)
13642 {
13643 it->second.strValue = aValue;
13644 it->second.mTimestamp = aTimestamp;
13645 it->second.mFlags = fFlags;
13646 }
13647 else
13648 mHWData->mGuestProperties.erase(it);
13649
13650 mData->mGuestPropertiesModified = TRUE;
13651 }
13652 else if (!fWasDeleted)
13653 {
13654 HWData::GuestProperty prop;
13655 prop.strValue = aValue;
13656 prop.mTimestamp = aTimestamp;
13657 prop.mFlags = fFlags;
13658
13659 mHWData->mGuestProperties[aName] = prop;
13660 mData->mGuestPropertiesModified = TRUE;
13661 }
13662
13663 alock.release();
13664
13665 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13666 }
13667 catch (...)
13668 {
13669 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13670 }
13671 return S_OK;
13672#else
13673 ReturnComNotImplemented();
13674#endif
13675}
13676
13677
13678HRESULT SessionMachine::lockMedia()
13679{
13680 AutoMultiWriteLock2 alock(this->lockHandle(),
13681 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13682
13683 AssertReturn( mData->mMachineState == MachineState_Starting
13684 || mData->mMachineState == MachineState_Restoring
13685 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13686
13687 clearError();
13688 alock.release();
13689 return i_lockMedia();
13690}
13691
13692HRESULT SessionMachine::unlockMedia()
13693{
13694 HRESULT hrc = i_unlockMedia();
13695 return hrc;
13696}
13697
13698HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13699 ComPtr<IMediumAttachment> &aNewAttachment)
13700{
13701 // request the host lock first, since might be calling Host methods for getting host drives;
13702 // next, protect the media tree all the while we're in here, as well as our member variables
13703 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13704 this->lockHandle(),
13705 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13706
13707 IMediumAttachment *iAttach = aAttachment;
13708 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13709
13710 Utf8Str ctrlName;
13711 LONG lPort;
13712 LONG lDevice;
13713 bool fTempEject;
13714 {
13715 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13716
13717 /* Need to query the details first, as the IMediumAttachment reference
13718 * might be to the original settings, which we are going to change. */
13719 ctrlName = pAttach->i_getControllerName();
13720 lPort = pAttach->i_getPort();
13721 lDevice = pAttach->i_getDevice();
13722 fTempEject = pAttach->i_getTempEject();
13723 }
13724
13725 if (!fTempEject)
13726 {
13727 /* Remember previously mounted medium. The medium before taking the
13728 * backup is not necessarily the same thing. */
13729 ComObjPtr<Medium> oldmedium;
13730 oldmedium = pAttach->i_getMedium();
13731
13732 i_setModified(IsModified_Storage);
13733 mMediumAttachments.backup();
13734
13735 // The backup operation makes the pAttach reference point to the
13736 // old settings. Re-get the correct reference.
13737 pAttach = i_findAttachment(*mMediumAttachments.data(),
13738 ctrlName,
13739 lPort,
13740 lDevice);
13741
13742 {
13743 AutoCaller autoAttachCaller(this);
13744 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13745
13746 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13747 if (!oldmedium.isNull())
13748 oldmedium->i_removeBackReference(mData->mUuid);
13749
13750 pAttach->i_updateMedium(NULL);
13751 pAttach->i_updateEjected();
13752 }
13753
13754 i_setModified(IsModified_Storage);
13755 }
13756 else
13757 {
13758 {
13759 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13760 pAttach->i_updateEjected();
13761 }
13762 }
13763
13764 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13765
13766 return S_OK;
13767}
13768
13769HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13770 com::Utf8Str &aResult)
13771{
13772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13773
13774 HRESULT hr = S_OK;
13775
13776 if (!mAuthLibCtx.hAuthLibrary)
13777 {
13778 /* Load the external authentication library. */
13779 Bstr authLibrary;
13780 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13781
13782 Utf8Str filename = authLibrary;
13783
13784 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13785 if (RT_FAILURE(vrc))
13786 hr = setErrorBoth(E_FAIL, vrc,
13787 tr("Could not load the external authentication library '%s' (%Rrc)"),
13788 filename.c_str(), vrc);
13789 }
13790
13791 /* The auth library might need the machine lock. */
13792 alock.release();
13793
13794 if (FAILED(hr))
13795 return hr;
13796
13797 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13798 {
13799 enum VRDEAuthParams
13800 {
13801 parmUuid = 1,
13802 parmGuestJudgement,
13803 parmUser,
13804 parmPassword,
13805 parmDomain,
13806 parmClientId
13807 };
13808
13809 AuthResult result = AuthResultAccessDenied;
13810
13811 Guid uuid(aAuthParams[parmUuid]);
13812 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13813 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13814
13815 result = AuthLibAuthenticate(&mAuthLibCtx,
13816 uuid.raw(), guestJudgement,
13817 aAuthParams[parmUser].c_str(),
13818 aAuthParams[parmPassword].c_str(),
13819 aAuthParams[parmDomain].c_str(),
13820 u32ClientId);
13821
13822 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13823 size_t cbPassword = aAuthParams[parmPassword].length();
13824 if (cbPassword)
13825 {
13826 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13827 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13828 }
13829
13830 if (result == AuthResultAccessGranted)
13831 aResult = "granted";
13832 else
13833 aResult = "denied";
13834
13835 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13836 aAuthParams[parmUser].c_str(), aResult.c_str()));
13837 }
13838 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13839 {
13840 enum VRDEAuthDisconnectParams
13841 {
13842 parmUuid = 1,
13843 parmClientId
13844 };
13845
13846 Guid uuid(aAuthParams[parmUuid]);
13847 uint32_t u32ClientId = 0;
13848 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13849 }
13850 else
13851 {
13852 hr = E_INVALIDARG;
13853 }
13854
13855 return hr;
13856}
13857
13858// public methods only for internal purposes
13859/////////////////////////////////////////////////////////////////////////////
13860
13861#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13862/**
13863 * Called from the client watcher thread to check for expected or unexpected
13864 * death of the client process that has a direct session to this machine.
13865 *
13866 * On Win32 and on OS/2, this method is called only when we've got the
13867 * mutex (i.e. the client has either died or terminated normally) so it always
13868 * returns @c true (the client is terminated, the session machine is
13869 * uninitialized).
13870 *
13871 * On other platforms, the method returns @c true if the client process has
13872 * terminated normally or abnormally and the session machine was uninitialized,
13873 * and @c false if the client process is still alive.
13874 *
13875 * @note Locks this object for writing.
13876 */
13877bool SessionMachine::i_checkForDeath()
13878{
13879 Uninit::Reason reason;
13880 bool terminated = false;
13881
13882 /* Enclose autoCaller with a block because calling uninit() from under it
13883 * will deadlock. */
13884 {
13885 AutoCaller autoCaller(this);
13886 if (!autoCaller.isOk())
13887 {
13888 /* return true if not ready, to cause the client watcher to exclude
13889 * the corresponding session from watching */
13890 LogFlowThisFunc(("Already uninitialized!\n"));
13891 return true;
13892 }
13893
13894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13895
13896 /* Determine the reason of death: if the session state is Closing here,
13897 * everything is fine. Otherwise it means that the client did not call
13898 * OnSessionEnd() before it released the IPC semaphore. This may happen
13899 * either because the client process has abnormally terminated, or
13900 * because it simply forgot to call ISession::Close() before exiting. We
13901 * threat the latter also as an abnormal termination (see
13902 * Session::uninit() for details). */
13903 reason = mData->mSession.mState == SessionState_Unlocking ?
13904 Uninit::Normal :
13905 Uninit::Abnormal;
13906
13907 if (mClientToken)
13908 terminated = mClientToken->release();
13909 } /* AutoCaller block */
13910
13911 if (terminated)
13912 uninit(reason);
13913
13914 return terminated;
13915}
13916
13917void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13918{
13919 LogFlowThisFunc(("\n"));
13920
13921 strTokenId.setNull();
13922
13923 AutoCaller autoCaller(this);
13924 AssertComRCReturnVoid(autoCaller.rc());
13925
13926 Assert(mClientToken);
13927 if (mClientToken)
13928 mClientToken->getId(strTokenId);
13929}
13930#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13931IToken *SessionMachine::i_getToken()
13932{
13933 LogFlowThisFunc(("\n"));
13934
13935 AutoCaller autoCaller(this);
13936 AssertComRCReturn(autoCaller.rc(), NULL);
13937
13938 Assert(mClientToken);
13939 if (mClientToken)
13940 return mClientToken->getToken();
13941 else
13942 return NULL;
13943}
13944#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13945
13946Machine::ClientToken *SessionMachine::i_getClientToken()
13947{
13948 LogFlowThisFunc(("\n"));
13949
13950 AutoCaller autoCaller(this);
13951 AssertComRCReturn(autoCaller.rc(), NULL);
13952
13953 return mClientToken;
13954}
13955
13956
13957/**
13958 * @note Locks this object for reading.
13959 */
13960HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13961{
13962 LogFlowThisFunc(("\n"));
13963
13964 AutoCaller autoCaller(this);
13965 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13966
13967 ComPtr<IInternalSessionControl> directControl;
13968 {
13969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13970 if (mData->mSession.mLockType == LockType_VM)
13971 directControl = mData->mSession.mDirectControl;
13972 }
13973
13974 /* ignore notifications sent after #OnSessionEnd() is called */
13975 if (!directControl)
13976 return S_OK;
13977
13978 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13979}
13980
13981/**
13982 * @note Locks this object for reading.
13983 */
13984HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13985 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13986 const Utf8Str &aGuestIp, LONG aGuestPort)
13987{
13988 LogFlowThisFunc(("\n"));
13989
13990 AutoCaller autoCaller(this);
13991 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13992
13993 ComPtr<IInternalSessionControl> directControl;
13994 {
13995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13996 if (mData->mSession.mLockType == LockType_VM)
13997 directControl = mData->mSession.mDirectControl;
13998 }
13999
14000 /* ignore notifications sent after #OnSessionEnd() is called */
14001 if (!directControl)
14002 return S_OK;
14003 /*
14004 * instead acting like callback we ask IVirtualBox deliver corresponding event
14005 */
14006
14007 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14008 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14009 return S_OK;
14010}
14011
14012/**
14013 * @note Locks this object for reading.
14014 */
14015HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14016{
14017 LogFlowThisFunc(("\n"));
14018
14019 AutoCaller autoCaller(this);
14020 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14021
14022 ComPtr<IInternalSessionControl> directControl;
14023 {
14024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14025 if (mData->mSession.mLockType == LockType_VM)
14026 directControl = mData->mSession.mDirectControl;
14027 }
14028
14029 /* ignore notifications sent after #OnSessionEnd() is called */
14030 if (!directControl)
14031 return S_OK;
14032
14033 return directControl->OnAudioAdapterChange(audioAdapter);
14034}
14035
14036/**
14037 * @note Locks this object for reading.
14038 */
14039HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14040{
14041 LogFlowThisFunc(("\n"));
14042
14043 AutoCaller autoCaller(this);
14044 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14045
14046 ComPtr<IInternalSessionControl> directControl;
14047 {
14048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14049 if (mData->mSession.mLockType == LockType_VM)
14050 directControl = mData->mSession.mDirectControl;
14051 }
14052
14053 /* ignore notifications sent after #OnSessionEnd() is called */
14054 if (!directControl)
14055 return S_OK;
14056
14057 return directControl->OnSerialPortChange(serialPort);
14058}
14059
14060/**
14061 * @note Locks this object for reading.
14062 */
14063HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14064{
14065 LogFlowThisFunc(("\n"));
14066
14067 AutoCaller autoCaller(this);
14068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14069
14070 ComPtr<IInternalSessionControl> directControl;
14071 {
14072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14073 if (mData->mSession.mLockType == LockType_VM)
14074 directControl = mData->mSession.mDirectControl;
14075 }
14076
14077 /* ignore notifications sent after #OnSessionEnd() is called */
14078 if (!directControl)
14079 return S_OK;
14080
14081 return directControl->OnParallelPortChange(parallelPort);
14082}
14083
14084/**
14085 * @note Locks this object for reading.
14086 */
14087HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14088{
14089 LogFlowThisFunc(("\n"));
14090
14091 AutoCaller autoCaller(this);
14092 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14093
14094 ComPtr<IInternalSessionControl> directControl;
14095 {
14096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14097 if (mData->mSession.mLockType == LockType_VM)
14098 directControl = mData->mSession.mDirectControl;
14099 }
14100
14101 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14102
14103 /* ignore notifications sent after #OnSessionEnd() is called */
14104 if (!directControl)
14105 return S_OK;
14106
14107 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14108}
14109
14110/**
14111 * @note Locks this object for reading.
14112 */
14113HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14114{
14115 LogFlowThisFunc(("\n"));
14116
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14119
14120 ComPtr<IInternalSessionControl> directControl;
14121 {
14122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14123 if (mData->mSession.mLockType == LockType_VM)
14124 directControl = mData->mSession.mDirectControl;
14125 }
14126
14127 mParent->i_onMediumChanged(aAttachment);
14128
14129 /* ignore notifications sent after #OnSessionEnd() is called */
14130 if (!directControl)
14131 return S_OK;
14132
14133 return directControl->OnMediumChange(aAttachment, aForce);
14134}
14135
14136HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14137{
14138 LogFlowThisFunc(("\n"));
14139
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14142
14143 ComPtr<IInternalSessionControl> directControl;
14144 {
14145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14146 if (mData->mSession.mLockType == LockType_VM)
14147 directControl = mData->mSession.mDirectControl;
14148 }
14149
14150 /* ignore notifications sent after #OnSessionEnd() is called */
14151 if (!directControl)
14152 return S_OK;
14153
14154 return directControl->OnVMProcessPriorityChange(aPriority);
14155}
14156
14157/**
14158 * @note Locks this object for reading.
14159 */
14160HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14161{
14162 LogFlowThisFunc(("\n"));
14163
14164 AutoCaller autoCaller(this);
14165 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14166
14167 ComPtr<IInternalSessionControl> directControl;
14168 {
14169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14170 if (mData->mSession.mLockType == LockType_VM)
14171 directControl = mData->mSession.mDirectControl;
14172 }
14173
14174 /* ignore notifications sent after #OnSessionEnd() is called */
14175 if (!directControl)
14176 return S_OK;
14177
14178 return directControl->OnCPUChange(aCPU, aRemove);
14179}
14180
14181HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14182{
14183 LogFlowThisFunc(("\n"));
14184
14185 AutoCaller autoCaller(this);
14186 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14187
14188 ComPtr<IInternalSessionControl> directControl;
14189 {
14190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14191 if (mData->mSession.mLockType == LockType_VM)
14192 directControl = mData->mSession.mDirectControl;
14193 }
14194
14195 /* ignore notifications sent after #OnSessionEnd() is called */
14196 if (!directControl)
14197 return S_OK;
14198
14199 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14200}
14201
14202/**
14203 * @note Locks this object for reading.
14204 */
14205HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14206{
14207 LogFlowThisFunc(("\n"));
14208
14209 AutoCaller autoCaller(this);
14210 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14211
14212 ComPtr<IInternalSessionControl> directControl;
14213 {
14214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14215 if (mData->mSession.mLockType == LockType_VM)
14216 directControl = mData->mSession.mDirectControl;
14217 }
14218
14219 /* ignore notifications sent after #OnSessionEnd() is called */
14220 if (!directControl)
14221 return S_OK;
14222
14223 return directControl->OnVRDEServerChange(aRestart);
14224}
14225
14226/**
14227 * @note Locks this object for reading.
14228 */
14229HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14230{
14231 LogFlowThisFunc(("\n"));
14232
14233 AutoCaller autoCaller(this);
14234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14235
14236 ComPtr<IInternalSessionControl> directControl;
14237 {
14238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14239 if (mData->mSession.mLockType == LockType_VM)
14240 directControl = mData->mSession.mDirectControl;
14241 }
14242
14243 /* ignore notifications sent after #OnSessionEnd() is called */
14244 if (!directControl)
14245 return S_OK;
14246
14247 return directControl->OnRecordingChange(aEnable);
14248}
14249
14250/**
14251 * @note Locks this object for reading.
14252 */
14253HRESULT SessionMachine::i_onUSBControllerChange()
14254{
14255 LogFlowThisFunc(("\n"));
14256
14257 AutoCaller autoCaller(this);
14258 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14259
14260 ComPtr<IInternalSessionControl> directControl;
14261 {
14262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14263 if (mData->mSession.mLockType == LockType_VM)
14264 directControl = mData->mSession.mDirectControl;
14265 }
14266
14267 /* ignore notifications sent after #OnSessionEnd() is called */
14268 if (!directControl)
14269 return S_OK;
14270
14271 return directControl->OnUSBControllerChange();
14272}
14273
14274/**
14275 * @note Locks this object for reading.
14276 */
14277HRESULT SessionMachine::i_onSharedFolderChange()
14278{
14279 LogFlowThisFunc(("\n"));
14280
14281 AutoCaller autoCaller(this);
14282 AssertComRCReturnRC(autoCaller.rc());
14283
14284 ComPtr<IInternalSessionControl> directControl;
14285 {
14286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14287 if (mData->mSession.mLockType == LockType_VM)
14288 directControl = mData->mSession.mDirectControl;
14289 }
14290
14291 /* ignore notifications sent after #OnSessionEnd() is called */
14292 if (!directControl)
14293 return S_OK;
14294
14295 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14296}
14297
14298/**
14299 * @note Locks this object for reading.
14300 */
14301HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14302{
14303 LogFlowThisFunc(("\n"));
14304
14305 AutoCaller autoCaller(this);
14306 AssertComRCReturnRC(autoCaller.rc());
14307
14308 ComPtr<IInternalSessionControl> directControl;
14309 {
14310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14311 if (mData->mSession.mLockType == LockType_VM)
14312 directControl = mData->mSession.mDirectControl;
14313 }
14314
14315 /* ignore notifications sent after #OnSessionEnd() is called */
14316 if (!directControl)
14317 return S_OK;
14318
14319 return directControl->OnClipboardModeChange(aClipboardMode);
14320}
14321
14322/**
14323 * @note Locks this object for reading.
14324 */
14325HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14326{
14327 LogFlowThisFunc(("\n"));
14328
14329 AutoCaller autoCaller(this);
14330 AssertComRCReturnRC(autoCaller.rc());
14331
14332 ComPtr<IInternalSessionControl> directControl;
14333 {
14334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14335 if (mData->mSession.mLockType == LockType_VM)
14336 directControl = mData->mSession.mDirectControl;
14337 }
14338
14339 /* ignore notifications sent after #OnSessionEnd() is called */
14340 if (!directControl)
14341 return S_OK;
14342
14343 return directControl->OnClipboardFileTransferModeChange(aEnable);
14344}
14345
14346/**
14347 * @note Locks this object for reading.
14348 */
14349HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14350{
14351 LogFlowThisFunc(("\n"));
14352
14353 AutoCaller autoCaller(this);
14354 AssertComRCReturnRC(autoCaller.rc());
14355
14356 ComPtr<IInternalSessionControl> directControl;
14357 {
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359 if (mData->mSession.mLockType == LockType_VM)
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* ignore notifications sent after #OnSessionEnd() is called */
14364 if (!directControl)
14365 return S_OK;
14366
14367 return directControl->OnDnDModeChange(aDnDMode);
14368}
14369
14370/**
14371 * @note Locks this object for reading.
14372 */
14373HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14374{
14375 LogFlowThisFunc(("\n"));
14376
14377 AutoCaller autoCaller(this);
14378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14379
14380 ComPtr<IInternalSessionControl> directControl;
14381 {
14382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14383 if (mData->mSession.mLockType == LockType_VM)
14384 directControl = mData->mSession.mDirectControl;
14385 }
14386
14387 /* ignore notifications sent after #OnSessionEnd() is called */
14388 if (!directControl)
14389 return S_OK;
14390
14391 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14392}
14393
14394/**
14395 * @note Locks this object for reading.
14396 */
14397HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14398{
14399 LogFlowThisFunc(("\n"));
14400
14401 AutoCaller autoCaller(this);
14402 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14403
14404 ComPtr<IInternalSessionControl> directControl;
14405 {
14406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14407 if (mData->mSession.mLockType == LockType_VM)
14408 directControl = mData->mSession.mDirectControl;
14409 }
14410
14411 /* ignore notifications sent after #OnSessionEnd() is called */
14412 if (!directControl)
14413 return S_OK;
14414
14415 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14416}
14417
14418/**
14419 * Returns @c true if this machine's USB controller reports it has a matching
14420 * filter for the given USB device and @c false otherwise.
14421 *
14422 * @note locks this object for reading.
14423 */
14424bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14425{
14426 AutoCaller autoCaller(this);
14427 /* silently return if not ready -- this method may be called after the
14428 * direct machine session has been called */
14429 if (!autoCaller.isOk())
14430 return false;
14431
14432#ifdef VBOX_WITH_USB
14433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14434
14435 switch (mData->mMachineState)
14436 {
14437 case MachineState_Starting:
14438 case MachineState_Restoring:
14439 case MachineState_TeleportingIn:
14440 case MachineState_Paused:
14441 case MachineState_Running:
14442 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14443 * elsewhere... */
14444 alock.release();
14445 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14446 default: break;
14447 }
14448#else
14449 NOREF(aDevice);
14450 NOREF(aMaskedIfs);
14451#endif
14452 return false;
14453}
14454
14455/**
14456 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14457 */
14458HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14459 IVirtualBoxErrorInfo *aError,
14460 ULONG aMaskedIfs,
14461 const com::Utf8Str &aCaptureFilename)
14462{
14463 LogFlowThisFunc(("\n"));
14464
14465 AutoCaller autoCaller(this);
14466
14467 /* This notification may happen after the machine object has been
14468 * uninitialized (the session was closed), so don't assert. */
14469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14470
14471 ComPtr<IInternalSessionControl> directControl;
14472 {
14473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14474 if (mData->mSession.mLockType == LockType_VM)
14475 directControl = mData->mSession.mDirectControl;
14476 }
14477
14478 /* fail on notifications sent after #OnSessionEnd() is called, it is
14479 * expected by the caller */
14480 if (!directControl)
14481 return E_FAIL;
14482
14483 /* No locks should be held at this point. */
14484 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14485 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14486
14487 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14488}
14489
14490/**
14491 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14492 */
14493HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14494 IVirtualBoxErrorInfo *aError)
14495{
14496 LogFlowThisFunc(("\n"));
14497
14498 AutoCaller autoCaller(this);
14499
14500 /* This notification may happen after the machine object has been
14501 * uninitialized (the session was closed), so don't assert. */
14502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14503
14504 ComPtr<IInternalSessionControl> directControl;
14505 {
14506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14507 if (mData->mSession.mLockType == LockType_VM)
14508 directControl = mData->mSession.mDirectControl;
14509 }
14510
14511 /* fail on notifications sent after #OnSessionEnd() is called, it is
14512 * expected by the caller */
14513 if (!directControl)
14514 return E_FAIL;
14515
14516 /* No locks should be held at this point. */
14517 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14518 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14519
14520 return directControl->OnUSBDeviceDetach(aId, aError);
14521}
14522
14523// protected methods
14524/////////////////////////////////////////////////////////////////////////////
14525
14526/**
14527 * Deletes the given file if it is no longer in use by either the current machine state
14528 * (if the machine is "saved") or any of the machine's snapshots.
14529 *
14530 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14531 * but is different for each SnapshotMachine. When calling this, the order of calling this
14532 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14533 * is therefore critical. I know, it's all rather messy.
14534 *
14535 * @param strStateFile
14536 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14537 * the test for whether the saved state file is in use.
14538 */
14539void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14540 Snapshot *pSnapshotToIgnore)
14541{
14542 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14543 if ( (strStateFile.isNotEmpty())
14544 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14545 )
14546 // ... and it must also not be shared with other snapshots
14547 if ( !mData->mFirstSnapshot
14548 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14549 // this checks the SnapshotMachine's state file paths
14550 )
14551 RTFileDelete(strStateFile.c_str());
14552}
14553
14554/**
14555 * Locks the attached media.
14556 *
14557 * All attached hard disks are locked for writing and DVD/floppy are locked for
14558 * reading. Parents of attached hard disks (if any) are locked for reading.
14559 *
14560 * This method also performs accessibility check of all media it locks: if some
14561 * media is inaccessible, the method will return a failure and a bunch of
14562 * extended error info objects per each inaccessible medium.
14563 *
14564 * Note that this method is atomic: if it returns a success, all media are
14565 * locked as described above; on failure no media is locked at all (all
14566 * succeeded individual locks will be undone).
14567 *
14568 * The caller is responsible for doing the necessary state sanity checks.
14569 *
14570 * The locks made by this method must be undone by calling #unlockMedia() when
14571 * no more needed.
14572 */
14573HRESULT SessionMachine::i_lockMedia()
14574{
14575 AutoCaller autoCaller(this);
14576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14577
14578 AutoMultiWriteLock2 alock(this->lockHandle(),
14579 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14580
14581 /* bail out if trying to lock things with already set up locking */
14582 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14583
14584 MultiResult mrc(S_OK);
14585
14586 /* Collect locking information for all medium objects attached to the VM. */
14587 for (MediumAttachmentList::const_iterator
14588 it = mMediumAttachments->begin();
14589 it != mMediumAttachments->end();
14590 ++it)
14591 {
14592 MediumAttachment *pAtt = *it;
14593 DeviceType_T devType = pAtt->i_getType();
14594 Medium *pMedium = pAtt->i_getMedium();
14595
14596 MediumLockList *pMediumLockList(new MediumLockList());
14597 // There can be attachments without a medium (floppy/dvd), and thus
14598 // it's impossible to create a medium lock list. It still makes sense
14599 // to have the empty medium lock list in the map in case a medium is
14600 // attached later.
14601 if (pMedium != NULL)
14602 {
14603 MediumType_T mediumType = pMedium->i_getType();
14604 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14605 || mediumType == MediumType_Shareable;
14606 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14607
14608 alock.release();
14609 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14610 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14611 false /* fMediumLockWriteAll */,
14612 NULL,
14613 *pMediumLockList);
14614 alock.acquire();
14615 if (FAILED(mrc))
14616 {
14617 delete pMediumLockList;
14618 mData->mSession.mLockedMedia.Clear();
14619 break;
14620 }
14621 }
14622
14623 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14624 if (FAILED(rc))
14625 {
14626 mData->mSession.mLockedMedia.Clear();
14627 mrc = setError(rc,
14628 tr("Collecting locking information for all attached media failed"));
14629 break;
14630 }
14631 }
14632
14633 if (SUCCEEDED(mrc))
14634 {
14635 /* Now lock all media. If this fails, nothing is locked. */
14636 alock.release();
14637 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14638 alock.acquire();
14639 if (FAILED(rc))
14640 {
14641 mrc = setError(rc,
14642 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14643 }
14644 }
14645
14646 return mrc;
14647}
14648
14649/**
14650 * Undoes the locks made by by #lockMedia().
14651 */
14652HRESULT SessionMachine::i_unlockMedia()
14653{
14654 AutoCaller autoCaller(this);
14655 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14656
14657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14658
14659 /* we may be holding important error info on the current thread;
14660 * preserve it */
14661 ErrorInfoKeeper eik;
14662
14663 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14664 AssertComRC(rc);
14665 return rc;
14666}
14667
14668/**
14669 * Helper to change the machine state (reimplementation).
14670 *
14671 * @note Locks this object for writing.
14672 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14673 * it can cause crashes in random places due to unexpectedly committing
14674 * the current settings. The caller is responsible for that. The call
14675 * to saveStateSettings is fine, because this method does not commit.
14676 */
14677HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14678{
14679 LogFlowThisFuncEnter();
14680
14681 AutoCaller autoCaller(this);
14682 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14683
14684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14685
14686 MachineState_T oldMachineState = mData->mMachineState;
14687
14688 AssertMsgReturn(oldMachineState != aMachineState,
14689 ("oldMachineState=%s, aMachineState=%s\n",
14690 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14691 E_FAIL);
14692
14693 HRESULT rc = S_OK;
14694
14695 int stsFlags = 0;
14696 bool deleteSavedState = false;
14697
14698 /* detect some state transitions */
14699
14700 if ( ( ( oldMachineState == MachineState_Saved
14701 || oldMachineState == MachineState_AbortedSaved
14702 )
14703 && aMachineState == MachineState_Restoring
14704 )
14705 || ( ( oldMachineState == MachineState_PoweredOff
14706 || oldMachineState == MachineState_Teleported
14707 || oldMachineState == MachineState_Aborted
14708 )
14709 && ( aMachineState == MachineState_TeleportingIn
14710 || aMachineState == MachineState_Starting
14711 )
14712 )
14713 )
14714 {
14715 /* The EMT thread is about to start */
14716
14717 /* Nothing to do here for now... */
14718
14719 /// @todo NEWMEDIA don't let mDVDDrive and other children
14720 /// change anything when in the Starting/Restoring state
14721 }
14722 else if ( ( oldMachineState == MachineState_Running
14723 || oldMachineState == MachineState_Paused
14724 || oldMachineState == MachineState_Teleporting
14725 || oldMachineState == MachineState_OnlineSnapshotting
14726 || oldMachineState == MachineState_LiveSnapshotting
14727 || oldMachineState == MachineState_Stuck
14728 || oldMachineState == MachineState_Starting
14729 || oldMachineState == MachineState_Stopping
14730 || oldMachineState == MachineState_Saving
14731 || oldMachineState == MachineState_Restoring
14732 || oldMachineState == MachineState_TeleportingPausedVM
14733 || oldMachineState == MachineState_TeleportingIn
14734 )
14735 && ( aMachineState == MachineState_PoweredOff
14736 || aMachineState == MachineState_Saved
14737 || aMachineState == MachineState_Teleported
14738 || aMachineState == MachineState_Aborted
14739 || aMachineState == MachineState_AbortedSaved
14740 )
14741 )
14742 {
14743 /* The EMT thread has just stopped, unlock attached media. Note that as
14744 * opposed to locking that is done from Console, we do unlocking here
14745 * because the VM process may have aborted before having a chance to
14746 * properly unlock all media it locked. */
14747
14748 unlockMedia();
14749 }
14750
14751 if (oldMachineState == MachineState_Restoring)
14752 {
14753 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14754 {
14755 /*
14756 * delete the saved state file once the machine has finished
14757 * restoring from it (note that Console sets the state from
14758 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14759 * to give the user an ability to fix an error and retry --
14760 * we keep the saved state file in this case)
14761 */
14762 deleteSavedState = true;
14763 }
14764 }
14765 else if ( oldMachineState == MachineState_Saved
14766 && ( aMachineState == MachineState_PoweredOff
14767 || aMachineState == MachineState_Teleported
14768 )
14769 )
14770 {
14771 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14772 deleteSavedState = true;
14773 mData->mCurrentStateModified = TRUE;
14774 stsFlags |= SaveSTS_CurStateModified;
14775 }
14776 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14777 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14778
14779 if ( aMachineState == MachineState_Starting
14780 || aMachineState == MachineState_Restoring
14781 || aMachineState == MachineState_TeleportingIn
14782 )
14783 {
14784 /* set the current state modified flag to indicate that the current
14785 * state is no more identical to the state in the
14786 * current snapshot */
14787 if (!mData->mCurrentSnapshot.isNull())
14788 {
14789 mData->mCurrentStateModified = TRUE;
14790 stsFlags |= SaveSTS_CurStateModified;
14791 }
14792 }
14793
14794 if (deleteSavedState)
14795 {
14796 if (mRemoveSavedState)
14797 {
14798 Assert(!mSSData->strStateFilePath.isEmpty());
14799
14800 // it is safe to delete the saved state file if ...
14801 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14802 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14803 // ... none of the snapshots share the saved state file
14804 )
14805 RTFileDelete(mSSData->strStateFilePath.c_str());
14806 }
14807
14808 mSSData->strStateFilePath.setNull();
14809 stsFlags |= SaveSTS_StateFilePath;
14810 }
14811
14812 /* redirect to the underlying peer machine */
14813 mPeer->i_setMachineState(aMachineState);
14814
14815 if ( oldMachineState != MachineState_RestoringSnapshot
14816 && ( aMachineState == MachineState_PoweredOff
14817 || aMachineState == MachineState_Teleported
14818 || aMachineState == MachineState_Aborted
14819 || aMachineState == MachineState_AbortedSaved
14820 || aMachineState == MachineState_Saved))
14821 {
14822 /* the machine has stopped execution
14823 * (or the saved state file was adopted) */
14824 stsFlags |= SaveSTS_StateTimeStamp;
14825 }
14826
14827 if ( ( oldMachineState == MachineState_PoweredOff
14828 || oldMachineState == MachineState_Aborted
14829 || oldMachineState == MachineState_Teleported
14830 )
14831 && aMachineState == MachineState_Saved)
14832 {
14833 /* the saved state file was adopted */
14834 Assert(!mSSData->strStateFilePath.isEmpty());
14835 stsFlags |= SaveSTS_StateFilePath;
14836 }
14837
14838#ifdef VBOX_WITH_GUEST_PROPS
14839 if ( aMachineState == MachineState_PoweredOff
14840 || aMachineState == MachineState_Aborted
14841 || aMachineState == MachineState_Teleported)
14842 {
14843 /* Make sure any transient guest properties get removed from the
14844 * property store on shutdown. */
14845 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14846
14847 /* remove it from the settings representation */
14848 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14849 for (settings::GuestPropertiesList::iterator
14850 it = llGuestProperties.begin();
14851 it != llGuestProperties.end();
14852 /*nothing*/)
14853 {
14854 const settings::GuestProperty &prop = *it;
14855 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14856 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14857 {
14858 it = llGuestProperties.erase(it);
14859 fNeedsSaving = true;
14860 }
14861 else
14862 {
14863 ++it;
14864 }
14865 }
14866
14867 /* Additionally remove it from the HWData representation. Required to
14868 * keep everything in sync, as this is what the API keeps using. */
14869 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14870 for (HWData::GuestPropertyMap::iterator
14871 it = llHWGuestProperties.begin();
14872 it != llHWGuestProperties.end();
14873 /*nothing*/)
14874 {
14875 uint32_t fFlags = it->second.mFlags;
14876 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14877 {
14878 /* iterator where we need to continue after the erase call
14879 * (C++03 is a fact still, and it doesn't return the iterator
14880 * which would allow continuing) */
14881 HWData::GuestPropertyMap::iterator it2 = it;
14882 ++it2;
14883 llHWGuestProperties.erase(it);
14884 it = it2;
14885 fNeedsSaving = true;
14886 }
14887 else
14888 {
14889 ++it;
14890 }
14891 }
14892
14893 if (fNeedsSaving)
14894 {
14895 mData->mCurrentStateModified = TRUE;
14896 stsFlags |= SaveSTS_CurStateModified;
14897 }
14898 }
14899#endif /* VBOX_WITH_GUEST_PROPS */
14900
14901 rc = i_saveStateSettings(stsFlags);
14902
14903 if ( ( oldMachineState != MachineState_PoweredOff
14904 && oldMachineState != MachineState_Aborted
14905 && oldMachineState != MachineState_Teleported
14906 )
14907 && ( aMachineState == MachineState_PoweredOff
14908 || aMachineState == MachineState_Aborted
14909 || aMachineState == MachineState_Teleported
14910 )
14911 )
14912 {
14913 /* we've been shut down for any reason */
14914 /* no special action so far */
14915 }
14916
14917 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
14918 LogFlowThisFuncLeave();
14919 return rc;
14920}
14921
14922/**
14923 * Sends the current machine state value to the VM process.
14924 *
14925 * @note Locks this object for reading, then calls a client process.
14926 */
14927HRESULT SessionMachine::i_updateMachineStateOnClient()
14928{
14929 AutoCaller autoCaller(this);
14930 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14931
14932 ComPtr<IInternalSessionControl> directControl;
14933 {
14934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14935 AssertReturn(!!mData, E_FAIL);
14936 if (mData->mSession.mLockType == LockType_VM)
14937 directControl = mData->mSession.mDirectControl;
14938
14939 /* directControl may be already set to NULL here in #OnSessionEnd()
14940 * called too early by the direct session process while there is still
14941 * some operation (like deleting the snapshot) in progress. The client
14942 * process in this case is waiting inside Session::close() for the
14943 * "end session" process object to complete, while #uninit() called by
14944 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14945 * operation to complete. For now, we accept this inconsistent behavior
14946 * and simply do nothing here. */
14947
14948 if (mData->mSession.mState == SessionState_Unlocking)
14949 return S_OK;
14950 }
14951
14952 /* ignore notifications sent after #OnSessionEnd() is called */
14953 if (!directControl)
14954 return S_OK;
14955
14956 return directControl->UpdateMachineState(mData->mMachineState);
14957}
14958
14959
14960/*static*/
14961HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14962{
14963 va_list args;
14964 va_start(args, pcszMsg);
14965 HRESULT rc = setErrorInternalV(aResultCode,
14966 getStaticClassIID(),
14967 getStaticComponentName(),
14968 pcszMsg, args,
14969 false /* aWarning */,
14970 true /* aLogIt */);
14971 va_end(args);
14972 return rc;
14973}
14974
14975
14976HRESULT Machine::updateState(MachineState_T aState)
14977{
14978 NOREF(aState);
14979 ReturnComNotImplemented();
14980}
14981
14982HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14983{
14984 NOREF(aProgress);
14985 ReturnComNotImplemented();
14986}
14987
14988HRESULT Machine::endPowerUp(LONG aResult)
14989{
14990 NOREF(aResult);
14991 ReturnComNotImplemented();
14992}
14993
14994HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14995{
14996 NOREF(aProgress);
14997 ReturnComNotImplemented();
14998}
14999
15000HRESULT Machine::endPoweringDown(LONG aResult,
15001 const com::Utf8Str &aErrMsg)
15002{
15003 NOREF(aResult);
15004 NOREF(aErrMsg);
15005 ReturnComNotImplemented();
15006}
15007
15008HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15009 BOOL *aMatched,
15010 ULONG *aMaskedInterfaces)
15011{
15012 NOREF(aDevice);
15013 NOREF(aMatched);
15014 NOREF(aMaskedInterfaces);
15015 ReturnComNotImplemented();
15016
15017}
15018
15019HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15020{
15021 NOREF(aId); NOREF(aCaptureFilename);
15022 ReturnComNotImplemented();
15023}
15024
15025HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15026 BOOL aDone)
15027{
15028 NOREF(aId);
15029 NOREF(aDone);
15030 ReturnComNotImplemented();
15031}
15032
15033HRESULT Machine::autoCaptureUSBDevices()
15034{
15035 ReturnComNotImplemented();
15036}
15037
15038HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15039{
15040 NOREF(aDone);
15041 ReturnComNotImplemented();
15042}
15043
15044HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15045 ComPtr<IProgress> &aProgress)
15046{
15047 NOREF(aSession);
15048 NOREF(aProgress);
15049 ReturnComNotImplemented();
15050}
15051
15052HRESULT Machine::finishOnlineMergeMedium()
15053{
15054 ReturnComNotImplemented();
15055}
15056
15057HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15058 std::vector<com::Utf8Str> &aValues,
15059 std::vector<LONG64> &aTimestamps,
15060 std::vector<com::Utf8Str> &aFlags)
15061{
15062 NOREF(aNames);
15063 NOREF(aValues);
15064 NOREF(aTimestamps);
15065 NOREF(aFlags);
15066 ReturnComNotImplemented();
15067}
15068
15069HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15070 const com::Utf8Str &aValue,
15071 LONG64 aTimestamp,
15072 const com::Utf8Str &aFlags,
15073 BOOL fWasDeleted)
15074{
15075 NOREF(aName);
15076 NOREF(aValue);
15077 NOREF(aTimestamp);
15078 NOREF(aFlags);
15079 NOREF(fWasDeleted);
15080 ReturnComNotImplemented();
15081}
15082
15083HRESULT Machine::lockMedia()
15084{
15085 ReturnComNotImplemented();
15086}
15087
15088HRESULT Machine::unlockMedia()
15089{
15090 ReturnComNotImplemented();
15091}
15092
15093HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15094 ComPtr<IMediumAttachment> &aNewAttachment)
15095{
15096 NOREF(aAttachment);
15097 NOREF(aNewAttachment);
15098 ReturnComNotImplemented();
15099}
15100
15101HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15102 ULONG aCpuUser,
15103 ULONG aCpuKernel,
15104 ULONG aCpuIdle,
15105 ULONG aMemTotal,
15106 ULONG aMemFree,
15107 ULONG aMemBalloon,
15108 ULONG aMemShared,
15109 ULONG aMemCache,
15110 ULONG aPagedTotal,
15111 ULONG aMemAllocTotal,
15112 ULONG aMemFreeTotal,
15113 ULONG aMemBalloonTotal,
15114 ULONG aMemSharedTotal,
15115 ULONG aVmNetRx,
15116 ULONG aVmNetTx)
15117{
15118 NOREF(aValidStats);
15119 NOREF(aCpuUser);
15120 NOREF(aCpuKernel);
15121 NOREF(aCpuIdle);
15122 NOREF(aMemTotal);
15123 NOREF(aMemFree);
15124 NOREF(aMemBalloon);
15125 NOREF(aMemShared);
15126 NOREF(aMemCache);
15127 NOREF(aPagedTotal);
15128 NOREF(aMemAllocTotal);
15129 NOREF(aMemFreeTotal);
15130 NOREF(aMemBalloonTotal);
15131 NOREF(aMemSharedTotal);
15132 NOREF(aVmNetRx);
15133 NOREF(aVmNetTx);
15134 ReturnComNotImplemented();
15135}
15136
15137HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15138 com::Utf8Str &aResult)
15139{
15140 NOREF(aAuthParams);
15141 NOREF(aResult);
15142 ReturnComNotImplemented();
15143}
15144
15145com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15146{
15147 com::Utf8Str strControllerName = "Unknown";
15148 switch (aBusType)
15149 {
15150 case StorageBus_IDE:
15151 {
15152 strControllerName = "IDE";
15153 break;
15154 }
15155 case StorageBus_SATA:
15156 {
15157 strControllerName = "SATA";
15158 break;
15159 }
15160 case StorageBus_SCSI:
15161 {
15162 strControllerName = "SCSI";
15163 break;
15164 }
15165 case StorageBus_Floppy:
15166 {
15167 strControllerName = "Floppy";
15168 break;
15169 }
15170 case StorageBus_SAS:
15171 {
15172 strControllerName = "SAS";
15173 break;
15174 }
15175 case StorageBus_USB:
15176 {
15177 strControllerName = "USB";
15178 break;
15179 }
15180 default:
15181 break;
15182 }
15183 return strControllerName;
15184}
15185
15186HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15187{
15188 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15189
15190 AutoCaller autoCaller(this);
15191 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15192
15193 HRESULT rc = S_OK;
15194
15195 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15196 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15197 rc = getUSBDeviceFilters(usbDeviceFilters);
15198 if (FAILED(rc)) return rc;
15199
15200 NOREF(aFlags);
15201 com::Utf8Str osTypeId;
15202 ComObjPtr<GuestOSType> osType = NULL;
15203
15204 /* Get the guest os type as a string from the VB. */
15205 rc = getOSTypeId(osTypeId);
15206 if (FAILED(rc)) return rc;
15207
15208 /* Get the os type obj that coresponds, can be used to get
15209 * the defaults for this guest OS. */
15210 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15211 if (FAILED(rc)) return rc;
15212
15213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15214
15215 /* Let the OS type select 64-bit ness. */
15216 mHWData->mLongMode = osType->i_is64Bit()
15217 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15218
15219 /* Let the OS type enable the X2APIC */
15220 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15221
15222 /* This one covers IOAPICEnabled. */
15223 mBIOSSettings->i_applyDefaults(osType);
15224
15225 /* Initialize default record settings. */
15226 mRecordingSettings->i_applyDefaults();
15227
15228 /* Initialize default BIOS settings here */
15229 /* Hardware virtualization must be ON by default */
15230 mHWData->mAPIC = true;
15231 mHWData->mHWVirtExEnabled = true;
15232
15233 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15237 if (FAILED(rc)) return rc;
15238
15239 /* Graphics stuff. */
15240 GraphicsControllerType_T graphicsController;
15241 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15242 if (FAILED(rc)) return rc;
15243
15244 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15245 if (FAILED(rc)) return rc;
15246
15247 ULONG vramSize;
15248 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15249 if (FAILED(rc)) return rc;
15250
15251 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15252 if (FAILED(rc)) return rc;
15253
15254 BOOL fAccelerate2DVideoEnabled;
15255 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15256 if (FAILED(rc)) return rc;
15257
15258 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15259 if (FAILED(rc)) return rc;
15260
15261 BOOL fAccelerate3DEnabled;
15262 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15263 if (FAILED(rc)) return rc;
15264
15265 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15266 if (FAILED(rc)) return rc;
15267
15268 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15269 if (FAILED(rc)) return rc;
15270
15271 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15275 if (FAILED(rc)) return rc;
15276
15277 BOOL mRTCUseUTC;
15278 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15279 if (FAILED(rc)) return rc;
15280
15281 setRTCUseUTC(mRTCUseUTC);
15282 if (FAILED(rc)) return rc;
15283
15284 /* the setter does more than just the assignment, so use it */
15285 ChipsetType_T enmChipsetType;
15286 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15287 if (FAILED(rc)) return rc;
15288
15289 rc = COMSETTER(ChipsetType)(enmChipsetType);
15290 if (FAILED(rc)) return rc;
15291
15292 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15293 if (FAILED(rc)) return rc;
15294
15295 /* Apply IOMMU defaults. */
15296 IommuType_T enmIommuType;
15297 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15298 if (FAILED(rc)) return rc;
15299
15300 rc = COMSETTER(IommuType)(enmIommuType);
15301 if (FAILED(rc)) return rc;
15302
15303 /* Apply network adapters defaults */
15304 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15305 mNetworkAdapters[slot]->i_applyDefaults(osType);
15306
15307 /* Apply serial port defaults */
15308 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15309 mSerialPorts[slot]->i_applyDefaults(osType);
15310
15311 /* Apply parallel port defaults - not OS dependent*/
15312 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15313 mParallelPorts[slot]->i_applyDefaults();
15314
15315 /* This one covers the TPM type. */
15316 mTrustedPlatformModule->i_applyDefaults(osType);
15317
15318 /* This one covers secure boot. */
15319 rc = mNvramStore->i_applyDefaults(osType);
15320 if (FAILED(rc)) return rc;
15321
15322 /* Audio stuff. */
15323 AudioControllerType_T audioController;
15324 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15325 if (FAILED(rc)) return rc;
15326
15327 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15328 if (FAILED(rc)) return rc;
15329
15330 AudioCodecType_T audioCodec;
15331 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15332 if (FAILED(rc)) return rc;
15333
15334 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15335 if (FAILED(rc)) return rc;
15336
15337 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15338 if (FAILED(rc)) return rc;
15339
15340 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15341 if (FAILED(rc)) return rc;
15342
15343 /* Storage Controllers */
15344 StorageControllerType_T hdStorageControllerType;
15345 StorageBus_T hdStorageBusType;
15346 StorageControllerType_T dvdStorageControllerType;
15347 StorageBus_T dvdStorageBusType;
15348 BOOL recommendedFloppy;
15349 ComPtr<IStorageController> floppyController;
15350 ComPtr<IStorageController> hdController;
15351 ComPtr<IStorageController> dvdController;
15352 Utf8Str strFloppyName, strDVDName, strHDName;
15353
15354 /* GUI auto generates controller names using bus type. Do the same*/
15355 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15356
15357 /* Floppy recommended? add one. */
15358 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15359 if (FAILED(rc)) return rc;
15360 if (recommendedFloppy)
15361 {
15362 rc = addStorageController(strFloppyName,
15363 StorageBus_Floppy,
15364 floppyController);
15365 if (FAILED(rc)) return rc;
15366 }
15367
15368 /* Setup one DVD storage controller. */
15369 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15370 if (FAILED(rc)) return rc;
15371
15372 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15373 if (FAILED(rc)) return rc;
15374
15375 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15376
15377 rc = addStorageController(strDVDName,
15378 dvdStorageBusType,
15379 dvdController);
15380 if (FAILED(rc)) return rc;
15381
15382 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15383 if (FAILED(rc)) return rc;
15384
15385 /* Setup one HDD storage controller. */
15386 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15387 if (FAILED(rc)) return rc;
15388
15389 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15390 if (FAILED(rc)) return rc;
15391
15392 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15393
15394 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15395 {
15396 rc = addStorageController(strHDName,
15397 hdStorageBusType,
15398 hdController);
15399 if (FAILED(rc)) return rc;
15400
15401 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15402 if (FAILED(rc)) return rc;
15403 }
15404 else
15405 {
15406 /* The HD controller is the same as DVD: */
15407 hdController = dvdController;
15408 }
15409
15410 /* Limit the AHCI port count if it's used because windows has trouble with
15411 * too many ports and other guest (OS X in particular) may take extra long
15412 * boot: */
15413
15414 // pParent = static_cast<Medium*>(aP)
15415 IStorageController *temp = hdController;
15416 ComObjPtr<StorageController> storageController;
15417 storageController = static_cast<StorageController *>(temp);
15418
15419 // tempHDController = aHDController;
15420 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15421 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15422 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15423 storageController->COMSETTER(PortCount)(1);
15424
15425 /* USB stuff */
15426
15427 bool ohciEnabled = false;
15428
15429 ComPtr<IUSBController> usbController;
15430 BOOL recommendedUSB3;
15431 BOOL recommendedUSB;
15432 BOOL usbProxyAvailable;
15433
15434 getUSBProxyAvailable(&usbProxyAvailable);
15435 if (FAILED(rc)) return rc;
15436
15437 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15438 if (FAILED(rc)) return rc;
15439 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15440 if (FAILED(rc)) return rc;
15441
15442 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15443 {
15444#ifdef VBOX_WITH_EXTPACK
15445 /* USB 3.0 is only available if the proper ExtPack is installed. */
15446 ExtPackManager *aManager = mParent->i_getExtPackManager();
15447 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15448 {
15449 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15450 if (FAILED(rc)) return rc;
15451
15452 /* xHci includes OHCI */
15453 ohciEnabled = true;
15454 }
15455#endif
15456 }
15457 if ( !ohciEnabled
15458 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15459 {
15460 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15461 if (FAILED(rc)) return rc;
15462 ohciEnabled = true;
15463
15464#ifdef VBOX_WITH_EXTPACK
15465 /* USB 2.0 is only available if the proper ExtPack is installed.
15466 * Note. Configuring EHCI here and providing messages about
15467 * the missing extpack isn't exactly clean, but it is a
15468 * necessary evil to patch over legacy compatability issues
15469 * introduced by the new distribution model. */
15470 ExtPackManager *manager = mParent->i_getExtPackManager();
15471 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15472 {
15473 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15474 if (FAILED(rc)) return rc;
15475 }
15476#endif
15477 }
15478
15479 /* Set recommended human interface device types: */
15480 BOOL recommendedUSBHID;
15481 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15482 if (FAILED(rc)) return rc;
15483
15484 if (recommendedUSBHID)
15485 {
15486 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15487 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15488 if (!ohciEnabled && !usbDeviceFilters.isNull())
15489 {
15490 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15491 if (FAILED(rc)) return rc;
15492 }
15493 }
15494
15495 BOOL recommendedUSBTablet;
15496 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15497 if (FAILED(rc)) return rc;
15498
15499 if (recommendedUSBTablet)
15500 {
15501 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15502 if (!ohciEnabled && !usbDeviceFilters.isNull())
15503 {
15504 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15505 if (FAILED(rc)) return rc;
15506 }
15507 }
15508
15509 /* Enable the VMMDev testing feature for bootsector VMs: */
15510 if (osTypeId == "VBoxBS_64")
15511 {
15512 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15513 if (FAILED(rc))
15514 return rc;
15515 }
15516
15517 return S_OK;
15518}
15519
15520/* This isn't handled entirely by the wrapper generator yet. */
15521#ifdef VBOX_WITH_XPCOM
15522NS_DECL_CLASSINFO(SessionMachine)
15523NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15524
15525NS_DECL_CLASSINFO(SnapshotMachine)
15526NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15527#endif
Note: See TracBrowser for help on using the repository browser.

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