VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 541.5 KB
Line 
1/* $Id: MachineImpl.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
176 mHWVirtExVPIDEnabled = true;
177 mHWVirtExUXEnabled = true;
178 mHWVirtExForceEnabled = false;
179 mHWVirtExUseNativeApi = false;
180 mHWVirtExVirtVmsaveVmload = true;
181#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
182 mPAEEnabled = true;
183#else
184 mPAEEnabled = false;
185#endif
186 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
187 mTripleFaultReset = false;
188 mAPIC = true;
189 mX2APIC = false;
190 mIBPBOnVMExit = false;
191 mIBPBOnVMEntry = false;
192 mSpecCtrl = false;
193 mSpecCtrlByHost = false;
194 mL1DFlushOnSched = true;
195 mL1DFlushOnVMEntry = false;
196 mMDSClearOnSched = true;
197 mMDSClearOnVMEntry = false;
198 mNestedHWVirt = false;
199 mHPETEnabled = false;
200 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
201 mCpuIdPortabilityLevel = 0;
202 mCpuProfile = "host";
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mClipboardFileTransfersEnabled = FALSE;
213
214 mDnDMode = DnDMode_Disabled;
215
216 mFirmwareType = FirmwareType_BIOS;
217 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
218 mPointingHIDType = PointingHIDType_PS2Mouse;
219 mChipsetType = ChipsetType_PIIX3;
220 mIommuType = IommuType_None;
221 mParavirtProvider = ParavirtProvider_Default;
222 mEmulatedUSBCardReaderEnabled = FALSE;
223
224 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
225 mCPUAttached[i] = false;
226
227 mIOCacheEnabled = true;
228 mIOCacheSize = 5; /* 5MB */
229}
230
231Machine::HWData::~HWData()
232{
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// Machine class
237/////////////////////////////////////////////////////////////////////////////
238
239// constructor / destructor
240/////////////////////////////////////////////////////////////////////////////
241
242Machine::Machine() :
243#ifdef VBOX_WITH_RESOURCE_USAGE_API
244 mCollectorGuest(NULL),
245#endif
246 mPeer(NULL),
247 mParent(NULL),
248 mSerialPorts(),
249 mParallelPorts(),
250 uRegistryNeedsSaving(0)
251{}
252
253Machine::~Machine()
254{}
255
256HRESULT Machine::FinalConstruct()
257{
258 LogFlowThisFunc(("\n"));
259 return BaseFinalConstruct();
260}
261
262void Machine::FinalRelease()
263{
264 LogFlowThisFunc(("\n"));
265 uninit();
266 BaseFinalRelease();
267}
268
269/**
270 * Initializes a new machine instance; this init() variant creates a new, empty machine.
271 * This gets called from VirtualBox::CreateMachine().
272 *
273 * @param aParent Associated parent object
274 * @param strConfigFile Local file system path to the VM settings file (can
275 * be relative to the VirtualBox config directory).
276 * @param strName name for the machine
277 * @param llGroups list of groups for the machine
278 * @param strOsType OS Type string (stored as is if aOsType is NULL).
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
283 * scheme (includes the UUID).
284 *
285 * @return Success indicator. if not S_OK, the machine object is invalid
286 */
287HRESULT Machine::init(VirtualBox *aParent,
288 const Utf8Str &strConfigFile,
289 const Utf8Str &strName,
290 const StringsList &llGroups,
291 const Utf8Str &strOsType,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 if (llGroups.size())
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = i_isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->i_id();
344
345 /* Let the OS type select 64-bit ness. */
346 mHWData->mLongMode = aOsType->i_is64Bit()
347 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
348
349 /* Let the OS type enable the X2APIC */
350 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
351
352 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
353 AssertComRC(rc);
354 }
355 else if (!strOsType.isEmpty())
356 {
357 /* Store OS type */
358 mUserData->s.strOsType = strOsType;
359
360 /* No guest OS type object. Pick some plausible defaults which the
361 * host can handle. There's no way to know or validate anything. */
362 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 mHWData->mX2APIC = false;
364 }
365
366 /* Apply BIOS defaults. */
367 mBIOSSettings->i_applyDefaults(aOsType);
368
369 /* Apply TPM defaults. */
370 mTrustedPlatformModule->i_applyDefaults(aOsType);
371
372 /* Apply record defaults. */
373 mRecordingSettings->i_applyDefaults();
374
375 /* Apply network adapters defaults */
376 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
377 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
378
379 /* Apply serial port defaults */
380 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
381 mSerialPorts[slot]->i_applyDefaults(aOsType);
382
383 /* Apply parallel port defaults */
384 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
385 mParallelPorts[slot]->i_applyDefaults();
386
387 /* Enable the VMMDev testing feature for bootsector VMs: */
388 if (aOsType && aOsType->i_id() == "VBoxBS_64")
389 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
390
391 /* At this point the changing of the current state modification
392 * flag is allowed. */
393 i_allowStateModification();
394
395 /* commit all changes made during the initialization */
396 i_commit();
397 }
398
399 /* Confirm a successful initialization when it's the case */
400 if (SUCCEEDED(rc))
401 {
402 if (mData->mAccessible)
403 autoInitSpan.setSucceeded();
404 else
405 autoInitSpan.setLimited();
406 }
407
408 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
409 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
410 mData->mRegistered,
411 mData->mAccessible,
412 rc));
413
414 LogFlowThisFuncLeave();
415
416 return rc;
417}
418
419/**
420 * Initializes a new instance with data from machine XML (formerly Init_Registered).
421 * Gets called in two modes:
422 *
423 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
424 * UUID is specified and we mark the machine as "registered";
425 *
426 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
427 * and the machine remains unregistered until RegisterMachine() is called.
428 *
429 * @param aParent Associated parent object
430 * @param strConfigFile Local file system path to the VM settings file (can
431 * be relative to the VirtualBox config directory).
432 * @param aId UUID of the machine or NULL (see above).
433 *
434 * @return Success indicator. if not S_OK, the machine object is invalid
435 */
436HRESULT Machine::initFromSettings(VirtualBox *aParent,
437 const Utf8Str &strConfigFile,
438 const Guid *aId)
439{
440 LogFlowThisFuncEnter();
441 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
442
443 /* Enclose the state transition NotReady->InInit->Ready */
444 AutoInitSpan autoInitSpan(this);
445 AssertReturn(autoInitSpan.isOk(), E_FAIL);
446
447 HRESULT rc = initImpl(aParent, strConfigFile);
448 if (FAILED(rc)) return rc;
449
450 if (aId)
451 {
452 // loading a registered VM:
453 unconst(mData->mUuid) = *aId;
454 mData->mRegistered = TRUE;
455 // now load the settings from XML:
456 rc = i_registeredInit();
457 // this calls initDataAndChildObjects() and loadSettings()
458 }
459 else
460 {
461 // opening an unregistered VM (VirtualBox::OpenMachine()):
462 rc = initDataAndChildObjects();
463
464 if (SUCCEEDED(rc))
465 {
466 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
467 mData->mAccessible = TRUE;
468
469 try
470 {
471 // load and parse machine XML; this will throw on XML or logic errors
472 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
473
474 // reject VM UUID duplicates, they can happen if someone
475 // tries to register an already known VM config again
476 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
477 true /* fPermitInaccessible */,
478 false /* aDoSetError */,
479 NULL) != VBOX_E_OBJECT_NOT_FOUND)
480 {
481 throw setError(E_FAIL,
482 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
483 mData->m_strConfigFile.c_str());
484 }
485
486 // use UUID from machine config
487 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
488
489 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
490 NULL /* puuidRegistry */);
491 if (FAILED(rc)) throw rc;
492
493 /* At this point the changing of the current state modification
494 * flag is allowed. */
495 i_allowStateModification();
496
497 i_commit();
498 }
499 catch (HRESULT err)
500 {
501 /* we assume that error info is set by the thrower */
502 rc = err;
503 }
504 catch (...)
505 {
506 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
507 }
508 }
509 }
510
511 /* Confirm a successful initialization when it's the case */
512 if (SUCCEEDED(rc))
513 {
514 if (mData->mAccessible)
515 autoInitSpan.setSucceeded();
516 else
517 {
518 autoInitSpan.setLimited();
519
520 // uninit media from this machine's media registry, or else
521 // reloading the settings will fail
522 mParent->i_unregisterMachineMedia(i_getId());
523 }
524 }
525
526 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
527 "rc=%08X\n",
528 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
529 mData->mRegistered, mData->mAccessible, rc));
530
531 LogFlowThisFuncLeave();
532
533 return rc;
534}
535
536/**
537 * Initializes a new instance from a machine config that is already in memory
538 * (import OVF case). Since we are importing, the UUID in the machine
539 * config is ignored and we always generate a fresh one.
540 *
541 * @param aParent Associated parent object.
542 * @param strName Name for the new machine; this overrides what is specified in config.
543 * @param strSettingsFilename File name of .vbox file.
544 * @param config Machine configuration loaded and parsed from XML.
545 *
546 * @return Success indicator. if not S_OK, the machine object is invalid
547 */
548HRESULT Machine::init(VirtualBox *aParent,
549 const Utf8Str &strName,
550 const Utf8Str &strSettingsFilename,
551 const settings::MachineConfigFile &config)
552{
553 LogFlowThisFuncEnter();
554
555 /* Enclose the state transition NotReady->InInit->Ready */
556 AutoInitSpan autoInitSpan(this);
557 AssertReturn(autoInitSpan.isOk(), E_FAIL);
558
559 HRESULT rc = initImpl(aParent, strSettingsFilename);
560 if (FAILED(rc)) return rc;
561
562 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
563 if (FAILED(rc)) return rc;
564
565 rc = initDataAndChildObjects();
566
567 if (SUCCEEDED(rc))
568 {
569 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
570 mData->mAccessible = TRUE;
571
572 // create empty machine config for instance data
573 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
574
575 // generate fresh UUID, ignore machine config
576 unconst(mData->mUuid).create();
577
578 rc = i_loadMachineDataFromSettings(config,
579 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
580
581 // override VM name as well, it may be different
582 mUserData->s.strName = strName;
583
584 if (SUCCEEDED(rc))
585 {
586 /* At this point the changing of the current state modification
587 * flag is allowed. */
588 i_allowStateModification();
589
590 /* commit all changes made during the initialization */
591 i_commit();
592 }
593 }
594
595 /* Confirm a successful initialization when it's the case */
596 if (SUCCEEDED(rc))
597 {
598 if (mData->mAccessible)
599 autoInitSpan.setSucceeded();
600 else
601 {
602 /* Ignore all errors from unregistering, they would destroy
603- * the more interesting error information we already have,
604- * pinpointing the issue with the VM config. */
605 ErrorInfoKeeper eik;
606
607 autoInitSpan.setLimited();
608
609 // uninit media from this machine's media registry, or else
610 // reloading the settings will fail
611 mParent->i_unregisterMachineMedia(i_getId());
612 }
613 }
614
615 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
616 "rc=%08X\n",
617 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
618 mData->mRegistered, mData->mAccessible, rc));
619
620 LogFlowThisFuncLeave();
621
622 return rc;
623}
624
625/**
626 * Shared code between the various init() implementations.
627 * @param aParent The VirtualBox object.
628 * @param strConfigFile Settings file.
629 * @return
630 */
631HRESULT Machine::initImpl(VirtualBox *aParent,
632 const Utf8Str &strConfigFile)
633{
634 LogFlowThisFuncEnter();
635
636 AssertReturn(aParent, E_INVALIDARG);
637 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
638
639 HRESULT rc = S_OK;
640
641 /* share the parent weakly */
642 unconst(mParent) = aParent;
643
644 /* allocate the essential machine data structure (the rest will be
645 * allocated later by initDataAndChildObjects() */
646 mData.allocate();
647
648 /* memorize the config file name (as provided) */
649 mData->m_strConfigFile = strConfigFile;
650
651 /* get the full file name */
652 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
653 if (RT_FAILURE(vrc1))
654 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
655 tr("Invalid machine settings file name '%s' (%Rrc)"),
656 strConfigFile.c_str(),
657 vrc1);
658
659 LogFlowThisFuncLeave();
660
661 return rc;
662}
663
664/**
665 * Tries to create a machine settings file in the path stored in the machine
666 * instance data. Used when a new machine is created to fail gracefully if
667 * the settings file could not be written (e.g. because machine dir is read-only).
668 * @return
669 */
670HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
671{
672 HRESULT rc = S_OK;
673
674 // when we create a new machine, we must be able to create the settings file
675 RTFILE f = NIL_RTFILE;
676 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
677 if ( RT_SUCCESS(vrc)
678 || vrc == VERR_SHARING_VIOLATION
679 )
680 {
681 if (RT_SUCCESS(vrc))
682 RTFileClose(f);
683 if (!fForceOverwrite)
684 rc = setError(VBOX_E_FILE_ERROR,
685 tr("Machine settings file '%s' already exists"),
686 mData->m_strConfigFileFull.c_str());
687 else
688 {
689 /* try to delete the config file, as otherwise the creation
690 * of a new settings file will fail. */
691 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
692 if (RT_FAILURE(vrc2))
693 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
694 tr("Could not delete the existing settings file '%s' (%Rrc)"),
695 mData->m_strConfigFileFull.c_str(), vrc2);
696 }
697 }
698 else if ( vrc != VERR_FILE_NOT_FOUND
699 && vrc != VERR_PATH_NOT_FOUND
700 )
701 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
702 tr("Invalid machine settings file name '%s' (%Rrc)"),
703 mData->m_strConfigFileFull.c_str(),
704 vrc);
705 return rc;
706}
707
708/**
709 * Initializes the registered machine by loading the settings file.
710 * This method is separated from #init() in order to make it possible to
711 * retry the operation after VirtualBox startup instead of refusing to
712 * startup the whole VirtualBox server in case if the settings file of some
713 * registered VM is invalid or inaccessible.
714 *
715 * @note Must be always called from this object's write lock
716 * (unless called from #init() that doesn't need any locking).
717 * @note Locks the mUSBController method for writing.
718 * @note Subclasses must not call this method.
719 */
720HRESULT Machine::i_registeredInit()
721{
722 AssertReturn(!i_isSessionMachine(), E_FAIL);
723 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
724 AssertReturn(mData->mUuid.isValid(), E_FAIL);
725 AssertReturn(!mData->mAccessible, E_FAIL);
726
727 HRESULT rc = initDataAndChildObjects();
728
729 if (SUCCEEDED(rc))
730 {
731 /* Temporarily reset the registered flag in order to let setters
732 * potentially called from loadSettings() succeed (isMutable() used in
733 * all setters will return FALSE for a Machine instance if mRegistered
734 * is TRUE). */
735 mData->mRegistered = FALSE;
736
737 try
738 {
739 // load and parse machine XML; this will throw on XML or logic errors
740 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
741
742 if (mData->mUuid != mData->pMachineConfigFile->uuid)
743 throw setError(E_FAIL,
744 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
745 mData->pMachineConfigFile->uuid.raw(),
746 mData->m_strConfigFileFull.c_str(),
747 mData->mUuid.toString().c_str(),
748 mParent->i_settingsFilePath().c_str());
749
750 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
751 NULL /* const Guid *puuidRegistry */);
752 if (FAILED(rc)) throw rc;
753 }
754 catch (HRESULT err)
755 {
756 /* we assume that error info is set by the thrower */
757 rc = err;
758 }
759 catch (...)
760 {
761 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
762 }
763
764 /* Restore the registered flag (even on failure) */
765 mData->mRegistered = TRUE;
766 }
767
768 if (SUCCEEDED(rc))
769 {
770 /* Set mAccessible to TRUE only if we successfully locked and loaded
771 * the settings file */
772 mData->mAccessible = TRUE;
773
774 /* commit all changes made during loading the settings file */
775 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
776 /// @todo r=klaus for some reason the settings loading logic backs up
777 // the settings, and therefore a commit is needed. Should probably be changed.
778 }
779 else
780 {
781 /* If the machine is registered, then, instead of returning a
782 * failure, we mark it as inaccessible and set the result to
783 * success to give it a try later */
784
785 /* fetch the current error info */
786 mData->mAccessError = com::ErrorInfo();
787 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
788
789 /* rollback all changes */
790 i_rollback(false /* aNotify */);
791
792 // uninit media from this machine's media registry, or else
793 // reloading the settings will fail
794 mParent->i_unregisterMachineMedia(i_getId());
795
796 /* uninitialize the common part to make sure all data is reset to
797 * default (null) values */
798 uninitDataAndChildObjects();
799
800 rc = S_OK;
801 }
802
803 return rc;
804}
805
806/**
807 * Uninitializes the instance.
808 * Called either from FinalRelease() or by the parent when it gets destroyed.
809 *
810 * @note The caller of this method must make sure that this object
811 * a) doesn't have active callers on the current thread and b) is not locked
812 * by the current thread; otherwise uninit() will hang either a) due to
813 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
814 * a dead-lock caused by this thread waiting for all callers on the other
815 * threads are done but preventing them from doing so by holding a lock.
816 */
817void Machine::uninit()
818{
819 LogFlowThisFuncEnter();
820
821 Assert(!isWriteLockOnCurrentThread());
822
823 Assert(!uRegistryNeedsSaving);
824 if (uRegistryNeedsSaving)
825 {
826 AutoCaller autoCaller(this);
827 if (SUCCEEDED(autoCaller.rc()))
828 {
829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
830 i_saveSettings(NULL, alock, Machine::SaveS_Force);
831 }
832 }
833
834 /* Enclose the state transition Ready->InUninit->NotReady */
835 AutoUninitSpan autoUninitSpan(this);
836 if (autoUninitSpan.uninitDone())
837 return;
838
839 Assert(!i_isSnapshotMachine());
840 Assert(!i_isSessionMachine());
841 Assert(!!mData);
842
843 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
844 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
845
846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
847
848 if (!mData->mSession.mMachine.isNull())
849 {
850 /* Theoretically, this can only happen if the VirtualBox server has been
851 * terminated while there were clients running that owned open direct
852 * sessions. Since in this case we are definitely called by
853 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
854 * won't happen on the client watcher thread (because it has a
855 * VirtualBox caller for the duration of the
856 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
857 * cannot happen until the VirtualBox caller is released). This is
858 * important, because SessionMachine::uninit() cannot correctly operate
859 * after we return from this method (it expects the Machine instance is
860 * still valid). We'll call it ourselves below.
861 */
862 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
863 (SessionMachine*)mData->mSession.mMachine));
864
865 if (Global::IsOnlineOrTransient(mData->mMachineState))
866 {
867 Log1WarningThisFunc(("Setting state to Aborted!\n"));
868 /* set machine state using SessionMachine reimplementation */
869 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
870 }
871
872 /*
873 * Uninitialize SessionMachine using public uninit() to indicate
874 * an unexpected uninitialization.
875 */
876 mData->mSession.mMachine->uninit();
877 /* SessionMachine::uninit() must set mSession.mMachine to null */
878 Assert(mData->mSession.mMachine.isNull());
879 }
880
881 // uninit media from this machine's media registry, if they're still there
882 Guid uuidMachine(i_getId());
883
884 /* the lock is no more necessary (SessionMachine is uninitialized) */
885 alock.release();
886
887 /* XXX This will fail with
888 * "cannot be closed because it is still attached to 1 virtual machines"
889 * because at this point we did not call uninitDataAndChildObjects() yet
890 * and therefore also removeBackReference() for all these mediums was not called! */
891
892 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
893 mParent->i_unregisterMachineMedia(uuidMachine);
894
895 // has machine been modified?
896 if (mData->flModifications)
897 {
898 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
899 i_rollback(false /* aNotify */);
900 }
901
902 if (mData->mAccessible)
903 uninitDataAndChildObjects();
904
905 /* free the essential data structure last */
906 mData.free();
907
908 LogFlowThisFuncLeave();
909}
910
911// Wrapped IMachine properties
912/////////////////////////////////////////////////////////////////////////////
913HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
914{
915 /* mParent is constant during life time, no need to lock */
916 ComObjPtr<VirtualBox> pVirtualBox(mParent);
917 aParent = pVirtualBox;
918
919 return S_OK;
920}
921
922
923HRESULT Machine::getAccessible(BOOL *aAccessible)
924{
925 /* In some cases (medium registry related), it is necessary to be able to
926 * go through the list of all machines. Happens when an inaccessible VM
927 * has a sensible medium registry. */
928 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
930
931 HRESULT rc = S_OK;
932
933 if (!mData->mAccessible)
934 {
935 /* try to initialize the VM once more if not accessible */
936
937 AutoReinitSpan autoReinitSpan(this);
938 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
939
940#ifdef DEBUG
941 LogFlowThisFunc(("Dumping media backreferences\n"));
942 mParent->i_dumpAllBackRefs();
943#endif
944
945 if (mData->pMachineConfigFile)
946 {
947 // reset the XML file to force loadSettings() (called from i_registeredInit())
948 // to parse it again; the file might have changed
949 delete mData->pMachineConfigFile;
950 mData->pMachineConfigFile = NULL;
951 }
952
953 rc = i_registeredInit();
954
955 if (SUCCEEDED(rc) && mData->mAccessible)
956 {
957 autoReinitSpan.setSucceeded();
958
959 /* make sure interesting parties will notice the accessibility
960 * state change */
961 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
962 mParent->i_onMachineDataChanged(mData->mUuid);
963 }
964 }
965
966 if (SUCCEEDED(rc))
967 *aAccessible = mData->mAccessible;
968
969 LogFlowThisFuncLeave();
970
971 return rc;
972}
973
974HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
975{
976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
977
978 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
979 {
980 /* return shortly */
981 aAccessError = NULL;
982 return S_OK;
983 }
984
985 HRESULT rc = S_OK;
986
987 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
988 rc = errorInfo.createObject();
989 if (SUCCEEDED(rc))
990 {
991 errorInfo->init(mData->mAccessError.getResultCode(),
992 mData->mAccessError.getInterfaceID().ref(),
993 Utf8Str(mData->mAccessError.getComponent()).c_str(),
994 Utf8Str(mData->mAccessError.getText()));
995 aAccessError = errorInfo;
996 }
997
998 return rc;
999}
1000
1001HRESULT Machine::getName(com::Utf8Str &aName)
1002{
1003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1004
1005 aName = mUserData->s.strName;
1006
1007 return S_OK;
1008}
1009
1010HRESULT Machine::setName(const com::Utf8Str &aName)
1011{
1012 // prohibit setting a UUID only as the machine name, or else it can
1013 // never be found by findMachine()
1014 Guid test(aName);
1015
1016 if (test.isValid())
1017 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1018
1019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1020
1021 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1022 if (FAILED(rc)) return rc;
1023
1024 i_setModified(IsModified_MachineData);
1025 mUserData.backup();
1026 mUserData->s.strName = aName;
1027
1028 return S_OK;
1029}
1030
1031HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1032{
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 aDescription = mUserData->s.strDescription;
1036
1037 return S_OK;
1038}
1039
1040HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1041{
1042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1043
1044 // this can be done in principle in any state as it doesn't affect the VM
1045 // significantly, but play safe by not messing around while complex
1046 // activities are going on
1047 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1048 if (FAILED(rc)) return rc;
1049
1050 i_setModified(IsModified_MachineData);
1051 mUserData.backup();
1052 mUserData->s.strDescription = aDescription;
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::getId(com::Guid &aId)
1058{
1059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1060
1061 aId = mData->mUuid;
1062
1063 return S_OK;
1064}
1065
1066HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1067{
1068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1069 aGroups.resize(mUserData->s.llGroups.size());
1070 size_t i = 0;
1071 for (StringsList::const_iterator
1072 it = mUserData->s.llGroups.begin();
1073 it != mUserData->s.llGroups.end();
1074 ++it, ++i)
1075 aGroups[i] = (*it);
1076
1077 return S_OK;
1078}
1079
1080HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1081{
1082 StringsList llGroups;
1083 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1084 if (FAILED(rc))
1085 return rc;
1086
1087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1088
1089 rc = i_checkStateDependency(MutableOrSavedStateDep);
1090 if (FAILED(rc)) return rc;
1091
1092 i_setModified(IsModified_MachineData);
1093 mUserData.backup();
1094 mUserData->s.llGroups = llGroups;
1095
1096 return S_OK;
1097}
1098
1099HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1100{
1101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1102
1103 aOSTypeId = mUserData->s.strOsType;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1109{
1110 /* look up the object by Id to check it is valid */
1111 ComObjPtr<GuestOSType> pGuestOSType;
1112 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1113
1114 /* when setting, always use the "etalon" value for consistency -- lookup
1115 * by ID is case-insensitive and the input value may have different case */
1116 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1117
1118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1119
1120 HRESULT rc = i_checkStateDependency(MutableStateDep);
1121 if (FAILED(rc)) return rc;
1122
1123 i_setModified(IsModified_MachineData);
1124 mUserData.backup();
1125 mUserData->s.strOsType = osTypeId;
1126
1127 return S_OK;
1128}
1129
1130HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1131{
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 *aFirmwareType = mHWData->mFirmwareType;
1135
1136 return S_OK;
1137}
1138
1139HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1140{
1141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1142
1143 HRESULT rc = i_checkStateDependency(MutableStateDep);
1144 if (FAILED(rc)) return rc;
1145
1146 i_setModified(IsModified_MachineData);
1147 mHWData.backup();
1148 mHWData->mFirmwareType = aFirmwareType;
1149 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1150 alock.release();
1151
1152 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aPointingHIDType = mHWData->mPointingHIDType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 i_setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mPointingHIDType = aPointingHIDType;
1199
1200 return S_OK;
1201}
1202
1203HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1204{
1205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1206
1207 *aChipsetType = mHWData->mChipsetType;
1208
1209 return S_OK;
1210}
1211
1212HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1213{
1214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1215
1216 HRESULT rc = i_checkStateDependency(MutableStateDep);
1217 if (FAILED(rc)) return rc;
1218
1219 if (aChipsetType != mHWData->mChipsetType)
1220 {
1221 i_setModified(IsModified_MachineData);
1222 mHWData.backup();
1223 mHWData->mChipsetType = aChipsetType;
1224
1225 // Resize network adapter array, to be finalized on commit/rollback.
1226 // We must not throw away entries yet, otherwise settings are lost
1227 // without a way to roll back.
1228 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1229 size_t oldCount = mNetworkAdapters.size();
1230 if (newCount > oldCount)
1231 {
1232 mNetworkAdapters.resize(newCount);
1233 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1234 {
1235 unconst(mNetworkAdapters[slot]).createObject();
1236 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1237 }
1238 }
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aIommuType = mHWData->mIommuType;
1249
1250 return S_OK;
1251}
1252
1253HRESULT Machine::setIommuType(IommuType_T aIommuType)
1254{
1255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 HRESULT rc = i_checkStateDependency(MutableStateDep);
1258 if (FAILED(rc)) return rc;
1259
1260 if (aIommuType != mHWData->mIommuType)
1261 {
1262 if (aIommuType == IommuType_Intel)
1263 {
1264#ifndef VBOX_WITH_IOMMU_INTEL
1265 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1266 return E_UNEXPECTED;
1267#endif
1268 }
1269
1270 i_setModified(IsModified_MachineData);
1271 mHWData.backup();
1272 mHWData->mIommuType = aIommuType;
1273 }
1274
1275 return S_OK;
1276}
1277
1278HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1279{
1280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 aParavirtDebug = mHWData->mParavirtDebug;
1283 return S_OK;
1284}
1285
1286HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1287{
1288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1289
1290 HRESULT rc = i_checkStateDependency(MutableStateDep);
1291 if (FAILED(rc)) return rc;
1292
1293 /** @todo Parse/validate options? */
1294 if (aParavirtDebug != mHWData->mParavirtDebug)
1295 {
1296 i_setModified(IsModified_MachineData);
1297 mHWData.backup();
1298 mHWData->mParavirtDebug = aParavirtDebug;
1299 }
1300
1301 return S_OK;
1302}
1303
1304HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1305{
1306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1307
1308 *aParavirtProvider = mHWData->mParavirtProvider;
1309
1310 return S_OK;
1311}
1312
1313HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1314{
1315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 HRESULT rc = i_checkStateDependency(MutableStateDep);
1318 if (FAILED(rc)) return rc;
1319
1320 if (aParavirtProvider != mHWData->mParavirtProvider)
1321 {
1322 i_setModified(IsModified_MachineData);
1323 mHWData.backup();
1324 mHWData->mParavirtProvider = aParavirtProvider;
1325 }
1326
1327 return S_OK;
1328}
1329
1330HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1331{
1332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1333
1334 *aParavirtProvider = mHWData->mParavirtProvider;
1335 switch (mHWData->mParavirtProvider)
1336 {
1337 case ParavirtProvider_None:
1338 case ParavirtProvider_HyperV:
1339 case ParavirtProvider_KVM:
1340 case ParavirtProvider_Minimal:
1341 break;
1342
1343 /* Resolve dynamic provider types to the effective types. */
1344 default:
1345 {
1346 ComObjPtr<GuestOSType> pGuestOSType;
1347 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1348 pGuestOSType);
1349 if (FAILED(hrc2) || pGuestOSType.isNull())
1350 {
1351 *aParavirtProvider = ParavirtProvider_None;
1352 break;
1353 }
1354
1355 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1356 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1357
1358 switch (mHWData->mParavirtProvider)
1359 {
1360 case ParavirtProvider_Legacy:
1361 {
1362 if (fOsXGuest)
1363 *aParavirtProvider = ParavirtProvider_Minimal;
1364 else
1365 *aParavirtProvider = ParavirtProvider_None;
1366 break;
1367 }
1368
1369 case ParavirtProvider_Default:
1370 {
1371 if (fOsXGuest)
1372 *aParavirtProvider = ParavirtProvider_Minimal;
1373 else if ( mUserData->s.strOsType == "Windows11_64"
1374 || mUserData->s.strOsType == "Windows10"
1375 || mUserData->s.strOsType == "Windows10_64"
1376 || mUserData->s.strOsType == "Windows81"
1377 || mUserData->s.strOsType == "Windows81_64"
1378 || mUserData->s.strOsType == "Windows8"
1379 || mUserData->s.strOsType == "Windows8_64"
1380 || mUserData->s.strOsType == "Windows7"
1381 || mUserData->s.strOsType == "Windows7_64"
1382 || mUserData->s.strOsType == "WindowsVista"
1383 || mUserData->s.strOsType == "WindowsVista_64"
1384 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1385 || mUserData->s.strOsType.startsWith("Windows201"))
1386 && mUserData->s.strOsType.endsWith("_64"))
1387 || mUserData->s.strOsType == "Windows2012"
1388 || mUserData->s.strOsType == "Windows2012_64"
1389 || mUserData->s.strOsType == "Windows2008"
1390 || mUserData->s.strOsType == "Windows2008_64")
1391 {
1392 *aParavirtProvider = ParavirtProvider_HyperV;
1393 }
1394 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1395 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1396 || mUserData->s.strOsType == "Linux"
1397 || mUserData->s.strOsType == "Linux_64"
1398 || mUserData->s.strOsType == "ArchLinux"
1399 || mUserData->s.strOsType == "ArchLinux_64"
1400 || mUserData->s.strOsType == "Debian"
1401 || mUserData->s.strOsType == "Debian_64"
1402 || mUserData->s.strOsType == "Fedora"
1403 || mUserData->s.strOsType == "Fedora_64"
1404 || mUserData->s.strOsType == "Gentoo"
1405 || mUserData->s.strOsType == "Gentoo_64"
1406 || mUserData->s.strOsType == "Mandriva"
1407 || mUserData->s.strOsType == "Mandriva_64"
1408 || mUserData->s.strOsType == "OpenSUSE"
1409 || mUserData->s.strOsType == "OpenSUSE_64"
1410 || mUserData->s.strOsType == "Oracle"
1411 || mUserData->s.strOsType == "Oracle_64"
1412 || mUserData->s.strOsType == "RedHat"
1413 || mUserData->s.strOsType == "RedHat_64"
1414 || mUserData->s.strOsType == "Turbolinux"
1415 || mUserData->s.strOsType == "Turbolinux_64"
1416 || mUserData->s.strOsType == "Ubuntu"
1417 || mUserData->s.strOsType == "Ubuntu_64"
1418 || mUserData->s.strOsType == "Xandros"
1419 || mUserData->s.strOsType == "Xandros_64")
1420 {
1421 *aParavirtProvider = ParavirtProvider_KVM;
1422 }
1423 else
1424 *aParavirtProvider = ParavirtProvider_None;
1425 break;
1426 }
1427
1428 default: AssertFailedBreak(); /* Shut up MSC. */
1429 }
1430 break;
1431 }
1432 }
1433
1434 Assert( *aParavirtProvider == ParavirtProvider_None
1435 || *aParavirtProvider == ParavirtProvider_Minimal
1436 || *aParavirtProvider == ParavirtProvider_HyperV
1437 || *aParavirtProvider == ParavirtProvider_KVM);
1438 return S_OK;
1439}
1440
1441HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1442{
1443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 aHardwareVersion = mHWData->mHWVersion;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1451{
1452 /* check known version */
1453 Utf8Str hwVersion = aHardwareVersion;
1454 if ( hwVersion.compare("1") != 0
1455 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1456 return setError(E_INVALIDARG,
1457 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1458
1459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
1461 HRESULT rc = i_checkStateDependency(MutableStateDep);
1462 if (FAILED(rc)) return rc;
1463
1464 i_setModified(IsModified_MachineData);
1465 mHWData.backup();
1466 mHWData->mHWVersion = aHardwareVersion;
1467
1468 return S_OK;
1469}
1470
1471HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1472{
1473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1474
1475 if (!mHWData->mHardwareUUID.isZero())
1476 aHardwareUUID = mHWData->mHardwareUUID;
1477 else
1478 aHardwareUUID = mData->mUuid;
1479
1480 return S_OK;
1481}
1482
1483HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1484{
1485 if (!aHardwareUUID.isValid())
1486 return E_INVALIDARG;
1487
1488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 HRESULT rc = i_checkStateDependency(MutableStateDep);
1491 if (FAILED(rc)) return rc;
1492
1493 i_setModified(IsModified_MachineData);
1494 mHWData.backup();
1495 if (aHardwareUUID == mData->mUuid)
1496 mHWData->mHardwareUUID.clear();
1497 else
1498 mHWData->mHardwareUUID = aHardwareUUID;
1499
1500 return S_OK;
1501}
1502
1503HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1504{
1505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1506
1507 *aMemorySize = mHWData->mMemorySize;
1508
1509 return S_OK;
1510}
1511
1512HRESULT Machine::setMemorySize(ULONG aMemorySize)
1513{
1514 /* check RAM limits */
1515 if ( aMemorySize < MM_RAM_MIN_IN_MB
1516 || aMemorySize > MM_RAM_MAX_IN_MB
1517 )
1518 return setError(E_INVALIDARG,
1519 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1520 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1521
1522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1523
1524 HRESULT rc = i_checkStateDependency(MutableStateDep);
1525 if (FAILED(rc)) return rc;
1526
1527 i_setModified(IsModified_MachineData);
1528 mHWData.backup();
1529 mHWData->mMemorySize = aMemorySize;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1535{
1536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 *aCPUCount = mHWData->mCPUCount;
1539
1540 return S_OK;
1541}
1542
1543HRESULT Machine::setCPUCount(ULONG aCPUCount)
1544{
1545 /* check CPU limits */
1546 if ( aCPUCount < SchemaDefs::MinCPUCount
1547 || aCPUCount > SchemaDefs::MaxCPUCount
1548 )
1549 return setError(E_INVALIDARG,
1550 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1551 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1552
1553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1554
1555 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1556 if (mHWData->mCPUHotPlugEnabled)
1557 {
1558 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1559 {
1560 if (mHWData->mCPUAttached[idx])
1561 return setError(E_INVALIDARG,
1562 tr("There is still a CPU attached to socket %lu."
1563 "Detach the CPU before removing the socket"),
1564 aCPUCount, idx+1);
1565 }
1566 }
1567
1568 HRESULT rc = i_checkStateDependency(MutableStateDep);
1569 if (FAILED(rc)) return rc;
1570
1571 i_setModified(IsModified_MachineData);
1572 mHWData.backup();
1573 mHWData->mCPUCount = aCPUCount;
1574
1575 return S_OK;
1576}
1577
1578HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1579{
1580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1581
1582 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1583
1584 return S_OK;
1585}
1586
1587HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1588{
1589 HRESULT rc = S_OK;
1590
1591 /* check throttle limits */
1592 if ( aCPUExecutionCap < 1
1593 || aCPUExecutionCap > 100
1594 )
1595 return setError(E_INVALIDARG,
1596 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1597 aCPUExecutionCap, 1, 100);
1598
1599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 alock.release();
1602 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1603 alock.acquire();
1604 if (FAILED(rc)) return rc;
1605
1606 i_setModified(IsModified_MachineData);
1607 mHWData.backup();
1608 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1609
1610 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1611 if (Global::IsOnline(mData->mMachineState))
1612 i_saveSettings(NULL, alock);
1613
1614 return S_OK;
1615}
1616
1617HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1618{
1619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1620
1621 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1622
1623 return S_OK;
1624}
1625
1626HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1627{
1628 HRESULT rc = S_OK;
1629
1630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1631
1632 rc = i_checkStateDependency(MutableStateDep);
1633 if (FAILED(rc)) return rc;
1634
1635 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1636 {
1637 if (aCPUHotPlugEnabled)
1638 {
1639 i_setModified(IsModified_MachineData);
1640 mHWData.backup();
1641
1642 /* Add the amount of CPUs currently attached */
1643 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1644 mHWData->mCPUAttached[i] = true;
1645 }
1646 else
1647 {
1648 /*
1649 * We can disable hotplug only if the amount of maximum CPUs is equal
1650 * to the amount of attached CPUs
1651 */
1652 unsigned cCpusAttached = 0;
1653 unsigned iHighestId = 0;
1654
1655 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1656 {
1657 if (mHWData->mCPUAttached[i])
1658 {
1659 cCpusAttached++;
1660 iHighestId = i;
1661 }
1662 }
1663
1664 if ( (cCpusAttached != mHWData->mCPUCount)
1665 || (iHighestId >= mHWData->mCPUCount))
1666 return setError(E_INVALIDARG,
1667 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1668
1669 i_setModified(IsModified_MachineData);
1670 mHWData.backup();
1671 }
1672 }
1673
1674 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1675
1676 return rc;
1677}
1678
1679HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1680{
1681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1684
1685 return S_OK;
1686}
1687
1688HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1689{
1690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1693 if (SUCCEEDED(hrc))
1694 {
1695 i_setModified(IsModified_MachineData);
1696 mHWData.backup();
1697 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1698 }
1699 return hrc;
1700}
1701
1702HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1703{
1704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1705 aCPUProfile = mHWData->mCpuProfile;
1706 return S_OK;
1707}
1708
1709HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1710{
1711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1712 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1713 if (SUCCEEDED(hrc))
1714 {
1715 i_setModified(IsModified_MachineData);
1716 mHWData.backup();
1717 /* Empty equals 'host'. */
1718 if (aCPUProfile.isNotEmpty())
1719 mHWData->mCpuProfile = aCPUProfile;
1720 else
1721 mHWData->mCpuProfile = "host";
1722 }
1723 return hrc;
1724}
1725
1726HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1727{
1728#ifdef VBOX_WITH_USB_CARDREADER
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1732
1733 return S_OK;
1734#else
1735 NOREF(aEmulatedUSBCardReaderEnabled);
1736 return E_NOTIMPL;
1737#endif
1738}
1739
1740HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1741{
1742#ifdef VBOX_WITH_USB_CARDREADER
1743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1746 if (FAILED(rc)) return rc;
1747
1748 i_setModified(IsModified_MachineData);
1749 mHWData.backup();
1750 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1751
1752 return S_OK;
1753#else
1754 NOREF(aEmulatedUSBCardReaderEnabled);
1755 return E_NOTIMPL;
1756#endif
1757}
1758
1759HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1760{
1761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1762
1763 *aHPETEnabled = mHWData->mHPETEnabled;
1764
1765 return S_OK;
1766}
1767
1768HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1769{
1770 HRESULT rc = S_OK;
1771
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 rc = i_checkStateDependency(MutableStateDep);
1775 if (FAILED(rc)) return rc;
1776
1777 i_setModified(IsModified_MachineData);
1778 mHWData.backup();
1779
1780 mHWData->mHPETEnabled = aHPETEnabled;
1781
1782 return rc;
1783}
1784
1785/** @todo this method should not be public */
1786HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1787{
1788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1789
1790 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1791
1792 return S_OK;
1793}
1794
1795/**
1796 * Set the memory balloon size.
1797 *
1798 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1799 * we have to make sure that we never call IGuest from here.
1800 */
1801HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1802{
1803 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1804#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1805 /* check limits */
1806 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1807 return setError(E_INVALIDARG,
1808 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1809 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1810
1811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1812
1813 i_setModified(IsModified_MachineData);
1814 mHWData.backup();
1815 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1816
1817 return S_OK;
1818#else
1819 NOREF(aMemoryBalloonSize);
1820 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1821#endif
1822}
1823
1824HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1825{
1826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1829 return S_OK;
1830}
1831
1832HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1833{
1834#ifdef VBOX_WITH_PAGE_SHARING
1835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1836
1837 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1838 i_setModified(IsModified_MachineData);
1839 mHWData.backup();
1840 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1841 return S_OK;
1842#else
1843 NOREF(aPageFusionEnabled);
1844 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1845#endif
1846}
1847
1848HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1849{
1850 /* mBIOSSettings is constant during life time, no need to lock */
1851 aBIOSSettings = mBIOSSettings;
1852
1853 return S_OK;
1854}
1855
1856HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1857{
1858 /* mTrustedPlatformModule is constant during life time, no need to lock */
1859 aTrustedPlatformModule = mTrustedPlatformModule;
1860
1861 return S_OK;
1862}
1863
1864HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1865{
1866 /* mNvramStore is constant during life time, no need to lock */
1867 aNvramStore = mNvramStore;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1873{
1874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1875
1876 aRecordingSettings = mRecordingSettings;
1877
1878 return S_OK;
1879}
1880
1881HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884
1885 aGraphicsAdapter = mGraphicsAdapter;
1886
1887 return S_OK;
1888}
1889
1890HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1891{
1892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1893
1894 switch (aProperty)
1895 {
1896 case CPUPropertyType_PAE:
1897 *aValue = mHWData->mPAEEnabled;
1898 break;
1899
1900 case CPUPropertyType_LongMode:
1901 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1902 *aValue = TRUE;
1903 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1904 *aValue = FALSE;
1905#if HC_ARCH_BITS == 64
1906 else
1907 *aValue = TRUE;
1908#else
1909 else
1910 {
1911 *aValue = FALSE;
1912
1913 ComObjPtr<GuestOSType> pGuestOSType;
1914 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1915 pGuestOSType);
1916 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1917 {
1918 if (pGuestOSType->i_is64Bit())
1919 {
1920 ComObjPtr<Host> pHost = mParent->i_host();
1921 alock.release();
1922
1923 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1924 if (FAILED(hrc2))
1925 *aValue = FALSE;
1926 }
1927 }
1928 }
1929#endif
1930 break;
1931
1932 case CPUPropertyType_TripleFaultReset:
1933 *aValue = mHWData->mTripleFaultReset;
1934 break;
1935
1936 case CPUPropertyType_APIC:
1937 *aValue = mHWData->mAPIC;
1938 break;
1939
1940 case CPUPropertyType_X2APIC:
1941 *aValue = mHWData->mX2APIC;
1942 break;
1943
1944 case CPUPropertyType_IBPBOnVMExit:
1945 *aValue = mHWData->mIBPBOnVMExit;
1946 break;
1947
1948 case CPUPropertyType_IBPBOnVMEntry:
1949 *aValue = mHWData->mIBPBOnVMEntry;
1950 break;
1951
1952 case CPUPropertyType_SpecCtrl:
1953 *aValue = mHWData->mSpecCtrl;
1954 break;
1955
1956 case CPUPropertyType_SpecCtrlByHost:
1957 *aValue = mHWData->mSpecCtrlByHost;
1958 break;
1959
1960 case CPUPropertyType_HWVirt:
1961 *aValue = mHWData->mNestedHWVirt;
1962 break;
1963
1964 case CPUPropertyType_L1DFlushOnEMTScheduling:
1965 *aValue = mHWData->mL1DFlushOnSched;
1966 break;
1967
1968 case CPUPropertyType_L1DFlushOnVMEntry:
1969 *aValue = mHWData->mL1DFlushOnVMEntry;
1970 break;
1971
1972 case CPUPropertyType_MDSClearOnEMTScheduling:
1973 *aValue = mHWData->mMDSClearOnSched;
1974 break;
1975
1976 case CPUPropertyType_MDSClearOnVMEntry:
1977 *aValue = mHWData->mMDSClearOnVMEntry;
1978 break;
1979
1980 default:
1981 return E_INVALIDARG;
1982 }
1983 return S_OK;
1984}
1985
1986HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1987{
1988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1989
1990 HRESULT rc = i_checkStateDependency(MutableStateDep);
1991 if (FAILED(rc)) return rc;
1992
1993 switch (aProperty)
1994 {
1995 case CPUPropertyType_PAE:
1996 i_setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mPAEEnabled = !!aValue;
1999 break;
2000
2001 case CPUPropertyType_LongMode:
2002 i_setModified(IsModified_MachineData);
2003 mHWData.backup();
2004 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2005 break;
2006
2007 case CPUPropertyType_TripleFaultReset:
2008 i_setModified(IsModified_MachineData);
2009 mHWData.backup();
2010 mHWData->mTripleFaultReset = !!aValue;
2011 break;
2012
2013 case CPUPropertyType_APIC:
2014 if (mHWData->mX2APIC)
2015 aValue = TRUE;
2016 i_setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mAPIC = !!aValue;
2019 break;
2020
2021 case CPUPropertyType_X2APIC:
2022 i_setModified(IsModified_MachineData);
2023 mHWData.backup();
2024 mHWData->mX2APIC = !!aValue;
2025 if (aValue)
2026 mHWData->mAPIC = !!aValue;
2027 break;
2028
2029 case CPUPropertyType_IBPBOnVMExit:
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mIBPBOnVMExit = !!aValue;
2033 break;
2034
2035 case CPUPropertyType_IBPBOnVMEntry:
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mIBPBOnVMEntry = !!aValue;
2039 break;
2040
2041 case CPUPropertyType_SpecCtrl:
2042 i_setModified(IsModified_MachineData);
2043 mHWData.backup();
2044 mHWData->mSpecCtrl = !!aValue;
2045 break;
2046
2047 case CPUPropertyType_SpecCtrlByHost:
2048 i_setModified(IsModified_MachineData);
2049 mHWData.backup();
2050 mHWData->mSpecCtrlByHost = !!aValue;
2051 break;
2052
2053 case CPUPropertyType_HWVirt:
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mNestedHWVirt = !!aValue;
2057 break;
2058
2059 case CPUPropertyType_L1DFlushOnEMTScheduling:
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mL1DFlushOnSched = !!aValue;
2063 break;
2064
2065 case CPUPropertyType_L1DFlushOnVMEntry:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mL1DFlushOnVMEntry = !!aValue;
2069 break;
2070
2071 case CPUPropertyType_MDSClearOnEMTScheduling:
2072 i_setModified(IsModified_MachineData);
2073 mHWData.backup();
2074 mHWData->mMDSClearOnSched = !!aValue;
2075 break;
2076
2077 case CPUPropertyType_MDSClearOnVMEntry:
2078 i_setModified(IsModified_MachineData);
2079 mHWData.backup();
2080 mHWData->mMDSClearOnVMEntry = !!aValue;
2081 break;
2082
2083 default:
2084 return E_INVALIDARG;
2085 }
2086 return S_OK;
2087}
2088
2089HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2090 ULONG *aValEcx, ULONG *aValEdx)
2091{
2092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2093 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2094 {
2095 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2096 it != mHWData->mCpuIdLeafList.end();
2097 ++it)
2098 {
2099 if (aOrdinal == 0)
2100 {
2101 const settings::CpuIdLeaf &rLeaf= *it;
2102 *aIdx = rLeaf.idx;
2103 *aSubIdx = rLeaf.idxSub;
2104 *aValEax = rLeaf.uEax;
2105 *aValEbx = rLeaf.uEbx;
2106 *aValEcx = rLeaf.uEcx;
2107 *aValEdx = rLeaf.uEdx;
2108 return S_OK;
2109 }
2110 aOrdinal--;
2111 }
2112 }
2113 return E_INVALIDARG;
2114}
2115
2116HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2117{
2118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2119
2120 /*
2121 * Search the list.
2122 */
2123 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2124 {
2125 const settings::CpuIdLeaf &rLeaf= *it;
2126 if ( rLeaf.idx == aIdx
2127 && ( aSubIdx == UINT32_MAX
2128 || rLeaf.idxSub == aSubIdx) )
2129 {
2130 *aValEax = rLeaf.uEax;
2131 *aValEbx = rLeaf.uEbx;
2132 *aValEcx = rLeaf.uEcx;
2133 *aValEdx = rLeaf.uEdx;
2134 return S_OK;
2135 }
2136 }
2137
2138 return E_INVALIDARG;
2139}
2140
2141
2142HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2143{
2144 /*
2145 * Validate input before taking locks and checking state.
2146 */
2147 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2148 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2149 if ( aIdx >= UINT32_C(0x20)
2150 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2151 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2152 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2153
2154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2155 HRESULT rc = i_checkStateDependency(MutableStateDep);
2156 if (FAILED(rc)) return rc;
2157
2158 /*
2159 * Impose a maximum number of leaves.
2160 */
2161 if (mHWData->mCpuIdLeafList.size() > 256)
2162 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2163
2164 /*
2165 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2166 */
2167 i_setModified(IsModified_MachineData);
2168 mHWData.backup();
2169
2170 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2171 {
2172 settings::CpuIdLeaf &rLeaf= *it;
2173 if ( rLeaf.idx == aIdx
2174 && ( aSubIdx == UINT32_MAX
2175 || rLeaf.idxSub == aSubIdx) )
2176 it = mHWData->mCpuIdLeafList.erase(it);
2177 else
2178 ++it;
2179 }
2180
2181 settings::CpuIdLeaf NewLeaf;
2182 NewLeaf.idx = aIdx;
2183 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2184 NewLeaf.uEax = aValEax;
2185 NewLeaf.uEbx = aValEbx;
2186 NewLeaf.uEcx = aValEcx;
2187 NewLeaf.uEdx = aValEdx;
2188 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2189 return S_OK;
2190}
2191
2192HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2193{
2194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2195
2196 HRESULT rc = i_checkStateDependency(MutableStateDep);
2197 if (FAILED(rc)) return rc;
2198
2199 /*
2200 * Do the removal.
2201 */
2202 bool fModified = mHWData.isBackedUp();
2203 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2204 {
2205 settings::CpuIdLeaf &rLeaf= *it;
2206 if ( rLeaf.idx == aIdx
2207 && ( aSubIdx == UINT32_MAX
2208 || rLeaf.idxSub == aSubIdx) )
2209 {
2210 if (!fModified)
2211 {
2212 fModified = true;
2213 i_setModified(IsModified_MachineData);
2214 mHWData.backup();
2215 // Start from the beginning, since mHWData.backup() creates
2216 // a new list, causing iterator mixup. This makes sure that
2217 // the settings are not unnecessarily marked as modified,
2218 // at the price of extra list walking.
2219 it = mHWData->mCpuIdLeafList.begin();
2220 }
2221 else
2222 it = mHWData->mCpuIdLeafList.erase(it);
2223 }
2224 else
2225 ++it;
2226 }
2227
2228 return S_OK;
2229}
2230
2231HRESULT Machine::removeAllCPUIDLeaves()
2232{
2233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2234
2235 HRESULT rc = i_checkStateDependency(MutableStateDep);
2236 if (FAILED(rc)) return rc;
2237
2238 if (mHWData->mCpuIdLeafList.size() > 0)
2239 {
2240 i_setModified(IsModified_MachineData);
2241 mHWData.backup();
2242
2243 mHWData->mCpuIdLeafList.clear();
2244 }
2245
2246 return S_OK;
2247}
2248HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2249{
2250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2251
2252 switch(aProperty)
2253 {
2254 case HWVirtExPropertyType_Enabled:
2255 *aValue = mHWData->mHWVirtExEnabled;
2256 break;
2257
2258 case HWVirtExPropertyType_VPID:
2259 *aValue = mHWData->mHWVirtExVPIDEnabled;
2260 break;
2261
2262 case HWVirtExPropertyType_NestedPaging:
2263 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2264 break;
2265
2266 case HWVirtExPropertyType_UnrestrictedExecution:
2267 *aValue = mHWData->mHWVirtExUXEnabled;
2268 break;
2269
2270 case HWVirtExPropertyType_LargePages:
2271 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2272 break;
2273
2274 case HWVirtExPropertyType_Force:
2275 *aValue = mHWData->mHWVirtExForceEnabled;
2276 break;
2277
2278 case HWVirtExPropertyType_UseNativeApi:
2279 *aValue = mHWData->mHWVirtExUseNativeApi;
2280 break;
2281
2282 case HWVirtExPropertyType_VirtVmsaveVmload:
2283 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2284 break;
2285
2286 default:
2287 return E_INVALIDARG;
2288 }
2289 return S_OK;
2290}
2291
2292HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2293{
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 HRESULT rc = i_checkStateDependency(MutableStateDep);
2297 if (FAILED(rc)) return rc;
2298
2299 switch (aProperty)
2300 {
2301 case HWVirtExPropertyType_Enabled:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mHWVirtExEnabled = !!aValue;
2305 break;
2306
2307 case HWVirtExPropertyType_VPID:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2311 break;
2312
2313 case HWVirtExPropertyType_NestedPaging:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2317 break;
2318
2319 case HWVirtExPropertyType_UnrestrictedExecution:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mHWVirtExUXEnabled = !!aValue;
2323 break;
2324
2325 case HWVirtExPropertyType_LargePages:
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2329 break;
2330
2331 case HWVirtExPropertyType_Force:
2332 i_setModified(IsModified_MachineData);
2333 mHWData.backup();
2334 mHWData->mHWVirtExForceEnabled = !!aValue;
2335 break;
2336
2337 case HWVirtExPropertyType_UseNativeApi:
2338 i_setModified(IsModified_MachineData);
2339 mHWData.backup();
2340 mHWData->mHWVirtExUseNativeApi = !!aValue;
2341 break;
2342
2343 case HWVirtExPropertyType_VirtVmsaveVmload:
2344 i_setModified(IsModified_MachineData);
2345 mHWData.backup();
2346 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2347 break;
2348
2349 default:
2350 return E_INVALIDARG;
2351 }
2352
2353 return S_OK;
2354}
2355
2356HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2357{
2358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2359
2360 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2361
2362 return S_OK;
2363}
2364
2365HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2366{
2367 /** @todo (r=dmik):
2368 * 1. Allow to change the name of the snapshot folder containing snapshots
2369 * 2. Rename the folder on disk instead of just changing the property
2370 * value (to be smart and not to leave garbage). Note that it cannot be
2371 * done here because the change may be rolled back. Thus, the right
2372 * place is #saveSettings().
2373 */
2374
2375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2376
2377 HRESULT rc = i_checkStateDependency(MutableStateDep);
2378 if (FAILED(rc)) return rc;
2379
2380 if (!mData->mCurrentSnapshot.isNull())
2381 return setError(E_FAIL,
2382 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2383
2384 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2385
2386 if (strSnapshotFolder.isEmpty())
2387 strSnapshotFolder = "Snapshots";
2388 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2389 if (RT_FAILURE(vrc))
2390 return setErrorBoth(E_FAIL, vrc,
2391 tr("Invalid snapshot folder '%s' (%Rrc)"),
2392 strSnapshotFolder.c_str(), vrc);
2393
2394 i_setModified(IsModified_MachineData);
2395 mUserData.backup();
2396
2397 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2398
2399 return S_OK;
2400}
2401
2402HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2403{
2404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2405
2406 aMediumAttachments.resize(mMediumAttachments->size());
2407 size_t i = 0;
2408 for (MediumAttachmentList::const_iterator
2409 it = mMediumAttachments->begin();
2410 it != mMediumAttachments->end();
2411 ++it, ++i)
2412 aMediumAttachments[i] = *it;
2413
2414 return S_OK;
2415}
2416
2417HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2418{
2419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2420
2421 Assert(!!mVRDEServer);
2422
2423 aVRDEServer = mVRDEServer;
2424
2425 return S_OK;
2426}
2427
2428HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2429{
2430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2431
2432 aAudioAdapter = mAudioAdapter;
2433
2434 return S_OK;
2435}
2436
2437HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2438{
2439#ifdef VBOX_WITH_VUSB
2440 clearError();
2441 MultiResult rc(S_OK);
2442
2443# ifdef VBOX_WITH_USB
2444 rc = mParent->i_host()->i_checkUSBProxyService();
2445 if (FAILED(rc)) return rc;
2446# endif
2447
2448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2449
2450 aUSBControllers.resize(mUSBControllers->size());
2451 size_t i = 0;
2452 for (USBControllerList::const_iterator
2453 it = mUSBControllers->begin();
2454 it != mUSBControllers->end();
2455 ++it, ++i)
2456 aUSBControllers[i] = *it;
2457
2458 return S_OK;
2459#else
2460 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2461 * extended error info to indicate that USB is simply not available
2462 * (w/o treating it as a failure), for example, as in OSE */
2463 NOREF(aUSBControllers);
2464 ReturnComNotImplemented();
2465#endif /* VBOX_WITH_VUSB */
2466}
2467
2468HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2469{
2470#ifdef VBOX_WITH_VUSB
2471 clearError();
2472 MultiResult rc(S_OK);
2473
2474# ifdef VBOX_WITH_USB
2475 rc = mParent->i_host()->i_checkUSBProxyService();
2476 if (FAILED(rc)) return rc;
2477# endif
2478
2479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2480
2481 aUSBDeviceFilters = mUSBDeviceFilters;
2482 return rc;
2483#else
2484 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2485 * extended error info to indicate that USB is simply not available
2486 * (w/o treating it as a failure), for example, as in OSE */
2487 NOREF(aUSBDeviceFilters);
2488 ReturnComNotImplemented();
2489#endif /* VBOX_WITH_VUSB */
2490}
2491
2492HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2493{
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 aSettingsFilePath = mData->m_strConfigFileFull;
2497
2498 return S_OK;
2499}
2500
2501HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2502{
2503 RT_NOREF(aSettingsFilePath);
2504 ReturnComNotImplemented();
2505}
2506
2507HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2508{
2509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2510
2511 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2512 if (FAILED(rc)) return rc;
2513
2514 if (!mData->pMachineConfigFile->fileExists())
2515 // this is a new machine, and no config file exists yet:
2516 *aSettingsModified = TRUE;
2517 else
2518 *aSettingsModified = (mData->flModifications != 0);
2519
2520 return S_OK;
2521}
2522
2523HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2524{
2525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2526
2527 *aSessionState = mData->mSession.mState;
2528
2529 return S_OK;
2530}
2531
2532HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2533{
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 aSessionName = mData->mSession.mName;
2537
2538 return S_OK;
2539}
2540
2541HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2542{
2543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2544
2545 *aSessionPID = mData->mSession.mPID;
2546
2547 return S_OK;
2548}
2549
2550HRESULT Machine::getState(MachineState_T *aState)
2551{
2552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2553
2554 *aState = mData->mMachineState;
2555 Assert(mData->mMachineState != MachineState_Null);
2556
2557 return S_OK;
2558}
2559
2560HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2561{
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2565
2566 return S_OK;
2567}
2568
2569HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2570{
2571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573 aStateFilePath = mSSData->strStateFilePath;
2574
2575 return S_OK;
2576}
2577
2578HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2579{
2580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2581
2582 i_getLogFolder(aLogFolder);
2583
2584 return S_OK;
2585}
2586
2587HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2588{
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 aCurrentSnapshot = mData->mCurrentSnapshot;
2592
2593 return S_OK;
2594}
2595
2596HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2597{
2598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2601 ? 0
2602 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2603
2604 return S_OK;
2605}
2606
2607HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2608{
2609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2610
2611 /* Note: for machines with no snapshots, we always return FALSE
2612 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2613 * reasons :) */
2614
2615 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2616 ? FALSE
2617 : mData->mCurrentStateModified;
2618
2619 return S_OK;
2620}
2621
2622HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2623{
2624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 aSharedFolders.resize(mHWData->mSharedFolders.size());
2627 size_t i = 0;
2628 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2629 it = mHWData->mSharedFolders.begin();
2630 it != mHWData->mSharedFolders.end();
2631 ++it, ++i)
2632 aSharedFolders[i] = *it;
2633
2634 return S_OK;
2635}
2636
2637HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2638{
2639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 *aClipboardMode = mHWData->mClipboardMode;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2647{
2648 HRESULT rc = S_OK;
2649
2650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 alock.release();
2653 rc = i_onClipboardModeChange(aClipboardMode);
2654 alock.acquire();
2655 if (FAILED(rc)) return rc;
2656
2657 i_setModified(IsModified_MachineData);
2658 mHWData.backup();
2659 mHWData->mClipboardMode = aClipboardMode;
2660
2661 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2662 if (Global::IsOnline(mData->mMachineState))
2663 i_saveSettings(NULL, alock);
2664
2665 return S_OK;
2666}
2667
2668HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2669{
2670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2671
2672 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2673
2674 return S_OK;
2675}
2676
2677HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2678{
2679 HRESULT rc = S_OK;
2680
2681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 alock.release();
2684 rc = i_onClipboardFileTransferModeChange(aEnabled);
2685 alock.acquire();
2686 if (FAILED(rc)) return rc;
2687
2688 i_setModified(IsModified_MachineData);
2689 mHWData.backup();
2690 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2691
2692 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2693 if (Global::IsOnline(mData->mMachineState))
2694 i_saveSettings(NULL, alock);
2695
2696 return S_OK;
2697}
2698
2699HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2700{
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 *aDnDMode = mHWData->mDnDMode;
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2709{
2710 HRESULT rc = S_OK;
2711
2712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 alock.release();
2715 rc = i_onDnDModeChange(aDnDMode);
2716
2717 alock.acquire();
2718 if (FAILED(rc)) return rc;
2719
2720 i_setModified(IsModified_MachineData);
2721 mHWData.backup();
2722 mHWData->mDnDMode = aDnDMode;
2723
2724 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2725 if (Global::IsOnline(mData->mMachineState))
2726 i_saveSettings(NULL, alock);
2727
2728 return S_OK;
2729}
2730
2731HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2732{
2733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2734
2735 aStorageControllers.resize(mStorageControllers->size());
2736 size_t i = 0;
2737 for (StorageControllerList::const_iterator
2738 it = mStorageControllers->begin();
2739 it != mStorageControllers->end();
2740 ++it, ++i)
2741 aStorageControllers[i] = *it;
2742
2743 return S_OK;
2744}
2745
2746HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2747{
2748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2749
2750 *aEnabled = mUserData->s.fTeleporterEnabled;
2751
2752 return S_OK;
2753}
2754
2755HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2756{
2757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2758
2759 /* Only allow it to be set to true when PoweredOff or Aborted.
2760 (Clearing it is always permitted.) */
2761 if ( aTeleporterEnabled
2762 && mData->mRegistered
2763 && ( !i_isSessionMachine()
2764 || ( mData->mMachineState != MachineState_PoweredOff
2765 && mData->mMachineState != MachineState_Teleported
2766 && mData->mMachineState != MachineState_Aborted
2767 )
2768 )
2769 )
2770 return setError(VBOX_E_INVALID_VM_STATE,
2771 tr("The machine is not powered off (state is %s)"),
2772 Global::stringifyMachineState(mData->mMachineState));
2773
2774 i_setModified(IsModified_MachineData);
2775 mUserData.backup();
2776 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2782{
2783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2786
2787 return S_OK;
2788}
2789
2790HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2791{
2792 if (aTeleporterPort >= _64K)
2793 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2794
2795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2798 if (FAILED(rc)) return rc;
2799
2800 i_setModified(IsModified_MachineData);
2801 mUserData.backup();
2802 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2817{
2818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2821 if (FAILED(rc)) return rc;
2822
2823 i_setModified(IsModified_MachineData);
2824 mUserData.backup();
2825 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2826
2827 return S_OK;
2828}
2829
2830HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2831{
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2839{
2840 /*
2841 * Hash the password first.
2842 */
2843 com::Utf8Str aT = aTeleporterPassword;
2844
2845 if (!aT.isEmpty())
2846 {
2847 if (VBoxIsPasswordHashed(&aT))
2848 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2849 VBoxHashPassword(&aT);
2850 }
2851
2852 /*
2853 * Do the update.
2854 */
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2857 if (SUCCEEDED(hrc))
2858 {
2859 i_setModified(IsModified_MachineData);
2860 mUserData.backup();
2861 mUserData->s.strTeleporterPassword = aT;
2862 }
2863
2864 return hrc;
2865}
2866
2867HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870
2871 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2872
2873 return S_OK;
2874}
2875
2876HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2877{
2878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 /* Only allow it to be set to true when PoweredOff or Aborted.
2881 (Clearing it is always permitted.) */
2882 if ( aRTCUseUTC
2883 && mData->mRegistered
2884 && ( !i_isSessionMachine()
2885 || ( mData->mMachineState != MachineState_PoweredOff
2886 && mData->mMachineState != MachineState_Teleported
2887 && mData->mMachineState != MachineState_Aborted
2888 )
2889 )
2890 )
2891 return setError(VBOX_E_INVALID_VM_STATE,
2892 tr("The machine is not powered off (state is %s)"),
2893 Global::stringifyMachineState(mData->mMachineState));
2894
2895 i_setModified(IsModified_MachineData);
2896 mUserData.backup();
2897 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2898
2899 return S_OK;
2900}
2901
2902HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2903{
2904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2907
2908 return S_OK;
2909}
2910
2911HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2912{
2913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2914
2915 HRESULT rc = i_checkStateDependency(MutableStateDep);
2916 if (FAILED(rc)) return rc;
2917
2918 i_setModified(IsModified_MachineData);
2919 mHWData.backup();
2920 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 *aIOCacheSize = mHWData->mIOCacheSize;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2935{
2936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 HRESULT rc = i_checkStateDependency(MutableStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 i_setModified(IsModified_MachineData);
2942 mHWData.backup();
2943 mHWData->mIOCacheSize = aIOCacheSize;
2944
2945 return S_OK;
2946}
2947
2948
2949/**
2950 * @note Locks objects!
2951 */
2952HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2953 LockType_T aLockType)
2954{
2955 /* check the session state */
2956 SessionState_T state;
2957 HRESULT rc = aSession->COMGETTER(State)(&state);
2958 if (FAILED(rc)) return rc;
2959
2960 if (state != SessionState_Unlocked)
2961 return setError(VBOX_E_INVALID_OBJECT_STATE,
2962 tr("The given session is busy"));
2963
2964 // get the client's IInternalSessionControl interface
2965 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2966 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2967 E_INVALIDARG);
2968
2969 // session name (only used in some code paths)
2970 Utf8Str strSessionName;
2971
2972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2973
2974 if (!mData->mRegistered)
2975 return setError(E_UNEXPECTED,
2976 tr("The machine '%s' is not registered"),
2977 mUserData->s.strName.c_str());
2978
2979 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2980
2981 SessionState_T oldState = mData->mSession.mState;
2982 /* Hack: in case the session is closing and there is a progress object
2983 * which allows waiting for the session to be closed, take the opportunity
2984 * and do a limited wait (max. 1 second). This helps a lot when the system
2985 * is busy and thus session closing can take a little while. */
2986 if ( mData->mSession.mState == SessionState_Unlocking
2987 && mData->mSession.mProgress)
2988 {
2989 alock.release();
2990 mData->mSession.mProgress->WaitForCompletion(1000);
2991 alock.acquire();
2992 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2993 }
2994
2995 // try again now
2996 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2997 // (i.e. session machine exists)
2998 && (aLockType == LockType_Shared) // caller wants a shared link to the
2999 // existing session that holds the write lock:
3000 )
3001 {
3002 // OK, share the session... we are now dealing with three processes:
3003 // 1) VBoxSVC (where this code runs);
3004 // 2) process C: the caller's client process (who wants a shared session);
3005 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3006
3007 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3008 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3009 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3010 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3011 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3012
3013 /*
3014 * Release the lock before calling the client process. It's safe here
3015 * since the only thing to do after we get the lock again is to add
3016 * the remote control to the list (which doesn't directly influence
3017 * anything).
3018 */
3019 alock.release();
3020
3021 // get the console of the session holding the write lock (this is a remote call)
3022 ComPtr<IConsole> pConsoleW;
3023 if (mData->mSession.mLockType == LockType_VM)
3024 {
3025 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3026 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3027 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3028 if (FAILED(rc))
3029 // the failure may occur w/o any error info (from RPC), so provide one
3030 return setError(VBOX_E_VM_ERROR,
3031 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3032 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3033 }
3034
3035 // share the session machine and W's console with the caller's session
3036 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3037 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3038 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3039
3040 if (FAILED(rc))
3041 // the failure may occur w/o any error info (from RPC), so provide one
3042 return setError(VBOX_E_VM_ERROR,
3043 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3044 alock.acquire();
3045
3046 // need to revalidate the state after acquiring the lock again
3047 if (mData->mSession.mState != SessionState_Locked)
3048 {
3049 pSessionControl->Uninitialize();
3050 return setError(VBOX_E_INVALID_SESSION_STATE,
3051 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3052 mUserData->s.strName.c_str());
3053 }
3054
3055 // add the caller's session to the list
3056 mData->mSession.mRemoteControls.push_back(pSessionControl);
3057 }
3058 else if ( mData->mSession.mState == SessionState_Locked
3059 || mData->mSession.mState == SessionState_Unlocking
3060 )
3061 {
3062 // sharing not permitted, or machine still unlocking:
3063 return setError(VBOX_E_INVALID_OBJECT_STATE,
3064 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3065 mUserData->s.strName.c_str());
3066 }
3067 else
3068 {
3069 // machine is not locked: then write-lock the machine (create the session machine)
3070
3071 // must not be busy
3072 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3073
3074 // get the caller's session PID
3075 RTPROCESS pid = NIL_RTPROCESS;
3076 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3077 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3078 Assert(pid != NIL_RTPROCESS);
3079
3080 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3081
3082 if (fLaunchingVMProcess)
3083 {
3084 if (mData->mSession.mPID == NIL_RTPROCESS)
3085 {
3086 // two or more clients racing for a lock, the one which set the
3087 // session state to Spawning will win, the others will get an
3088 // error as we can't decide here if waiting a little would help
3089 // (only for shared locks this would avoid an error)
3090 return setError(VBOX_E_INVALID_OBJECT_STATE,
3091 tr("The machine '%s' already has a lock request pending"),
3092 mUserData->s.strName.c_str());
3093 }
3094
3095 // this machine is awaiting for a spawning session to be opened:
3096 // then the calling process must be the one that got started by
3097 // LaunchVMProcess()
3098
3099 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3100 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3101
3102#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3103 /* Hardened windows builds spawns three processes when a VM is
3104 launched, the 3rd one is the one that will end up here. */
3105 RTPROCESS pidParent;
3106 int vrc = RTProcQueryParent(pid, &pidParent);
3107 if (RT_SUCCESS(vrc))
3108 vrc = RTProcQueryParent(pidParent, &pidParent);
3109 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3110 || vrc == VERR_ACCESS_DENIED)
3111 {
3112 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3113 mData->mSession.mPID = pid;
3114 }
3115#endif
3116
3117 if (mData->mSession.mPID != pid)
3118 return setError(E_ACCESSDENIED,
3119 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3120 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3121 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3122 }
3123
3124 // create the mutable SessionMachine from the current machine
3125 ComObjPtr<SessionMachine> sessionMachine;
3126 sessionMachine.createObject();
3127 rc = sessionMachine->init(this);
3128 AssertComRC(rc);
3129
3130 /* NOTE: doing return from this function after this point but
3131 * before the end is forbidden since it may call SessionMachine::uninit()
3132 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3133 * lock while still holding the Machine lock in alock so that a deadlock
3134 * is possible due to the wrong lock order. */
3135
3136 if (SUCCEEDED(rc))
3137 {
3138 /*
3139 * Set the session state to Spawning to protect against subsequent
3140 * attempts to open a session and to unregister the machine after
3141 * we release the lock.
3142 */
3143 SessionState_T origState = mData->mSession.mState;
3144 mData->mSession.mState = SessionState_Spawning;
3145
3146#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3147 /* Get the client token ID to be passed to the client process */
3148 Utf8Str strTokenId;
3149 sessionMachine->i_getTokenId(strTokenId);
3150 Assert(!strTokenId.isEmpty());
3151#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3152 /* Get the client token to be passed to the client process */
3153 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3154 /* The token is now "owned" by pToken, fix refcount */
3155 if (!pToken.isNull())
3156 pToken->Release();
3157#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3158
3159 /*
3160 * Release the lock before calling the client process -- it will call
3161 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3162 * because the state is Spawning, so that LaunchVMProcess() and
3163 * LockMachine() calls will fail. This method, called before we
3164 * acquire the lock again, will fail because of the wrong PID.
3165 *
3166 * Note that mData->mSession.mRemoteControls accessed outside
3167 * the lock may not be modified when state is Spawning, so it's safe.
3168 */
3169 alock.release();
3170
3171 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3172#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3173 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3174#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3175 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3176 /* Now the token is owned by the client process. */
3177 pToken.setNull();
3178#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3179 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3180
3181 /* The failure may occur w/o any error info (from RPC), so provide one */
3182 if (FAILED(rc))
3183 setError(VBOX_E_VM_ERROR,
3184 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3185
3186 // get session name, either to remember or to compare against
3187 // the already known session name.
3188 {
3189 Bstr bstrSessionName;
3190 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3191 if (SUCCEEDED(rc2))
3192 strSessionName = bstrSessionName;
3193 }
3194
3195 if ( SUCCEEDED(rc)
3196 && fLaunchingVMProcess
3197 )
3198 {
3199 /* complete the remote session initialization */
3200
3201 /* get the console from the direct session */
3202 ComPtr<IConsole> console;
3203 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3204 ComAssertComRC(rc);
3205
3206 if (SUCCEEDED(rc) && !console)
3207 {
3208 ComAssert(!!console);
3209 rc = E_FAIL;
3210 }
3211
3212 /* assign machine & console to the remote session */
3213 if (SUCCEEDED(rc))
3214 {
3215 /*
3216 * after LaunchVMProcess(), the first and the only
3217 * entry in remoteControls is that remote session
3218 */
3219 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3220 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3221 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3222
3223 /* The failure may occur w/o any error info (from RPC), so provide one */
3224 if (FAILED(rc))
3225 setError(VBOX_E_VM_ERROR,
3226 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3227 }
3228
3229 if (FAILED(rc))
3230 pSessionControl->Uninitialize();
3231 }
3232
3233 /* acquire the lock again */
3234 alock.acquire();
3235
3236 /* Restore the session state */
3237 mData->mSession.mState = origState;
3238 }
3239
3240 // finalize spawning anyway (this is why we don't return on errors above)
3241 if (fLaunchingVMProcess)
3242 {
3243 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3244 /* Note that the progress object is finalized later */
3245 /** @todo Consider checking mData->mSession.mProgress for cancellation
3246 * around here. */
3247
3248 /* We don't reset mSession.mPID here because it is necessary for
3249 * SessionMachine::uninit() to reap the child process later. */
3250
3251 if (FAILED(rc))
3252 {
3253 /* Close the remote session, remove the remote control from the list
3254 * and reset session state to Closed (@note keep the code in sync
3255 * with the relevant part in checkForSpawnFailure()). */
3256
3257 Assert(mData->mSession.mRemoteControls.size() == 1);
3258 if (mData->mSession.mRemoteControls.size() == 1)
3259 {
3260 ErrorInfoKeeper eik;
3261 mData->mSession.mRemoteControls.front()->Uninitialize();
3262 }
3263
3264 mData->mSession.mRemoteControls.clear();
3265 mData->mSession.mState = SessionState_Unlocked;
3266 }
3267 }
3268 else
3269 {
3270 /* memorize PID of the directly opened session */
3271 if (SUCCEEDED(rc))
3272 mData->mSession.mPID = pid;
3273 }
3274
3275 if (SUCCEEDED(rc))
3276 {
3277 mData->mSession.mLockType = aLockType;
3278 /* memorize the direct session control and cache IUnknown for it */
3279 mData->mSession.mDirectControl = pSessionControl;
3280 mData->mSession.mState = SessionState_Locked;
3281 if (!fLaunchingVMProcess)
3282 mData->mSession.mName = strSessionName;
3283 /* associate the SessionMachine with this Machine */
3284 mData->mSession.mMachine = sessionMachine;
3285
3286 /* request an IUnknown pointer early from the remote party for later
3287 * identity checks (it will be internally cached within mDirectControl
3288 * at least on XPCOM) */
3289 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3290 NOREF(unk);
3291 }
3292
3293 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3294 * would break the lock order */
3295 alock.release();
3296
3297 /* uninitialize the created session machine on failure */
3298 if (FAILED(rc))
3299 sessionMachine->uninit();
3300 }
3301
3302 if (SUCCEEDED(rc))
3303 {
3304 /*
3305 * tell the client watcher thread to update the set of
3306 * machines that have open sessions
3307 */
3308 mParent->i_updateClientWatcher();
3309
3310 if (oldState != SessionState_Locked)
3311 /* fire an event */
3312 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3313 }
3314
3315 return rc;
3316}
3317
3318/**
3319 * @note Locks objects!
3320 */
3321HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3322 const com::Utf8Str &aName,
3323 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3324 ComPtr<IProgress> &aProgress)
3325{
3326 Utf8Str strFrontend(aName);
3327 /* "emergencystop" doesn't need the session, so skip the checks/interface
3328 * retrieval. This code doesn't quite fit in here, but introducing a
3329 * special API method would be even more effort, and would require explicit
3330 * support by every API client. It's better to hide the feature a bit. */
3331 if (strFrontend != "emergencystop")
3332 CheckComArgNotNull(aSession);
3333
3334 HRESULT rc = S_OK;
3335 if (strFrontend.isEmpty())
3336 {
3337 Bstr bstrFrontend;
3338 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3339 if (FAILED(rc))
3340 return rc;
3341 strFrontend = bstrFrontend;
3342 if (strFrontend.isEmpty())
3343 {
3344 ComPtr<ISystemProperties> systemProperties;
3345 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3346 if (FAILED(rc))
3347 return rc;
3348 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3349 if (FAILED(rc))
3350 return rc;
3351 strFrontend = bstrFrontend;
3352 }
3353 /* paranoia - emergencystop is not a valid default */
3354 if (strFrontend == "emergencystop")
3355 strFrontend = Utf8Str::Empty;
3356 }
3357 /* default frontend: Qt GUI */
3358 if (strFrontend.isEmpty())
3359 strFrontend = "GUI/Qt";
3360
3361 if (strFrontend != "emergencystop")
3362 {
3363 /* check the session state */
3364 SessionState_T state;
3365 rc = aSession->COMGETTER(State)(&state);
3366 if (FAILED(rc))
3367 return rc;
3368
3369 if (state != SessionState_Unlocked)
3370 return setError(VBOX_E_INVALID_OBJECT_STATE,
3371 tr("The given session is busy"));
3372
3373 /* get the IInternalSessionControl interface */
3374 ComPtr<IInternalSessionControl> control(aSession);
3375 ComAssertMsgRet(!control.isNull(),
3376 ("No IInternalSessionControl interface"),
3377 E_INVALIDARG);
3378
3379 /* get the teleporter enable state for the progress object init. */
3380 BOOL fTeleporterEnabled;
3381 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3382 if (FAILED(rc))
3383 return rc;
3384
3385 /* create a progress object */
3386 ComObjPtr<ProgressProxy> progress;
3387 progress.createObject();
3388 rc = progress->init(mParent,
3389 static_cast<IMachine*>(this),
3390 Bstr(tr("Starting VM")).raw(),
3391 TRUE /* aCancelable */,
3392 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3393 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3394 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3395 2 /* uFirstOperationWeight */,
3396 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3397
3398 if (SUCCEEDED(rc))
3399 {
3400 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3401 if (SUCCEEDED(rc))
3402 {
3403 aProgress = progress;
3404
3405 /* signal the client watcher thread */
3406 mParent->i_updateClientWatcher();
3407
3408 /* fire an event */
3409 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3410 }
3411 }
3412 }
3413 else
3414 {
3415 /* no progress object - either instant success or failure */
3416 aProgress = NULL;
3417
3418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3419
3420 if (mData->mSession.mState != SessionState_Locked)
3421 return setError(VBOX_E_INVALID_OBJECT_STATE,
3422 tr("The machine '%s' is not locked by a session"),
3423 mUserData->s.strName.c_str());
3424
3425 /* must have a VM process associated - do not kill normal API clients
3426 * with an open session */
3427 if (!Global::IsOnline(mData->mMachineState))
3428 return setError(VBOX_E_INVALID_OBJECT_STATE,
3429 tr("The machine '%s' does not have a VM process"),
3430 mUserData->s.strName.c_str());
3431
3432 /* forcibly terminate the VM process */
3433 if (mData->mSession.mPID != NIL_RTPROCESS)
3434 RTProcTerminate(mData->mSession.mPID);
3435
3436 /* signal the client watcher thread, as most likely the client has
3437 * been terminated */
3438 mParent->i_updateClientWatcher();
3439 }
3440
3441 return rc;
3442}
3443
3444HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3445{
3446 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3447 return setError(E_INVALIDARG,
3448 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3449 aPosition, SchemaDefs::MaxBootPosition);
3450
3451 if (aDevice == DeviceType_USB)
3452 return setError(E_NOTIMPL,
3453 tr("Booting from USB device is currently not supported"));
3454
3455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3456
3457 HRESULT rc = i_checkStateDependency(MutableStateDep);
3458 if (FAILED(rc)) return rc;
3459
3460 i_setModified(IsModified_MachineData);
3461 mHWData.backup();
3462 mHWData->mBootOrder[aPosition - 1] = aDevice;
3463
3464 return S_OK;
3465}
3466
3467HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3468{
3469 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3470 return setError(E_INVALIDARG,
3471 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3472 aPosition, SchemaDefs::MaxBootPosition);
3473
3474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3475
3476 *aDevice = mHWData->mBootOrder[aPosition - 1];
3477
3478 return S_OK;
3479}
3480
3481HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3482 LONG aControllerPort,
3483 LONG aDevice,
3484 DeviceType_T aType,
3485 const ComPtr<IMedium> &aMedium)
3486{
3487 IMedium *aM = aMedium;
3488 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3489 aName.c_str(), aControllerPort, aDevice, aType, aM));
3490
3491 // request the host lock first, since might be calling Host methods for getting host drives;
3492 // next, protect the media tree all the while we're in here, as well as our member variables
3493 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3494 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3495
3496 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3497 if (FAILED(rc)) return rc;
3498
3499 /// @todo NEWMEDIA implicit machine registration
3500 if (!mData->mRegistered)
3501 return setError(VBOX_E_INVALID_OBJECT_STATE,
3502 tr("Cannot attach storage devices to an unregistered machine"));
3503
3504 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3505
3506 /* Check for an existing controller. */
3507 ComObjPtr<StorageController> ctl;
3508 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3509 if (FAILED(rc)) return rc;
3510
3511 StorageControllerType_T ctrlType;
3512 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3513 if (FAILED(rc))
3514 return setError(E_FAIL,
3515 tr("Could not get type of controller '%s'"),
3516 aName.c_str());
3517
3518 bool fSilent = false;
3519 Utf8Str strReconfig;
3520
3521 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3522 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3523 if ( mData->mMachineState == MachineState_Paused
3524 && strReconfig == "1")
3525 fSilent = true;
3526
3527 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3528 bool fHotplug = false;
3529 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3530 fHotplug = true;
3531
3532 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3533 return setError(VBOX_E_INVALID_VM_STATE,
3534 tr("Controller '%s' does not support hotplugging"),
3535 aName.c_str());
3536
3537 // check that the port and device are not out of range
3538 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3539 if (FAILED(rc)) return rc;
3540
3541 /* check if the device slot is already busy */
3542 MediumAttachment *pAttachTemp;
3543 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3544 aName,
3545 aControllerPort,
3546 aDevice)))
3547 {
3548 Medium *pMedium = pAttachTemp->i_getMedium();
3549 if (pMedium)
3550 {
3551 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3552 return setError(VBOX_E_OBJECT_IN_USE,
3553 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3554 pMedium->i_getLocationFull().c_str(),
3555 aControllerPort,
3556 aDevice,
3557 aName.c_str());
3558 }
3559 else
3560 return setError(VBOX_E_OBJECT_IN_USE,
3561 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3562 aControllerPort, aDevice, aName.c_str());
3563 }
3564
3565 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3566 if (aMedium && medium.isNull())
3567 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3568
3569 AutoCaller mediumCaller(medium);
3570 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3571
3572 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3573
3574 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3575 && !medium.isNull()
3576 && ( medium->i_getType() != MediumType_Readonly
3577 || medium->i_getDeviceType() != DeviceType_DVD)
3578 )
3579 return setError(VBOX_E_OBJECT_IN_USE,
3580 tr("Medium '%s' is already attached to this virtual machine"),
3581 medium->i_getLocationFull().c_str());
3582
3583 if (!medium.isNull())
3584 {
3585 MediumType_T mtype = medium->i_getType();
3586 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3587 // For DVDs it's not written to the config file, so needs no global config
3588 // version bump. For floppies it's a new attribute "type", which is ignored
3589 // by older VirtualBox version, so needs no global config version bump either.
3590 // For hard disks this type is not accepted.
3591 if (mtype == MediumType_MultiAttach)
3592 {
3593 // This type is new with VirtualBox 4.0 and therefore requires settings
3594 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3595 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3596 // two reasons: The medium type is a property of the media registry tree, which
3597 // can reside in the global config file (for pre-4.0 media); we would therefore
3598 // possibly need to bump the global config version. We don't want to do that though
3599 // because that might make downgrading to pre-4.0 impossible.
3600 // As a result, we can only use these two new types if the medium is NOT in the
3601 // global registry:
3602 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3603 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3604 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3605 )
3606 return setError(VBOX_E_INVALID_OBJECT_STATE,
3607 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3608 "to machines that were created with VirtualBox 4.0 or later"),
3609 medium->i_getLocationFull().c_str());
3610 }
3611 }
3612
3613 bool fIndirect = false;
3614 if (!medium.isNull())
3615 fIndirect = medium->i_isReadOnly();
3616 bool associate = true;
3617
3618 do
3619 {
3620 if ( aType == DeviceType_HardDisk
3621 && mMediumAttachments.isBackedUp())
3622 {
3623 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3624
3625 /* check if the medium was attached to the VM before we started
3626 * changing attachments in which case the attachment just needs to
3627 * be restored */
3628 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3629 {
3630 AssertReturn(!fIndirect, E_FAIL);
3631
3632 /* see if it's the same bus/channel/device */
3633 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3634 {
3635 /* the simplest case: restore the whole attachment
3636 * and return, nothing else to do */
3637 mMediumAttachments->push_back(pAttachTemp);
3638
3639 /* Reattach the medium to the VM. */
3640 if (fHotplug || fSilent)
3641 {
3642 mediumLock.release();
3643 treeLock.release();
3644 alock.release();
3645
3646 MediumLockList *pMediumLockList(new MediumLockList());
3647
3648 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3649 medium /* pToLockWrite */,
3650 false /* fMediumLockWriteAll */,
3651 NULL,
3652 *pMediumLockList);
3653 alock.acquire();
3654 if (FAILED(rc))
3655 delete pMediumLockList;
3656 else
3657 {
3658 mData->mSession.mLockedMedia.Unlock();
3659 alock.release();
3660 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3661 mData->mSession.mLockedMedia.Lock();
3662 alock.acquire();
3663 }
3664 alock.release();
3665
3666 if (SUCCEEDED(rc))
3667 {
3668 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3669 /* Remove lock list in case of error. */
3670 if (FAILED(rc))
3671 {
3672 mData->mSession.mLockedMedia.Unlock();
3673 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3674 mData->mSession.mLockedMedia.Lock();
3675 }
3676 }
3677 }
3678
3679 return S_OK;
3680 }
3681
3682 /* bus/channel/device differ; we need a new attachment object,
3683 * but don't try to associate it again */
3684 associate = false;
3685 break;
3686 }
3687 }
3688
3689 /* go further only if the attachment is to be indirect */
3690 if (!fIndirect)
3691 break;
3692
3693 /* perform the so called smart attachment logic for indirect
3694 * attachments. Note that smart attachment is only applicable to base
3695 * hard disks. */
3696
3697 if (medium->i_getParent().isNull())
3698 {
3699 /* first, investigate the backup copy of the current hard disk
3700 * attachments to make it possible to re-attach existing diffs to
3701 * another device slot w/o losing their contents */
3702 if (mMediumAttachments.isBackedUp())
3703 {
3704 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3705
3706 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3707 uint32_t foundLevel = 0;
3708
3709 for (MediumAttachmentList::const_iterator
3710 it = oldAtts.begin();
3711 it != oldAtts.end();
3712 ++it)
3713 {
3714 uint32_t level = 0;
3715 MediumAttachment *pAttach = *it;
3716 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3717 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3718 if (pMedium.isNull())
3719 continue;
3720
3721 if (pMedium->i_getBase(&level) == medium)
3722 {
3723 /* skip the hard disk if its currently attached (we
3724 * cannot attach the same hard disk twice) */
3725 if (i_findAttachment(*mMediumAttachments.data(),
3726 pMedium))
3727 continue;
3728
3729 /* matched device, channel and bus (i.e. attached to the
3730 * same place) will win and immediately stop the search;
3731 * otherwise the attachment that has the youngest
3732 * descendant of medium will be used
3733 */
3734 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3735 {
3736 /* the simplest case: restore the whole attachment
3737 * and return, nothing else to do */
3738 mMediumAttachments->push_back(*it);
3739
3740 /* Reattach the medium to the VM. */
3741 if (fHotplug || fSilent)
3742 {
3743 mediumLock.release();
3744 treeLock.release();
3745 alock.release();
3746
3747 MediumLockList *pMediumLockList(new MediumLockList());
3748
3749 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3750 medium /* pToLockWrite */,
3751 false /* fMediumLockWriteAll */,
3752 NULL,
3753 *pMediumLockList);
3754 alock.acquire();
3755 if (FAILED(rc))
3756 delete pMediumLockList;
3757 else
3758 {
3759 mData->mSession.mLockedMedia.Unlock();
3760 alock.release();
3761 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3762 mData->mSession.mLockedMedia.Lock();
3763 alock.acquire();
3764 }
3765 alock.release();
3766
3767 if (SUCCEEDED(rc))
3768 {
3769 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3770 /* Remove lock list in case of error. */
3771 if (FAILED(rc))
3772 {
3773 mData->mSession.mLockedMedia.Unlock();
3774 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3775 mData->mSession.mLockedMedia.Lock();
3776 }
3777 }
3778 }
3779
3780 return S_OK;
3781 }
3782 else if ( foundIt == oldAtts.end()
3783 || level > foundLevel /* prefer younger */
3784 )
3785 {
3786 foundIt = it;
3787 foundLevel = level;
3788 }
3789 }
3790 }
3791
3792 if (foundIt != oldAtts.end())
3793 {
3794 /* use the previously attached hard disk */
3795 medium = (*foundIt)->i_getMedium();
3796 mediumCaller.attach(medium);
3797 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3798 mediumLock.attach(medium);
3799 /* not implicit, doesn't require association with this VM */
3800 fIndirect = false;
3801 associate = false;
3802 /* go right to the MediumAttachment creation */
3803 break;
3804 }
3805 }
3806
3807 /* must give up the medium lock and medium tree lock as below we
3808 * go over snapshots, which needs a lock with higher lock order. */
3809 mediumLock.release();
3810 treeLock.release();
3811
3812 /* then, search through snapshots for the best diff in the given
3813 * hard disk's chain to base the new diff on */
3814
3815 ComObjPtr<Medium> base;
3816 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3817 while (snap)
3818 {
3819 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3820
3821 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3822
3823 MediumAttachment *pAttachFound = NULL;
3824 uint32_t foundLevel = 0;
3825
3826 for (MediumAttachmentList::const_iterator
3827 it = snapAtts.begin();
3828 it != snapAtts.end();
3829 ++it)
3830 {
3831 MediumAttachment *pAttach = *it;
3832 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3833 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3834 if (pMedium.isNull())
3835 continue;
3836
3837 uint32_t level = 0;
3838 if (pMedium->i_getBase(&level) == medium)
3839 {
3840 /* matched device, channel and bus (i.e. attached to the
3841 * same place) will win and immediately stop the search;
3842 * otherwise the attachment that has the youngest
3843 * descendant of medium will be used
3844 */
3845 if ( pAttach->i_getDevice() == aDevice
3846 && pAttach->i_getPort() == aControllerPort
3847 && pAttach->i_getControllerName() == aName
3848 )
3849 {
3850 pAttachFound = pAttach;
3851 break;
3852 }
3853 else if ( !pAttachFound
3854 || level > foundLevel /* prefer younger */
3855 )
3856 {
3857 pAttachFound = pAttach;
3858 foundLevel = level;
3859 }
3860 }
3861 }
3862
3863 if (pAttachFound)
3864 {
3865 base = pAttachFound->i_getMedium();
3866 break;
3867 }
3868
3869 snap = snap->i_getParent();
3870 }
3871
3872 /* re-lock medium tree and the medium, as we need it below */
3873 treeLock.acquire();
3874 mediumLock.acquire();
3875
3876 /* found a suitable diff, use it as a base */
3877 if (!base.isNull())
3878 {
3879 medium = base;
3880 mediumCaller.attach(medium);
3881 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3882 mediumLock.attach(medium);
3883 }
3884 }
3885
3886 Utf8Str strFullSnapshotFolder;
3887 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3888
3889 ComObjPtr<Medium> diff;
3890 diff.createObject();
3891 // store this diff in the same registry as the parent
3892 Guid uuidRegistryParent;
3893 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3894 {
3895 // parent image has no registry: this can happen if we're attaching a new immutable
3896 // image that has not yet been attached (medium then points to the base and we're
3897 // creating the diff image for the immutable, and the parent is not yet registered);
3898 // put the parent in the machine registry then
3899 mediumLock.release();
3900 treeLock.release();
3901 alock.release();
3902 i_addMediumToRegistry(medium);
3903 alock.acquire();
3904 treeLock.acquire();
3905 mediumLock.acquire();
3906 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3907 }
3908 rc = diff->init(mParent,
3909 medium->i_getPreferredDiffFormat(),
3910 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3911 uuidRegistryParent,
3912 DeviceType_HardDisk);
3913 if (FAILED(rc)) return rc;
3914
3915 /* Apply the normal locking logic to the entire chain. */
3916 MediumLockList *pMediumLockList(new MediumLockList());
3917 mediumLock.release();
3918 treeLock.release();
3919 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3920 diff /* pToLockWrite */,
3921 false /* fMediumLockWriteAll */,
3922 medium,
3923 *pMediumLockList);
3924 treeLock.acquire();
3925 mediumLock.acquire();
3926 if (SUCCEEDED(rc))
3927 {
3928 mediumLock.release();
3929 treeLock.release();
3930 rc = pMediumLockList->Lock();
3931 treeLock.acquire();
3932 mediumLock.acquire();
3933 if (FAILED(rc))
3934 setError(rc,
3935 tr("Could not lock medium when creating diff '%s'"),
3936 diff->i_getLocationFull().c_str());
3937 else
3938 {
3939 /* will release the lock before the potentially lengthy
3940 * operation, so protect with the special state */
3941 MachineState_T oldState = mData->mMachineState;
3942 i_setMachineState(MachineState_SettingUp);
3943
3944 mediumLock.release();
3945 treeLock.release();
3946 alock.release();
3947
3948 rc = medium->i_createDiffStorage(diff,
3949 medium->i_getPreferredDiffVariant(),
3950 pMediumLockList,
3951 NULL /* aProgress */,
3952 true /* aWait */,
3953 false /* aNotify */);
3954
3955 alock.acquire();
3956 treeLock.acquire();
3957 mediumLock.acquire();
3958
3959 i_setMachineState(oldState);
3960 }
3961 }
3962
3963 /* Unlock the media and free the associated memory. */
3964 delete pMediumLockList;
3965
3966 if (FAILED(rc)) return rc;
3967
3968 /* use the created diff for the actual attachment */
3969 medium = diff;
3970 mediumCaller.attach(medium);
3971 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3972 mediumLock.attach(medium);
3973 }
3974 while (0);
3975
3976 ComObjPtr<MediumAttachment> attachment;
3977 attachment.createObject();
3978 rc = attachment->init(this,
3979 medium,
3980 aName,
3981 aControllerPort,
3982 aDevice,
3983 aType,
3984 fIndirect,
3985 false /* fPassthrough */,
3986 false /* fTempEject */,
3987 false /* fNonRotational */,
3988 false /* fDiscard */,
3989 fHotplug /* fHotPluggable */,
3990 Utf8Str::Empty);
3991 if (FAILED(rc)) return rc;
3992
3993 if (associate && !medium.isNull())
3994 {
3995 // as the last step, associate the medium to the VM
3996 rc = medium->i_addBackReference(mData->mUuid);
3997 // here we can fail because of Deleting, or being in process of creating a Diff
3998 if (FAILED(rc)) return rc;
3999
4000 mediumLock.release();
4001 treeLock.release();
4002 alock.release();
4003 i_addMediumToRegistry(medium);
4004 alock.acquire();
4005 treeLock.acquire();
4006 mediumLock.acquire();
4007 }
4008
4009 /* success: finally remember the attachment */
4010 i_setModified(IsModified_Storage);
4011 mMediumAttachments.backup();
4012 mMediumAttachments->push_back(attachment);
4013
4014 mediumLock.release();
4015 treeLock.release();
4016 alock.release();
4017
4018 if (fHotplug || fSilent)
4019 {
4020 if (!medium.isNull())
4021 {
4022 MediumLockList *pMediumLockList(new MediumLockList());
4023
4024 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4025 medium /* pToLockWrite */,
4026 false /* fMediumLockWriteAll */,
4027 NULL,
4028 *pMediumLockList);
4029 alock.acquire();
4030 if (FAILED(rc))
4031 delete pMediumLockList;
4032 else
4033 {
4034 mData->mSession.mLockedMedia.Unlock();
4035 alock.release();
4036 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4037 mData->mSession.mLockedMedia.Lock();
4038 alock.acquire();
4039 }
4040 alock.release();
4041 }
4042
4043 if (SUCCEEDED(rc))
4044 {
4045 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4046 /* Remove lock list in case of error. */
4047 if (FAILED(rc))
4048 {
4049 mData->mSession.mLockedMedia.Unlock();
4050 mData->mSession.mLockedMedia.Remove(attachment);
4051 mData->mSession.mLockedMedia.Lock();
4052 }
4053 }
4054 }
4055
4056 /* Save modified registries, but skip this machine as it's the caller's
4057 * job to save its settings like all other settings changes. */
4058 mParent->i_unmarkRegistryModified(i_getId());
4059 mParent->i_saveModifiedRegistries();
4060
4061 if (SUCCEEDED(rc))
4062 {
4063 if (fIndirect && medium != aM)
4064 mParent->i_onMediumConfigChanged(medium);
4065 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4066 }
4067
4068 return rc;
4069}
4070
4071HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4072 LONG aDevice)
4073{
4074 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4075 aName.c_str(), aControllerPort, aDevice));
4076
4077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4078
4079 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4080 if (FAILED(rc)) return rc;
4081
4082 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4083
4084 /* Check for an existing controller. */
4085 ComObjPtr<StorageController> ctl;
4086 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4087 if (FAILED(rc)) return rc;
4088
4089 StorageControllerType_T ctrlType;
4090 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4091 if (FAILED(rc))
4092 return setError(E_FAIL,
4093 tr("Could not get type of controller '%s'"),
4094 aName.c_str());
4095
4096 bool fSilent = false;
4097 Utf8Str strReconfig;
4098
4099 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4100 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4101 if ( mData->mMachineState == MachineState_Paused
4102 && strReconfig == "1")
4103 fSilent = true;
4104
4105 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4106 bool fHotplug = false;
4107 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4108 fHotplug = true;
4109
4110 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4111 return setError(VBOX_E_INVALID_VM_STATE,
4112 tr("Controller '%s' does not support hotplugging"),
4113 aName.c_str());
4114
4115 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4116 aName,
4117 aControllerPort,
4118 aDevice);
4119 if (!pAttach)
4120 return setError(VBOX_E_OBJECT_NOT_FOUND,
4121 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4122 aDevice, aControllerPort, aName.c_str());
4123
4124 if (fHotplug && !pAttach->i_getHotPluggable())
4125 return setError(VBOX_E_NOT_SUPPORTED,
4126 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4127 aDevice, aControllerPort, aName.c_str());
4128
4129 /*
4130 * The VM has to detach the device before we delete any implicit diffs.
4131 * If this fails we can roll back without loosing data.
4132 */
4133 if (fHotplug || fSilent)
4134 {
4135 alock.release();
4136 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4137 alock.acquire();
4138 }
4139 if (FAILED(rc)) return rc;
4140
4141 /* If we are here everything went well and we can delete the implicit now. */
4142 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4143
4144 alock.release();
4145
4146 /* Save modified registries, but skip this machine as it's the caller's
4147 * job to save its settings like all other settings changes. */
4148 mParent->i_unmarkRegistryModified(i_getId());
4149 mParent->i_saveModifiedRegistries();
4150
4151 if (SUCCEEDED(rc))
4152 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4153
4154 return rc;
4155}
4156
4157HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4158 LONG aDevice, BOOL aPassthrough)
4159{
4160 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4161 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4162
4163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4164
4165 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4166 if (FAILED(rc)) return rc;
4167
4168 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4169
4170 /* Check for an existing controller. */
4171 ComObjPtr<StorageController> ctl;
4172 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4173 if (FAILED(rc)) return rc;
4174
4175 StorageControllerType_T ctrlType;
4176 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4177 if (FAILED(rc))
4178 return setError(E_FAIL,
4179 tr("Could not get type of controller '%s'"),
4180 aName.c_str());
4181
4182 bool fSilent = false;
4183 Utf8Str strReconfig;
4184
4185 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4186 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4187 if ( mData->mMachineState == MachineState_Paused
4188 && strReconfig == "1")
4189 fSilent = true;
4190
4191 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4192 bool fHotplug = false;
4193 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4194 fHotplug = true;
4195
4196 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4197 return setError(VBOX_E_INVALID_VM_STATE,
4198 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4199 aName.c_str());
4200
4201 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4202 aName,
4203 aControllerPort,
4204 aDevice);
4205 if (!pAttach)
4206 return setError(VBOX_E_OBJECT_NOT_FOUND,
4207 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4208 aDevice, aControllerPort, aName.c_str());
4209
4210
4211 i_setModified(IsModified_Storage);
4212 mMediumAttachments.backup();
4213
4214 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4215
4216 if (pAttach->i_getType() != DeviceType_DVD)
4217 return setError(E_INVALIDARG,
4218 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4219 aDevice, aControllerPort, aName.c_str());
4220
4221 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4222
4223 pAttach->i_updatePassthrough(!!aPassthrough);
4224
4225 attLock.release();
4226 alock.release();
4227 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4228 if (SUCCEEDED(rc) && fValueChanged)
4229 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4230
4231 return rc;
4232}
4233
4234HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4235 LONG aDevice, BOOL aTemporaryEject)
4236{
4237
4238 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4239 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4240
4241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4242
4243 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4244 if (FAILED(rc)) return rc;
4245
4246 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4247 aName,
4248 aControllerPort,
4249 aDevice);
4250 if (!pAttach)
4251 return setError(VBOX_E_OBJECT_NOT_FOUND,
4252 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4253 aDevice, aControllerPort, aName.c_str());
4254
4255
4256 i_setModified(IsModified_Storage);
4257 mMediumAttachments.backup();
4258
4259 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4260
4261 if (pAttach->i_getType() != DeviceType_DVD)
4262 return setError(E_INVALIDARG,
4263 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4264 aDevice, aControllerPort, aName.c_str());
4265 pAttach->i_updateTempEject(!!aTemporaryEject);
4266
4267 return S_OK;
4268}
4269
4270HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4271 LONG aDevice, BOOL aNonRotational)
4272{
4273
4274 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4275 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4276
4277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4278
4279 HRESULT rc = i_checkStateDependency(MutableStateDep);
4280 if (FAILED(rc)) return rc;
4281
4282 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4283
4284 if (Global::IsOnlineOrTransient(mData->mMachineState))
4285 return setError(VBOX_E_INVALID_VM_STATE,
4286 tr("Invalid machine state: %s"),
4287 Global::stringifyMachineState(mData->mMachineState));
4288
4289 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4290 aName,
4291 aControllerPort,
4292 aDevice);
4293 if (!pAttach)
4294 return setError(VBOX_E_OBJECT_NOT_FOUND,
4295 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4296 aDevice, aControllerPort, aName.c_str());
4297
4298
4299 i_setModified(IsModified_Storage);
4300 mMediumAttachments.backup();
4301
4302 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4303
4304 if (pAttach->i_getType() != DeviceType_HardDisk)
4305 return setError(E_INVALIDARG,
4306 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"),
4307 aDevice, aControllerPort, aName.c_str());
4308 pAttach->i_updateNonRotational(!!aNonRotational);
4309
4310 return S_OK;
4311}
4312
4313HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4314 LONG aDevice, BOOL aDiscard)
4315{
4316
4317 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4318 aName.c_str(), aControllerPort, aDevice, aDiscard));
4319
4320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4321
4322 HRESULT rc = i_checkStateDependency(MutableStateDep);
4323 if (FAILED(rc)) return rc;
4324
4325 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4326
4327 if (Global::IsOnlineOrTransient(mData->mMachineState))
4328 return setError(VBOX_E_INVALID_VM_STATE,
4329 tr("Invalid machine state: %s"),
4330 Global::stringifyMachineState(mData->mMachineState));
4331
4332 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4333 aName,
4334 aControllerPort,
4335 aDevice);
4336 if (!pAttach)
4337 return setError(VBOX_E_OBJECT_NOT_FOUND,
4338 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4339 aDevice, aControllerPort, aName.c_str());
4340
4341
4342 i_setModified(IsModified_Storage);
4343 mMediumAttachments.backup();
4344
4345 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4346
4347 if (pAttach->i_getType() != DeviceType_HardDisk)
4348 return setError(E_INVALIDARG,
4349 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"),
4350 aDevice, aControllerPort, aName.c_str());
4351 pAttach->i_updateDiscard(!!aDiscard);
4352
4353 return S_OK;
4354}
4355
4356HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4357 LONG aDevice, BOOL aHotPluggable)
4358{
4359 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4360 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4361
4362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4363
4364 HRESULT rc = i_checkStateDependency(MutableStateDep);
4365 if (FAILED(rc)) return rc;
4366
4367 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4368
4369 if (Global::IsOnlineOrTransient(mData->mMachineState))
4370 return setError(VBOX_E_INVALID_VM_STATE,
4371 tr("Invalid machine state: %s"),
4372 Global::stringifyMachineState(mData->mMachineState));
4373
4374 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4375 aName,
4376 aControllerPort,
4377 aDevice);
4378 if (!pAttach)
4379 return setError(VBOX_E_OBJECT_NOT_FOUND,
4380 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4381 aDevice, aControllerPort, aName.c_str());
4382
4383 /* Check for an existing controller. */
4384 ComObjPtr<StorageController> ctl;
4385 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4386 if (FAILED(rc)) return rc;
4387
4388 StorageControllerType_T ctrlType;
4389 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4390 if (FAILED(rc))
4391 return setError(E_FAIL,
4392 tr("Could not get type of controller '%s'"),
4393 aName.c_str());
4394
4395 if (!i_isControllerHotplugCapable(ctrlType))
4396 return setError(VBOX_E_NOT_SUPPORTED,
4397 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4398 aName.c_str());
4399
4400 i_setModified(IsModified_Storage);
4401 mMediumAttachments.backup();
4402
4403 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4404
4405 if (pAttach->i_getType() == DeviceType_Floppy)
4406 return setError(E_INVALIDARG,
4407 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"),
4408 aDevice, aControllerPort, aName.c_str());
4409 pAttach->i_updateHotPluggable(!!aHotPluggable);
4410
4411 return S_OK;
4412}
4413
4414HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4415 LONG aDevice)
4416{
4417 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4418 aName.c_str(), aControllerPort, aDevice));
4419
4420 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4421}
4422
4423HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4424 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4425{
4426 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4427 aName.c_str(), aControllerPort, aDevice));
4428
4429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4430
4431 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4432 if (FAILED(rc)) return rc;
4433
4434 if (Global::IsOnlineOrTransient(mData->mMachineState))
4435 return setError(VBOX_E_INVALID_VM_STATE,
4436 tr("Invalid machine state: %s"),
4437 Global::stringifyMachineState(mData->mMachineState));
4438
4439 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4440 aName,
4441 aControllerPort,
4442 aDevice);
4443 if (!pAttach)
4444 return setError(VBOX_E_OBJECT_NOT_FOUND,
4445 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4446 aDevice, aControllerPort, aName.c_str());
4447
4448
4449 i_setModified(IsModified_Storage);
4450 mMediumAttachments.backup();
4451
4452 IBandwidthGroup *iB = aBandwidthGroup;
4453 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4454 if (aBandwidthGroup && group.isNull())
4455 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4456
4457 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4458
4459 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4460 if (strBandwidthGroupOld.isNotEmpty())
4461 {
4462 /* Get the bandwidth group object and release it - this must not fail. */
4463 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4464 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4465 Assert(SUCCEEDED(rc));
4466
4467 pBandwidthGroupOld->i_release();
4468 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4469 }
4470
4471 if (!group.isNull())
4472 {
4473 group->i_reference();
4474 pAttach->i_updateBandwidthGroup(group->i_getName());
4475 }
4476
4477 return S_OK;
4478}
4479
4480HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4481 LONG aControllerPort,
4482 LONG aDevice,
4483 DeviceType_T aType)
4484{
4485 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4486 aName.c_str(), aControllerPort, aDevice, aType));
4487
4488 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4489}
4490
4491
4492HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4493 LONG aControllerPort,
4494 LONG aDevice,
4495 BOOL aForce)
4496{
4497 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4498 aName.c_str(), aControllerPort, aForce));
4499
4500 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4501}
4502
4503HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4504 LONG aControllerPort,
4505 LONG aDevice,
4506 const ComPtr<IMedium> &aMedium,
4507 BOOL aForce)
4508{
4509 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4510 aName.c_str(), aControllerPort, aDevice, aForce));
4511
4512 // request the host lock first, since might be calling Host methods for getting host drives;
4513 // next, protect the media tree all the while we're in here, as well as our member variables
4514 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4515 this->lockHandle(),
4516 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4517
4518 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4519 aName,
4520 aControllerPort,
4521 aDevice);
4522 if (pAttach.isNull())
4523 return setError(VBOX_E_OBJECT_NOT_FOUND,
4524 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4525 aDevice, aControllerPort, aName.c_str());
4526
4527 /* Remember previously mounted medium. The medium before taking the
4528 * backup is not necessarily the same thing. */
4529 ComObjPtr<Medium> oldmedium;
4530 oldmedium = pAttach->i_getMedium();
4531
4532 IMedium *iM = aMedium;
4533 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4534 if (aMedium && pMedium.isNull())
4535 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4536
4537 AutoCaller mediumCaller(pMedium);
4538 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4539
4540 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4541 if (pMedium)
4542 {
4543 DeviceType_T mediumType = pAttach->i_getType();
4544 switch (mediumType)
4545 {
4546 case DeviceType_DVD:
4547 case DeviceType_Floppy:
4548 break;
4549
4550 default:
4551 return setError(VBOX_E_INVALID_OBJECT_STATE,
4552 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4553 aControllerPort,
4554 aDevice,
4555 aName.c_str());
4556 }
4557 }
4558
4559 i_setModified(IsModified_Storage);
4560 mMediumAttachments.backup();
4561
4562 {
4563 // The backup operation makes the pAttach reference point to the
4564 // old settings. Re-get the correct reference.
4565 pAttach = i_findAttachment(*mMediumAttachments.data(),
4566 aName,
4567 aControllerPort,
4568 aDevice);
4569 if (!oldmedium.isNull())
4570 oldmedium->i_removeBackReference(mData->mUuid);
4571 if (!pMedium.isNull())
4572 {
4573 pMedium->i_addBackReference(mData->mUuid);
4574
4575 mediumLock.release();
4576 multiLock.release();
4577 i_addMediumToRegistry(pMedium);
4578 multiLock.acquire();
4579 mediumLock.acquire();
4580 }
4581
4582 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4583 pAttach->i_updateMedium(pMedium);
4584 }
4585
4586 i_setModified(IsModified_Storage);
4587
4588 mediumLock.release();
4589 multiLock.release();
4590 HRESULT rc = i_onMediumChange(pAttach, aForce);
4591 multiLock.acquire();
4592 mediumLock.acquire();
4593
4594 /* On error roll back this change only. */
4595 if (FAILED(rc))
4596 {
4597 if (!pMedium.isNull())
4598 pMedium->i_removeBackReference(mData->mUuid);
4599 pAttach = i_findAttachment(*mMediumAttachments.data(),
4600 aName,
4601 aControllerPort,
4602 aDevice);
4603 /* If the attachment is gone in the meantime, bail out. */
4604 if (pAttach.isNull())
4605 return rc;
4606 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4607 if (!oldmedium.isNull())
4608 oldmedium->i_addBackReference(mData->mUuid);
4609 pAttach->i_updateMedium(oldmedium);
4610 }
4611
4612 mediumLock.release();
4613 multiLock.release();
4614
4615 /* Save modified registries, but skip this machine as it's the caller's
4616 * job to save its settings like all other settings changes. */
4617 mParent->i_unmarkRegistryModified(i_getId());
4618 mParent->i_saveModifiedRegistries();
4619
4620 return rc;
4621}
4622HRESULT Machine::getMedium(const com::Utf8Str &aName,
4623 LONG aControllerPort,
4624 LONG aDevice,
4625 ComPtr<IMedium> &aMedium)
4626{
4627 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4628 aName.c_str(), aControllerPort, aDevice));
4629
4630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4631
4632 aMedium = NULL;
4633
4634 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4635 aName,
4636 aControllerPort,
4637 aDevice);
4638 if (pAttach.isNull())
4639 return setError(VBOX_E_OBJECT_NOT_FOUND,
4640 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4641 aDevice, aControllerPort, aName.c_str());
4642
4643 aMedium = pAttach->i_getMedium();
4644
4645 return S_OK;
4646}
4647
4648HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4649{
4650 if (aSlot < RT_ELEMENTS(mSerialPorts))
4651 {
4652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4653 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4654 return S_OK;
4655 }
4656 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4657}
4658
4659HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4660{
4661 if (aSlot < RT_ELEMENTS(mParallelPorts))
4662 {
4663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4664 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4665 return S_OK;
4666 }
4667 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4668}
4669
4670
4671HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4672{
4673 /* Do not assert if slot is out of range, just return the advertised
4674 status. testdriver/vbox.py triggers this in logVmInfo. */
4675 if (aSlot >= mNetworkAdapters.size())
4676 return setError(E_INVALIDARG,
4677 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4678 aSlot, mNetworkAdapters.size());
4679
4680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4683
4684 return S_OK;
4685}
4686
4687HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4688{
4689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4690
4691 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4692 size_t i = 0;
4693 for (settings::StringsMap::const_iterator
4694 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4695 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4696 ++it, ++i)
4697 aKeys[i] = it->first;
4698
4699 return S_OK;
4700}
4701
4702 /**
4703 * @note Locks this object for reading.
4704 */
4705HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4706 com::Utf8Str &aValue)
4707{
4708 /* start with nothing found */
4709 aValue = "";
4710
4711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4712
4713 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4714 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4715 // found:
4716 aValue = it->second; // source is a Utf8Str
4717
4718 /* return the result to caller (may be empty) */
4719 return S_OK;
4720}
4721
4722 /**
4723 * @note Locks mParent for writing + this object for writing.
4724 */
4725HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4726{
4727 /* Because control characters in aKey have caused problems in the settings
4728 * they are rejected unless the key should be deleted. */
4729 if (!aValue.isEmpty())
4730 {
4731 for (size_t i = 0; i < aKey.length(); ++i)
4732 {
4733 char ch = aKey[i];
4734 if (RTLocCIsCntrl(ch))
4735 return E_INVALIDARG;
4736 }
4737 }
4738
4739 Utf8Str strOldValue; // empty
4740
4741 // locking note: we only hold the read lock briefly to look up the old value,
4742 // then release it and call the onExtraCanChange callbacks. There is a small
4743 // chance of a race insofar as the callback might be called twice if two callers
4744 // change the same key at the same time, but that's a much better solution
4745 // than the deadlock we had here before. The actual changing of the extradata
4746 // is then performed under the write lock and race-free.
4747
4748 // look up the old value first; if nothing has changed then we need not do anything
4749 {
4750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4751
4752 // For snapshots don't even think about allowing changes, extradata
4753 // is global for a machine, so there is nothing snapshot specific.
4754 if (i_isSnapshotMachine())
4755 return setError(VBOX_E_INVALID_VM_STATE,
4756 tr("Cannot set extradata for a snapshot"));
4757
4758 // check if the right IMachine instance is used
4759 if (mData->mRegistered && !i_isSessionMachine())
4760 return setError(VBOX_E_INVALID_VM_STATE,
4761 tr("Cannot set extradata for an immutable machine"));
4762
4763 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4764 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4765 strOldValue = it->second;
4766 }
4767
4768 bool fChanged;
4769 if ((fChanged = (strOldValue != aValue)))
4770 {
4771 // ask for permission from all listeners outside the locks;
4772 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4773 // lock to copy the list of callbacks to invoke
4774 Bstr bstrError;
4775 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4776 {
4777 const char *sep = bstrError.isEmpty() ? "" : ": ";
4778 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4779 return setError(E_ACCESSDENIED,
4780 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4781 aKey.c_str(),
4782 aValue.c_str(),
4783 sep,
4784 bstrError.raw());
4785 }
4786
4787 // data is changing and change not vetoed: then write it out under the lock
4788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4789
4790 if (aValue.isEmpty())
4791 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4792 else
4793 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4794 // creates a new key if needed
4795
4796 bool fNeedsGlobalSaveSettings = false;
4797 // This saving of settings is tricky: there is no "old state" for the
4798 // extradata items at all (unlike all other settings), so the old/new
4799 // settings comparison would give a wrong result!
4800 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4801
4802 if (fNeedsGlobalSaveSettings)
4803 {
4804 // save the global settings; for that we should hold only the VirtualBox lock
4805 alock.release();
4806 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4807 mParent->i_saveSettings();
4808 }
4809 }
4810
4811 // fire notification outside the lock
4812 if (fChanged)
4813 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4814
4815 return S_OK;
4816}
4817
4818HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4819{
4820 aProgress = NULL;
4821 NOREF(aSettingsFilePath);
4822 ReturnComNotImplemented();
4823}
4824
4825HRESULT Machine::saveSettings()
4826{
4827 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4828
4829 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4830 if (FAILED(rc)) return rc;
4831
4832 /* the settings file path may never be null */
4833 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4834
4835 /* save all VM data excluding snapshots */
4836 bool fNeedsGlobalSaveSettings = false;
4837 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4838 mlock.release();
4839
4840 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4841 {
4842 // save the global settings; for that we should hold only the VirtualBox lock
4843 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4844 rc = mParent->i_saveSettings();
4845 }
4846
4847 return rc;
4848}
4849
4850
4851HRESULT Machine::discardSettings()
4852{
4853 /*
4854 * We need to take the machine list lock here as well as the machine one
4855 * or we'll get into trouble should any media stuff require rolling back.
4856 *
4857 * Details:
4858 *
4859 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4861 * 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]
4862 * 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
4863 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4864 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4865 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4866 * 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
4867 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4869 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4870 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4872 * 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]
4873 * 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] (*)
4874 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4875 * 0:005> k
4876 * # Child-SP RetAddr Call Site
4877 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4878 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4879 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4880 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4881 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4882 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4883 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4884 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4885 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4886 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4887 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4888 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4889 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4890 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4891 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4892 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4893 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4894 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4895 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4896 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4897 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4898 *
4899 */
4900 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4902
4903 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4904 if (FAILED(rc)) return rc;
4905
4906 /*
4907 * during this rollback, the session will be notified if data has
4908 * been actually changed
4909 */
4910 i_rollback(true /* aNotify */);
4911
4912 return S_OK;
4913}
4914
4915/** @note Locks objects! */
4916HRESULT Machine::unregister(AutoCaller &autoCaller,
4917 CleanupMode_T aCleanupMode,
4918 std::vector<ComPtr<IMedium> > &aMedia)
4919{
4920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4921
4922 Guid id(i_getId());
4923
4924 if (mData->mSession.mState != SessionState_Unlocked)
4925 return setError(VBOX_E_INVALID_OBJECT_STATE,
4926 tr("Cannot unregister the machine '%s' while it is locked"),
4927 mUserData->s.strName.c_str());
4928
4929 // wait for state dependents to drop to zero
4930 i_ensureNoStateDependencies(alock);
4931
4932 if (!mData->mAccessible)
4933 {
4934 // inaccessible machines can only be unregistered; uninitialize ourselves
4935 // here because currently there may be no unregistered that are inaccessible
4936 // (this state combination is not supported). Note releasing the caller and
4937 // leaving the lock before calling uninit()
4938 alock.release();
4939 autoCaller.release();
4940
4941 uninit();
4942
4943 mParent->i_unregisterMachine(this, id);
4944 // calls VirtualBox::i_saveSettings()
4945
4946 return S_OK;
4947 }
4948
4949 HRESULT rc = S_OK;
4950 mData->llFilesToDelete.clear();
4951
4952 if (!mSSData->strStateFilePath.isEmpty())
4953 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4954
4955 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4956 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4957 mData->llFilesToDelete.push_back(strNVRAMFile);
4958
4959 // This list collects the medium objects from all medium attachments
4960 // which we will detach from the machine and its snapshots, in a specific
4961 // order which allows for closing all media without getting "media in use"
4962 // errors, simply by going through the list from the front to the back:
4963 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4964 // and must be closed before the parent media from the snapshots, or closing the parents
4965 // will fail because they still have children);
4966 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4967 // the root ("first") snapshot of the machine.
4968 MediaList llMedia;
4969
4970 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4971 && mMediumAttachments->size()
4972 )
4973 {
4974 // we have media attachments: detach them all and add the Medium objects to our list
4975 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4976 }
4977
4978 if (mData->mFirstSnapshot)
4979 {
4980 // add the media from the medium attachments of the snapshots to llMedia
4981 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4982 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4983 // into the children first
4984
4985 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4986 MachineState_T oldState = mData->mMachineState;
4987 mData->mMachineState = MachineState_DeletingSnapshot;
4988
4989 // make a copy of the first snapshot reference so the refcount does not
4990 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4991 // (would hang due to the AutoCaller voodoo)
4992 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4993
4994 // GO!
4995 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4996
4997 mData->mMachineState = oldState;
4998 }
4999
5000 if (FAILED(rc))
5001 {
5002 i_rollbackMedia();
5003 return rc;
5004 }
5005
5006 // commit all the media changes made above
5007 i_commitMedia();
5008
5009 mData->mRegistered = false;
5010
5011 // machine lock no longer needed
5012 alock.release();
5013
5014 /* Make sure that the settings of the current VM are not saved, because
5015 * they are rather crippled at this point to meet the cleanup expectations
5016 * and there's no point destroying the VM config on disk just because. */
5017 mParent->i_unmarkRegistryModified(id);
5018
5019 // return media to caller
5020 aMedia.resize(llMedia.size());
5021 size_t i = 0;
5022 for (MediaList::const_iterator
5023 it = llMedia.begin();
5024 it != llMedia.end();
5025 ++it, ++i)
5026 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5027
5028 mParent->i_unregisterMachine(this, id);
5029 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5030
5031 return S_OK;
5032}
5033
5034/**
5035 * Task record for deleting a machine config.
5036 */
5037class Machine::DeleteConfigTask
5038 : public Machine::Task
5039{
5040public:
5041 DeleteConfigTask(Machine *m,
5042 Progress *p,
5043 const Utf8Str &t,
5044 const RTCList<ComPtr<IMedium> > &llMediums,
5045 const StringsList &llFilesToDelete)
5046 : Task(m, p, t),
5047 m_llMediums(llMediums),
5048 m_llFilesToDelete(llFilesToDelete)
5049 {}
5050
5051private:
5052 void handler()
5053 {
5054 try
5055 {
5056 m_pMachine->i_deleteConfigHandler(*this);
5057 }
5058 catch (...)
5059 {
5060 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5061 }
5062 }
5063
5064 RTCList<ComPtr<IMedium> > m_llMediums;
5065 StringsList m_llFilesToDelete;
5066
5067 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5068};
5069
5070/**
5071 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5072 * SessionMachine::taskHandler().
5073 *
5074 * @note Locks this object for writing.
5075 *
5076 * @param task
5077 * @return
5078 */
5079void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5080{
5081 LogFlowThisFuncEnter();
5082
5083 AutoCaller autoCaller(this);
5084 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5085 if (FAILED(autoCaller.rc()))
5086 {
5087 /* we might have been uninitialized because the session was accidentally
5088 * closed by the client, so don't assert */
5089 HRESULT rc = setError(E_FAIL,
5090 tr("The session has been accidentally closed"));
5091 task.m_pProgress->i_notifyComplete(rc);
5092 LogFlowThisFuncLeave();
5093 return;
5094 }
5095
5096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5097
5098 HRESULT rc = S_OK;
5099
5100 try
5101 {
5102 ULONG uLogHistoryCount = 3;
5103 ComPtr<ISystemProperties> systemProperties;
5104 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5105 if (FAILED(rc)) throw rc;
5106
5107 if (!systemProperties.isNull())
5108 {
5109 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5110 if (FAILED(rc)) throw rc;
5111 }
5112
5113 MachineState_T oldState = mData->mMachineState;
5114 i_setMachineState(MachineState_SettingUp);
5115 alock.release();
5116 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5117 {
5118 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5119 {
5120 AutoCaller mac(pMedium);
5121 if (FAILED(mac.rc())) throw mac.rc();
5122 Utf8Str strLocation = pMedium->i_getLocationFull();
5123 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5124 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5125 if (FAILED(rc)) throw rc;
5126 }
5127 if (pMedium->i_isMediumFormatFile())
5128 {
5129 ComPtr<IProgress> pProgress2;
5130 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5131 if (FAILED(rc)) throw rc;
5132 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5133 if (FAILED(rc)) throw rc;
5134 }
5135
5136 /* Close the medium, deliberately without checking the return
5137 * code, and without leaving any trace in the error info, as
5138 * a failure here is a very minor issue, which shouldn't happen
5139 * as above we even managed to delete the medium. */
5140 {
5141 ErrorInfoKeeper eik;
5142 pMedium->Close();
5143 }
5144 }
5145 i_setMachineState(oldState);
5146 alock.acquire();
5147
5148 // delete the files pushed on the task list by Machine::Delete()
5149 // (this includes saved states of the machine and snapshots and
5150 // medium storage files from the IMedium list passed in, and the
5151 // machine XML file)
5152 for (StringsList::const_iterator
5153 it = task.m_llFilesToDelete.begin();
5154 it != task.m_llFilesToDelete.end();
5155 ++it)
5156 {
5157 const Utf8Str &strFile = *it;
5158 LogFunc(("Deleting file %s\n", strFile.c_str()));
5159 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5160 if (FAILED(rc)) throw rc;
5161
5162 int vrc = RTFileDelete(strFile.c_str());
5163 if (RT_FAILURE(vrc))
5164 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5165 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5166 }
5167
5168 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5169 if (FAILED(rc)) throw rc;
5170
5171 /* delete the settings only when the file actually exists */
5172 if (mData->pMachineConfigFile->fileExists())
5173 {
5174 /* Delete any backup or uncommitted XML files. Ignore failures.
5175 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5176 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5177 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5178 RTFileDelete(otherXml.c_str());
5179 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5180 RTFileDelete(otherXml.c_str());
5181
5182 /* delete the Logs folder, nothing important should be left
5183 * there (we don't check for errors because the user might have
5184 * some private files there that we don't want to delete) */
5185 Utf8Str logFolder;
5186 getLogFolder(logFolder);
5187 Assert(logFolder.length());
5188 if (RTDirExists(logFolder.c_str()))
5189 {
5190 /* Delete all VBox.log[.N] files from the Logs folder
5191 * (this must be in sync with the rotation logic in
5192 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5193 * files that may have been created by the GUI. */
5194 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5195 RTFileDelete(log.c_str());
5196 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5197 RTFileDelete(log.c_str());
5198 for (ULONG i = uLogHistoryCount; i > 0; i--)
5199 {
5200 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5201 RTFileDelete(log.c_str());
5202 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5203 RTFileDelete(log.c_str());
5204 }
5205 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5206 RTFileDelete(log.c_str());
5207#if defined(RT_OS_WINDOWS)
5208 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5209 RTFileDelete(log.c_str());
5210 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5211 RTFileDelete(log.c_str());
5212#endif
5213
5214 RTDirRemove(logFolder.c_str());
5215 }
5216
5217 /* delete the Snapshots folder, nothing important should be left
5218 * there (we don't check for errors because the user might have
5219 * some private files there that we don't want to delete) */
5220 Utf8Str strFullSnapshotFolder;
5221 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5222 Assert(!strFullSnapshotFolder.isEmpty());
5223 if (RTDirExists(strFullSnapshotFolder.c_str()))
5224 RTDirRemove(strFullSnapshotFolder.c_str());
5225
5226 // delete the directory that contains the settings file, but only
5227 // if it matches the VM name
5228 Utf8Str settingsDir;
5229 if (i_isInOwnDir(&settingsDir))
5230 RTDirRemove(settingsDir.c_str());
5231 }
5232
5233 alock.release();
5234
5235 mParent->i_saveModifiedRegistries();
5236 }
5237 catch (HRESULT aRC) { rc = aRC; }
5238
5239 task.m_pProgress->i_notifyComplete(rc);
5240
5241 LogFlowThisFuncLeave();
5242}
5243
5244HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5245{
5246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5247
5248 HRESULT rc = i_checkStateDependency(MutableStateDep);
5249 if (FAILED(rc)) return rc;
5250
5251 if (mData->mRegistered)
5252 return setError(VBOX_E_INVALID_VM_STATE,
5253 tr("Cannot delete settings of a registered machine"));
5254
5255 // collect files to delete
5256 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5257 // machine config file
5258 if (mData->pMachineConfigFile->fileExists())
5259 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5260 // backup of machine config file
5261 Utf8Str strTmp(mData->m_strConfigFileFull);
5262 strTmp.append("-prev");
5263 if (RTFileExists(strTmp.c_str()))
5264 llFilesToDelete.push_back(strTmp);
5265
5266 RTCList<ComPtr<IMedium> > llMediums;
5267 for (size_t i = 0; i < aMedia.size(); ++i)
5268 {
5269 IMedium *pIMedium(aMedia[i]);
5270 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5271 if (pMedium.isNull())
5272 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5273 SafeArray<BSTR> ids;
5274 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5275 if (FAILED(rc)) return rc;
5276 /* At this point the medium should not have any back references
5277 * anymore. If it has it is attached to another VM and *must* not
5278 * deleted. */
5279 if (ids.size() < 1)
5280 llMediums.append(pMedium);
5281 }
5282
5283 ComObjPtr<Progress> pProgress;
5284 pProgress.createObject();
5285 rc = pProgress->init(i_getVirtualBox(),
5286 static_cast<IMachine*>(this) /* aInitiator */,
5287 tr("Deleting files"),
5288 true /* fCancellable */,
5289 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5290 tr("Collecting file inventory"));
5291 if (FAILED(rc))
5292 return rc;
5293
5294 /* create and start the task on a separate thread (note that it will not
5295 * start working until we release alock) */
5296 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5297 rc = pTask->createThread();
5298 pTask = NULL;
5299 if (FAILED(rc))
5300 return rc;
5301
5302 pProgress.queryInterfaceTo(aProgress.asOutParam());
5303
5304 LogFlowFuncLeave();
5305
5306 return S_OK;
5307}
5308
5309HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5310{
5311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5312
5313 ComObjPtr<Snapshot> pSnapshot;
5314 HRESULT rc;
5315
5316 if (aNameOrId.isEmpty())
5317 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5318 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5319 else
5320 {
5321 Guid uuid(aNameOrId);
5322 if (uuid.isValid())
5323 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5324 else
5325 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5326 }
5327 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5328
5329 return rc;
5330}
5331
5332HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5333 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5334{
5335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5336
5337 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5338 if (FAILED(rc)) return rc;
5339
5340 ComObjPtr<SharedFolder> sharedFolder;
5341 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5342 if (SUCCEEDED(rc))
5343 return setError(VBOX_E_OBJECT_IN_USE,
5344 tr("Shared folder named '%s' already exists"),
5345 aName.c_str());
5346
5347 sharedFolder.createObject();
5348 rc = sharedFolder->init(i_getMachine(),
5349 aName,
5350 aHostPath,
5351 !!aWritable,
5352 !!aAutomount,
5353 aAutoMountPoint,
5354 true /* fFailOnError */);
5355 if (FAILED(rc)) return rc;
5356
5357 i_setModified(IsModified_SharedFolders);
5358 mHWData.backup();
5359 mHWData->mSharedFolders.push_back(sharedFolder);
5360
5361 /* inform the direct session if any */
5362 alock.release();
5363 i_onSharedFolderChange();
5364
5365 return S_OK;
5366}
5367
5368HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5369{
5370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5371
5372 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5373 if (FAILED(rc)) return rc;
5374
5375 ComObjPtr<SharedFolder> sharedFolder;
5376 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5377 if (FAILED(rc)) return rc;
5378
5379 i_setModified(IsModified_SharedFolders);
5380 mHWData.backup();
5381 mHWData->mSharedFolders.remove(sharedFolder);
5382
5383 /* inform the direct session if any */
5384 alock.release();
5385 i_onSharedFolderChange();
5386
5387 return S_OK;
5388}
5389
5390HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5391{
5392 /* start with No */
5393 *aCanShow = FALSE;
5394
5395 ComPtr<IInternalSessionControl> directControl;
5396 {
5397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5398
5399 if (mData->mSession.mState != SessionState_Locked)
5400 return setError(VBOX_E_INVALID_VM_STATE,
5401 tr("Machine is not locked for session (session state: %s)"),
5402 Global::stringifySessionState(mData->mSession.mState));
5403
5404 if (mData->mSession.mLockType == LockType_VM)
5405 directControl = mData->mSession.mDirectControl;
5406 }
5407
5408 /* ignore calls made after #OnSessionEnd() is called */
5409 if (!directControl)
5410 return S_OK;
5411
5412 LONG64 dummy;
5413 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5414}
5415
5416HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5417{
5418 ComPtr<IInternalSessionControl> directControl;
5419 {
5420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5421
5422 if (mData->mSession.mState != SessionState_Locked)
5423 return setError(E_FAIL,
5424 tr("Machine is not locked for session (session state: %s)"),
5425 Global::stringifySessionState(mData->mSession.mState));
5426
5427 if (mData->mSession.mLockType == LockType_VM)
5428 directControl = mData->mSession.mDirectControl;
5429 }
5430
5431 /* ignore calls made after #OnSessionEnd() is called */
5432 if (!directControl)
5433 return S_OK;
5434
5435 BOOL dummy;
5436 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5437}
5438
5439#ifdef VBOX_WITH_GUEST_PROPS
5440/**
5441 * Look up a guest property in VBoxSVC's internal structures.
5442 */
5443HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5444 com::Utf8Str &aValue,
5445 LONG64 *aTimestamp,
5446 com::Utf8Str &aFlags) const
5447{
5448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5449
5450 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5451 if (it != mHWData->mGuestProperties.end())
5452 {
5453 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5454 aValue = it->second.strValue;
5455 *aTimestamp = it->second.mTimestamp;
5456 GuestPropWriteFlags(it->second.mFlags, szFlags);
5457 aFlags = Utf8Str(szFlags);
5458 }
5459
5460 return S_OK;
5461}
5462
5463/**
5464 * Query the VM that a guest property belongs to for the property.
5465 * @returns E_ACCESSDENIED if the VM process is not available or not
5466 * currently handling queries and the lookup should then be done in
5467 * VBoxSVC.
5468 */
5469HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5470 com::Utf8Str &aValue,
5471 LONG64 *aTimestamp,
5472 com::Utf8Str &aFlags) const
5473{
5474 HRESULT rc = S_OK;
5475 Bstr bstrValue;
5476 Bstr bstrFlags;
5477
5478 ComPtr<IInternalSessionControl> directControl;
5479 {
5480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5481 if (mData->mSession.mLockType == LockType_VM)
5482 directControl = mData->mSession.mDirectControl;
5483 }
5484
5485 /* ignore calls made after #OnSessionEnd() is called */
5486 if (!directControl)
5487 rc = E_ACCESSDENIED;
5488 else
5489 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5490 0 /* accessMode */,
5491 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5492
5493 aValue = bstrValue;
5494 aFlags = bstrFlags;
5495
5496 return rc;
5497}
5498#endif // VBOX_WITH_GUEST_PROPS
5499
5500HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5501 com::Utf8Str &aValue,
5502 LONG64 *aTimestamp,
5503 com::Utf8Str &aFlags)
5504{
5505#ifndef VBOX_WITH_GUEST_PROPS
5506 ReturnComNotImplemented();
5507#else // VBOX_WITH_GUEST_PROPS
5508
5509 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5510
5511 if (rc == E_ACCESSDENIED)
5512 /* The VM is not running or the service is not (yet) accessible */
5513 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5514 return rc;
5515#endif // VBOX_WITH_GUEST_PROPS
5516}
5517
5518HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5519{
5520 LONG64 dummyTimestamp;
5521 com::Utf8Str dummyFlags;
5522 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5523 return rc;
5524
5525}
5526HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5527{
5528 com::Utf8Str dummyFlags;
5529 com::Utf8Str dummyValue;
5530 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5531 return rc;
5532}
5533
5534#ifdef VBOX_WITH_GUEST_PROPS
5535/**
5536 * Set a guest property in VBoxSVC's internal structures.
5537 */
5538HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5539 const com::Utf8Str &aFlags, bool fDelete)
5540{
5541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5542 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5543 if (FAILED(rc)) return rc;
5544
5545 try
5546 {
5547 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5548 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5549 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5550
5551 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5552 if (it == mHWData->mGuestProperties.end())
5553 {
5554 if (!fDelete)
5555 {
5556 i_setModified(IsModified_MachineData);
5557 mHWData.backupEx();
5558
5559 RTTIMESPEC time;
5560 HWData::GuestProperty prop;
5561 prop.strValue = Bstr(aValue).raw();
5562 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5563 prop.mFlags = fFlags;
5564 mHWData->mGuestProperties[aName] = prop;
5565 }
5566 }
5567 else
5568 {
5569 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5570 {
5571 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5572 }
5573 else
5574 {
5575 i_setModified(IsModified_MachineData);
5576 mHWData.backupEx();
5577
5578 /* The backupEx() operation invalidates our iterator,
5579 * so get a new one. */
5580 it = mHWData->mGuestProperties.find(aName);
5581 Assert(it != mHWData->mGuestProperties.end());
5582
5583 if (!fDelete)
5584 {
5585 RTTIMESPEC time;
5586 it->second.strValue = aValue;
5587 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5588 it->second.mFlags = fFlags;
5589 }
5590 else
5591 mHWData->mGuestProperties.erase(it);
5592 }
5593 }
5594
5595 if (SUCCEEDED(rc))
5596 {
5597 alock.release();
5598
5599 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5600 }
5601 }
5602 catch (std::bad_alloc &)
5603 {
5604 rc = E_OUTOFMEMORY;
5605 }
5606
5607 return rc;
5608}
5609
5610/**
5611 * Set a property on the VM that that property belongs to.
5612 * @returns E_ACCESSDENIED if the VM process is not available or not
5613 * currently handling queries and the setting should then be done in
5614 * VBoxSVC.
5615 */
5616HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5617 const com::Utf8Str &aFlags, bool fDelete)
5618{
5619 HRESULT rc;
5620
5621 try
5622 {
5623 ComPtr<IInternalSessionControl> directControl;
5624 {
5625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5626 if (mData->mSession.mLockType == LockType_VM)
5627 directControl = mData->mSession.mDirectControl;
5628 }
5629
5630 Bstr dummy1; /* will not be changed (setter) */
5631 Bstr dummy2; /* will not be changed (setter) */
5632 LONG64 dummy64;
5633 if (!directControl)
5634 rc = E_ACCESSDENIED;
5635 else
5636 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5637 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5638 fDelete ? 2 : 1 /* accessMode */,
5639 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5640 }
5641 catch (std::bad_alloc &)
5642 {
5643 rc = E_OUTOFMEMORY;
5644 }
5645
5646 return rc;
5647}
5648#endif // VBOX_WITH_GUEST_PROPS
5649
5650HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5651 const com::Utf8Str &aFlags)
5652{
5653#ifndef VBOX_WITH_GUEST_PROPS
5654 ReturnComNotImplemented();
5655#else // VBOX_WITH_GUEST_PROPS
5656 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5657 if (rc == E_ACCESSDENIED)
5658 /* The VM is not running or the service is not (yet) accessible */
5659 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5660 return rc;
5661#endif // VBOX_WITH_GUEST_PROPS
5662}
5663
5664HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5665{
5666 return setGuestProperty(aProperty, aValue, "");
5667}
5668
5669HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5670{
5671#ifndef VBOX_WITH_GUEST_PROPS
5672 ReturnComNotImplemented();
5673#else // VBOX_WITH_GUEST_PROPS
5674 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5675 if (rc == E_ACCESSDENIED)
5676 /* The VM is not running or the service is not (yet) accessible */
5677 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5678 return rc;
5679#endif // VBOX_WITH_GUEST_PROPS
5680}
5681
5682#ifdef VBOX_WITH_GUEST_PROPS
5683/**
5684 * Enumerate the guest properties in VBoxSVC's internal structures.
5685 */
5686HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5687 std::vector<com::Utf8Str> &aNames,
5688 std::vector<com::Utf8Str> &aValues,
5689 std::vector<LONG64> &aTimestamps,
5690 std::vector<com::Utf8Str> &aFlags)
5691{
5692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5693 Utf8Str strPatterns(aPatterns);
5694
5695 /*
5696 * Look for matching patterns and build up a list.
5697 */
5698 HWData::GuestPropertyMap propMap;
5699 for (HWData::GuestPropertyMap::const_iterator
5700 it = mHWData->mGuestProperties.begin();
5701 it != mHWData->mGuestProperties.end();
5702 ++it)
5703 {
5704 if ( strPatterns.isEmpty()
5705 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5706 RTSTR_MAX,
5707 it->first.c_str(),
5708 RTSTR_MAX,
5709 NULL)
5710 )
5711 propMap.insert(*it);
5712 }
5713
5714 alock.release();
5715
5716 /*
5717 * And build up the arrays for returning the property information.
5718 */
5719 size_t cEntries = propMap.size();
5720
5721 aNames.resize(cEntries);
5722 aValues.resize(cEntries);
5723 aTimestamps.resize(cEntries);
5724 aFlags.resize(cEntries);
5725
5726 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5727 size_t i = 0;
5728 for (HWData::GuestPropertyMap::const_iterator
5729 it = propMap.begin();
5730 it != propMap.end();
5731 ++it, ++i)
5732 {
5733 aNames[i] = it->first;
5734 aValues[i] = it->second.strValue;
5735 aTimestamps[i] = it->second.mTimestamp;
5736 GuestPropWriteFlags(it->second.mFlags, szFlags);
5737 aFlags[i] = Utf8Str(szFlags);
5738 }
5739
5740 return S_OK;
5741}
5742
5743/**
5744 * Enumerate the properties managed by a VM.
5745 * @returns E_ACCESSDENIED if the VM process is not available or not
5746 * currently handling queries and the setting should then be done in
5747 * VBoxSVC.
5748 */
5749HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5750 std::vector<com::Utf8Str> &aNames,
5751 std::vector<com::Utf8Str> &aValues,
5752 std::vector<LONG64> &aTimestamps,
5753 std::vector<com::Utf8Str> &aFlags)
5754{
5755 HRESULT rc;
5756 ComPtr<IInternalSessionControl> directControl;
5757 {
5758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5759 if (mData->mSession.mLockType == LockType_VM)
5760 directControl = mData->mSession.mDirectControl;
5761 }
5762
5763 com::SafeArray<BSTR> bNames;
5764 com::SafeArray<BSTR> bValues;
5765 com::SafeArray<LONG64> bTimestamps;
5766 com::SafeArray<BSTR> bFlags;
5767
5768 if (!directControl)
5769 rc = E_ACCESSDENIED;
5770 else
5771 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5772 ComSafeArrayAsOutParam(bNames),
5773 ComSafeArrayAsOutParam(bValues),
5774 ComSafeArrayAsOutParam(bTimestamps),
5775 ComSafeArrayAsOutParam(bFlags));
5776 size_t i;
5777 aNames.resize(bNames.size());
5778 for (i = 0; i < bNames.size(); ++i)
5779 aNames[i] = Utf8Str(bNames[i]);
5780 aValues.resize(bValues.size());
5781 for (i = 0; i < bValues.size(); ++i)
5782 aValues[i] = Utf8Str(bValues[i]);
5783 aTimestamps.resize(bTimestamps.size());
5784 for (i = 0; i < bTimestamps.size(); ++i)
5785 aTimestamps[i] = bTimestamps[i];
5786 aFlags.resize(bFlags.size());
5787 for (i = 0; i < bFlags.size(); ++i)
5788 aFlags[i] = Utf8Str(bFlags[i]);
5789
5790 return rc;
5791}
5792#endif // VBOX_WITH_GUEST_PROPS
5793HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5794 std::vector<com::Utf8Str> &aNames,
5795 std::vector<com::Utf8Str> &aValues,
5796 std::vector<LONG64> &aTimestamps,
5797 std::vector<com::Utf8Str> &aFlags)
5798{
5799#ifndef VBOX_WITH_GUEST_PROPS
5800 ReturnComNotImplemented();
5801#else // VBOX_WITH_GUEST_PROPS
5802
5803 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5804
5805 if (rc == E_ACCESSDENIED)
5806 /* The VM is not running or the service is not (yet) accessible */
5807 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5808 return rc;
5809#endif // VBOX_WITH_GUEST_PROPS
5810}
5811
5812HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5813 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5814{
5815 MediumAttachmentList atts;
5816
5817 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5818 if (FAILED(rc)) return rc;
5819
5820 aMediumAttachments.resize(atts.size());
5821 size_t i = 0;
5822 for (MediumAttachmentList::const_iterator
5823 it = atts.begin();
5824 it != atts.end();
5825 ++it, ++i)
5826 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5827
5828 return S_OK;
5829}
5830
5831HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5832 LONG aControllerPort,
5833 LONG aDevice,
5834 ComPtr<IMediumAttachment> &aAttachment)
5835{
5836 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5837 aName.c_str(), aControllerPort, aDevice));
5838
5839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5840
5841 aAttachment = NULL;
5842
5843 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5844 aName,
5845 aControllerPort,
5846 aDevice);
5847 if (pAttach.isNull())
5848 return setError(VBOX_E_OBJECT_NOT_FOUND,
5849 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5850 aDevice, aControllerPort, aName.c_str());
5851
5852 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5853
5854 return S_OK;
5855}
5856
5857
5858HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5859 StorageBus_T aConnectionType,
5860 ComPtr<IStorageController> &aController)
5861{
5862 if ( (aConnectionType <= StorageBus_Null)
5863 || (aConnectionType > StorageBus_VirtioSCSI))
5864 return setError(E_INVALIDARG,
5865 tr("Invalid connection type: %d"),
5866 aConnectionType);
5867
5868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5869
5870 HRESULT rc = i_checkStateDependency(MutableStateDep);
5871 if (FAILED(rc)) return rc;
5872
5873 /* try to find one with the name first. */
5874 ComObjPtr<StorageController> ctrl;
5875
5876 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5877 if (SUCCEEDED(rc))
5878 return setError(VBOX_E_OBJECT_IN_USE,
5879 tr("Storage controller named '%s' already exists"),
5880 aName.c_str());
5881
5882 ctrl.createObject();
5883
5884 /* get a new instance number for the storage controller */
5885 ULONG ulInstance = 0;
5886 bool fBootable = true;
5887 for (StorageControllerList::const_iterator
5888 it = mStorageControllers->begin();
5889 it != mStorageControllers->end();
5890 ++it)
5891 {
5892 if ((*it)->i_getStorageBus() == aConnectionType)
5893 {
5894 ULONG ulCurInst = (*it)->i_getInstance();
5895
5896 if (ulCurInst >= ulInstance)
5897 ulInstance = ulCurInst + 1;
5898
5899 /* Only one controller of each type can be marked as bootable. */
5900 if ((*it)->i_getBootable())
5901 fBootable = false;
5902 }
5903 }
5904
5905 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5906 if (FAILED(rc)) return rc;
5907
5908 i_setModified(IsModified_Storage);
5909 mStorageControllers.backup();
5910 mStorageControllers->push_back(ctrl);
5911
5912 ctrl.queryInterfaceTo(aController.asOutParam());
5913
5914 /* inform the direct session if any */
5915 alock.release();
5916 i_onStorageControllerChange(i_getId(), aName);
5917
5918 return S_OK;
5919}
5920
5921HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5922 ComPtr<IStorageController> &aStorageController)
5923{
5924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5925
5926 ComObjPtr<StorageController> ctrl;
5927
5928 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5929 if (SUCCEEDED(rc))
5930 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5931
5932 return rc;
5933}
5934
5935HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5936 ULONG aInstance,
5937 ComPtr<IStorageController> &aStorageController)
5938{
5939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5940
5941 for (StorageControllerList::const_iterator
5942 it = mStorageControllers->begin();
5943 it != mStorageControllers->end();
5944 ++it)
5945 {
5946 if ( (*it)->i_getStorageBus() == aConnectionType
5947 && (*it)->i_getInstance() == aInstance)
5948 {
5949 (*it).queryInterfaceTo(aStorageController.asOutParam());
5950 return S_OK;
5951 }
5952 }
5953
5954 return setError(VBOX_E_OBJECT_NOT_FOUND,
5955 tr("Could not find a storage controller with instance number '%lu'"),
5956 aInstance);
5957}
5958
5959HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5960{
5961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5962
5963 HRESULT rc = i_checkStateDependency(MutableStateDep);
5964 if (FAILED(rc)) return rc;
5965
5966 ComObjPtr<StorageController> ctrl;
5967
5968 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5969 if (SUCCEEDED(rc))
5970 {
5971 /* Ensure that only one controller of each type is marked as bootable. */
5972 if (aBootable == TRUE)
5973 {
5974 for (StorageControllerList::const_iterator
5975 it = mStorageControllers->begin();
5976 it != mStorageControllers->end();
5977 ++it)
5978 {
5979 ComObjPtr<StorageController> aCtrl = (*it);
5980
5981 if ( (aCtrl->i_getName() != aName)
5982 && aCtrl->i_getBootable() == TRUE
5983 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5984 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5985 {
5986 aCtrl->i_setBootable(FALSE);
5987 break;
5988 }
5989 }
5990 }
5991
5992 if (SUCCEEDED(rc))
5993 {
5994 ctrl->i_setBootable(aBootable);
5995 i_setModified(IsModified_Storage);
5996 }
5997 }
5998
5999 if (SUCCEEDED(rc))
6000 {
6001 /* inform the direct session if any */
6002 alock.release();
6003 i_onStorageControllerChange(i_getId(), aName);
6004 }
6005
6006 return rc;
6007}
6008
6009HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6010{
6011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6012
6013 HRESULT rc = i_checkStateDependency(MutableStateDep);
6014 if (FAILED(rc)) return rc;
6015
6016 ComObjPtr<StorageController> ctrl;
6017 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6018 if (FAILED(rc)) return rc;
6019
6020 MediumAttachmentList llDetachedAttachments;
6021 {
6022 /* find all attached devices to the appropriate storage controller and detach them all */
6023 // make a temporary list because detachDevice invalidates iterators into
6024 // mMediumAttachments
6025 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6026
6027 for (MediumAttachmentList::const_iterator
6028 it = llAttachments2.begin();
6029 it != llAttachments2.end();
6030 ++it)
6031 {
6032 MediumAttachment *pAttachTemp = *it;
6033
6034 AutoCaller localAutoCaller(pAttachTemp);
6035 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6036
6037 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6038
6039 if (pAttachTemp->i_getControllerName() == aName)
6040 {
6041 llDetachedAttachments.push_back(pAttachTemp);
6042 rc = i_detachDevice(pAttachTemp, alock, NULL);
6043 if (FAILED(rc)) return rc;
6044 }
6045 }
6046 }
6047
6048 /* send event about detached devices before removing parent controller */
6049 for (MediumAttachmentList::const_iterator
6050 it = llDetachedAttachments.begin();
6051 it != llDetachedAttachments.end();
6052 ++it)
6053 {
6054 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6055 }
6056
6057 /* We can remove it now. */
6058 i_setModified(IsModified_Storage);
6059 mStorageControllers.backup();
6060
6061 ctrl->i_unshare();
6062
6063 mStorageControllers->remove(ctrl);
6064
6065 /* inform the direct session if any */
6066 alock.release();
6067 i_onStorageControllerChange(i_getId(), aName);
6068
6069 return S_OK;
6070}
6071
6072HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6073 ComPtr<IUSBController> &aController)
6074{
6075 if ( (aType <= USBControllerType_Null)
6076 || (aType >= USBControllerType_Last))
6077 return setError(E_INVALIDARG,
6078 tr("Invalid USB controller type: %d"),
6079 aType);
6080
6081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6082
6083 HRESULT rc = i_checkStateDependency(MutableStateDep);
6084 if (FAILED(rc)) return rc;
6085
6086 /* try to find one with the same type first. */
6087 ComObjPtr<USBController> ctrl;
6088
6089 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6090 if (SUCCEEDED(rc))
6091 return setError(VBOX_E_OBJECT_IN_USE,
6092 tr("USB controller named '%s' already exists"),
6093 aName.c_str());
6094
6095 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6096 ULONG maxInstances;
6097 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6098 if (FAILED(rc))
6099 return rc;
6100
6101 ULONG cInstances = i_getUSBControllerCountByType(aType);
6102 if (cInstances >= maxInstances)
6103 return setError(E_INVALIDARG,
6104 tr("Too many USB controllers of this type"));
6105
6106 ctrl.createObject();
6107
6108 rc = ctrl->init(this, aName, aType);
6109 if (FAILED(rc)) return rc;
6110
6111 i_setModified(IsModified_USB);
6112 mUSBControllers.backup();
6113 mUSBControllers->push_back(ctrl);
6114
6115 ctrl.queryInterfaceTo(aController.asOutParam());
6116
6117 /* inform the direct session if any */
6118 alock.release();
6119 i_onUSBControllerChange();
6120
6121 return S_OK;
6122}
6123
6124HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6125{
6126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6127
6128 ComObjPtr<USBController> ctrl;
6129
6130 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6131 if (SUCCEEDED(rc))
6132 ctrl.queryInterfaceTo(aController.asOutParam());
6133
6134 return rc;
6135}
6136
6137HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6138 ULONG *aControllers)
6139{
6140 if ( (aType <= USBControllerType_Null)
6141 || (aType >= USBControllerType_Last))
6142 return setError(E_INVALIDARG,
6143 tr("Invalid USB controller type: %d"),
6144 aType);
6145
6146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6147
6148 ComObjPtr<USBController> ctrl;
6149
6150 *aControllers = i_getUSBControllerCountByType(aType);
6151
6152 return S_OK;
6153}
6154
6155HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6156{
6157
6158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6159
6160 HRESULT rc = i_checkStateDependency(MutableStateDep);
6161 if (FAILED(rc)) return rc;
6162
6163 ComObjPtr<USBController> ctrl;
6164 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6165 if (FAILED(rc)) return rc;
6166
6167 i_setModified(IsModified_USB);
6168 mUSBControllers.backup();
6169
6170 ctrl->i_unshare();
6171
6172 mUSBControllers->remove(ctrl);
6173
6174 /* inform the direct session if any */
6175 alock.release();
6176 i_onUSBControllerChange();
6177
6178 return S_OK;
6179}
6180
6181HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6182 ULONG *aOriginX,
6183 ULONG *aOriginY,
6184 ULONG *aWidth,
6185 ULONG *aHeight,
6186 BOOL *aEnabled)
6187{
6188 uint32_t u32OriginX= 0;
6189 uint32_t u32OriginY= 0;
6190 uint32_t u32Width = 0;
6191 uint32_t u32Height = 0;
6192 uint16_t u16Flags = 0;
6193
6194 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6195 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6196 if (RT_FAILURE(vrc))
6197 {
6198#ifdef RT_OS_WINDOWS
6199 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6200 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6201 * So just assign fEnable to TRUE again.
6202 * The right fix would be to change GUI API wrappers to make sure that parameters
6203 * are changed only if API succeeds.
6204 */
6205 *aEnabled = TRUE;
6206#endif
6207 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6208 tr("Saved guest size is not available (%Rrc)"),
6209 vrc);
6210 }
6211
6212 *aOriginX = u32OriginX;
6213 *aOriginY = u32OriginY;
6214 *aWidth = u32Width;
6215 *aHeight = u32Height;
6216 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6217
6218 return S_OK;
6219}
6220
6221HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6222 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6223{
6224 if (aScreenId != 0)
6225 return E_NOTIMPL;
6226
6227 if ( aBitmapFormat != BitmapFormat_BGR0
6228 && aBitmapFormat != BitmapFormat_BGRA
6229 && aBitmapFormat != BitmapFormat_RGBA
6230 && aBitmapFormat != BitmapFormat_PNG)
6231 return setError(E_NOTIMPL,
6232 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6233
6234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6235
6236 uint8_t *pu8Data = NULL;
6237 uint32_t cbData = 0;
6238 uint32_t u32Width = 0;
6239 uint32_t u32Height = 0;
6240
6241 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6242
6243 if (RT_FAILURE(vrc))
6244 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6245 tr("Saved thumbnail data is not available (%Rrc)"),
6246 vrc);
6247
6248 HRESULT hr = S_OK;
6249
6250 *aWidth = u32Width;
6251 *aHeight = u32Height;
6252
6253 if (cbData > 0)
6254 {
6255 /* Convert pixels to the format expected by the API caller. */
6256 if (aBitmapFormat == BitmapFormat_BGR0)
6257 {
6258 /* [0] B, [1] G, [2] R, [3] 0. */
6259 aData.resize(cbData);
6260 memcpy(&aData.front(), pu8Data, cbData);
6261 }
6262 else if (aBitmapFormat == BitmapFormat_BGRA)
6263 {
6264 /* [0] B, [1] G, [2] R, [3] A. */
6265 aData.resize(cbData);
6266 for (uint32_t i = 0; i < cbData; i += 4)
6267 {
6268 aData[i] = pu8Data[i];
6269 aData[i + 1] = pu8Data[i + 1];
6270 aData[i + 2] = pu8Data[i + 2];
6271 aData[i + 3] = 0xff;
6272 }
6273 }
6274 else if (aBitmapFormat == BitmapFormat_RGBA)
6275 {
6276 /* [0] R, [1] G, [2] B, [3] A. */
6277 aData.resize(cbData);
6278 for (uint32_t i = 0; i < cbData; i += 4)
6279 {
6280 aData[i] = pu8Data[i + 2];
6281 aData[i + 1] = pu8Data[i + 1];
6282 aData[i + 2] = pu8Data[i];
6283 aData[i + 3] = 0xff;
6284 }
6285 }
6286 else if (aBitmapFormat == BitmapFormat_PNG)
6287 {
6288 uint8_t *pu8PNG = NULL;
6289 uint32_t cbPNG = 0;
6290 uint32_t cxPNG = 0;
6291 uint32_t cyPNG = 0;
6292
6293 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6294
6295 if (RT_SUCCESS(vrc))
6296 {
6297 aData.resize(cbPNG);
6298 if (cbPNG)
6299 memcpy(&aData.front(), pu8PNG, cbPNG);
6300 }
6301 else
6302 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6303 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6304 vrc);
6305
6306 RTMemFree(pu8PNG);
6307 }
6308 }
6309
6310 freeSavedDisplayScreenshot(pu8Data);
6311
6312 return hr;
6313}
6314
6315HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6316 ULONG *aWidth,
6317 ULONG *aHeight,
6318 std::vector<BitmapFormat_T> &aBitmapFormats)
6319{
6320 if (aScreenId != 0)
6321 return E_NOTIMPL;
6322
6323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 uint8_t *pu8Data = NULL;
6326 uint32_t cbData = 0;
6327 uint32_t u32Width = 0;
6328 uint32_t u32Height = 0;
6329
6330 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6331
6332 if (RT_FAILURE(vrc))
6333 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6334 tr("Saved screenshot data is not available (%Rrc)"),
6335 vrc);
6336
6337 *aWidth = u32Width;
6338 *aHeight = u32Height;
6339 aBitmapFormats.resize(1);
6340 aBitmapFormats[0] = BitmapFormat_PNG;
6341
6342 freeSavedDisplayScreenshot(pu8Data);
6343
6344 return S_OK;
6345}
6346
6347HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6348 BitmapFormat_T aBitmapFormat,
6349 ULONG *aWidth,
6350 ULONG *aHeight,
6351 std::vector<BYTE> &aData)
6352{
6353 if (aScreenId != 0)
6354 return E_NOTIMPL;
6355
6356 if (aBitmapFormat != BitmapFormat_PNG)
6357 return E_NOTIMPL;
6358
6359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6360
6361 uint8_t *pu8Data = NULL;
6362 uint32_t cbData = 0;
6363 uint32_t u32Width = 0;
6364 uint32_t u32Height = 0;
6365
6366 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6367
6368 if (RT_FAILURE(vrc))
6369 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6370 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6371 vrc);
6372
6373 *aWidth = u32Width;
6374 *aHeight = u32Height;
6375
6376 aData.resize(cbData);
6377 if (cbData)
6378 memcpy(&aData.front(), pu8Data, cbData);
6379
6380 freeSavedDisplayScreenshot(pu8Data);
6381
6382 return S_OK;
6383}
6384
6385HRESULT Machine::hotPlugCPU(ULONG aCpu)
6386{
6387 HRESULT rc = S_OK;
6388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6389
6390 if (!mHWData->mCPUHotPlugEnabled)
6391 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6392
6393 if (aCpu >= mHWData->mCPUCount)
6394 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6395
6396 if (mHWData->mCPUAttached[aCpu])
6397 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6398
6399 alock.release();
6400 rc = i_onCPUChange(aCpu, false);
6401 alock.acquire();
6402 if (FAILED(rc)) return rc;
6403
6404 i_setModified(IsModified_MachineData);
6405 mHWData.backup();
6406 mHWData->mCPUAttached[aCpu] = true;
6407
6408 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6409 if (Global::IsOnline(mData->mMachineState))
6410 i_saveSettings(NULL, alock);
6411
6412 return S_OK;
6413}
6414
6415HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6416{
6417 HRESULT rc = S_OK;
6418
6419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 if (!mHWData->mCPUHotPlugEnabled)
6422 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6423
6424 if (aCpu >= SchemaDefs::MaxCPUCount)
6425 return setError(E_INVALIDARG,
6426 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6427 SchemaDefs::MaxCPUCount);
6428
6429 if (!mHWData->mCPUAttached[aCpu])
6430 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6431
6432 /* CPU 0 can't be detached */
6433 if (aCpu == 0)
6434 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6435
6436 alock.release();
6437 rc = i_onCPUChange(aCpu, true);
6438 alock.acquire();
6439 if (FAILED(rc)) return rc;
6440
6441 i_setModified(IsModified_MachineData);
6442 mHWData.backup();
6443 mHWData->mCPUAttached[aCpu] = false;
6444
6445 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6446 if (Global::IsOnline(mData->mMachineState))
6447 i_saveSettings(NULL, alock);
6448
6449 return S_OK;
6450}
6451
6452HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6453{
6454 *aAttached = false;
6455
6456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6457
6458 /* If hotplug is enabled the CPU is always enabled. */
6459 if (!mHWData->mCPUHotPlugEnabled)
6460 {
6461 if (aCpu < mHWData->mCPUCount)
6462 *aAttached = true;
6463 }
6464 else
6465 {
6466 if (aCpu < SchemaDefs::MaxCPUCount)
6467 *aAttached = mHWData->mCPUAttached[aCpu];
6468 }
6469
6470 return S_OK;
6471}
6472
6473HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6474{
6475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6476
6477 Utf8Str log = i_getLogFilename(aIdx);
6478 if (!RTFileExists(log.c_str()))
6479 log.setNull();
6480 aFilename = log;
6481
6482 return S_OK;
6483}
6484
6485HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6486{
6487 if (aSize < 0)
6488 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6489
6490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6491
6492 HRESULT rc = S_OK;
6493 Utf8Str log = i_getLogFilename(aIdx);
6494
6495 /* do not unnecessarily hold the lock while doing something which does
6496 * not need the lock and potentially takes a long time. */
6497 alock.release();
6498
6499 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6500 * keeps the SOAP reply size under 1M for the webservice (we're using
6501 * base64 encoded strings for binary data for years now, avoiding the
6502 * expansion of each byte array element to approx. 25 bytes of XML. */
6503 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6504 aData.resize(cbData);
6505
6506 RTFILE LogFile;
6507 int vrc = RTFileOpen(&LogFile, log.c_str(),
6508 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6509 if (RT_SUCCESS(vrc))
6510 {
6511 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6512 if (RT_SUCCESS(vrc))
6513 aData.resize(cbData);
6514 else
6515 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6516 tr("Could not read log file '%s' (%Rrc)"),
6517 log.c_str(), vrc);
6518 RTFileClose(LogFile);
6519 }
6520 else
6521 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6522 tr("Could not open log file '%s' (%Rrc)"),
6523 log.c_str(), vrc);
6524
6525 if (FAILED(rc))
6526 aData.resize(0);
6527
6528 return rc;
6529}
6530
6531
6532/**
6533 * Currently this method doesn't attach device to the running VM,
6534 * just makes sure it's plugged on next VM start.
6535 */
6536HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6537{
6538 // lock scope
6539 {
6540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6541
6542 HRESULT rc = i_checkStateDependency(MutableStateDep);
6543 if (FAILED(rc)) return rc;
6544
6545 ChipsetType_T aChipset = ChipsetType_PIIX3;
6546 COMGETTER(ChipsetType)(&aChipset);
6547
6548 if (aChipset != ChipsetType_ICH9)
6549 {
6550 return setError(E_INVALIDARG,
6551 tr("Host PCI attachment only supported with ICH9 chipset"));
6552 }
6553
6554 // check if device with this host PCI address already attached
6555 for (HWData::PCIDeviceAssignmentList::const_iterator
6556 it = mHWData->mPCIDeviceAssignments.begin();
6557 it != mHWData->mPCIDeviceAssignments.end();
6558 ++it)
6559 {
6560 LONG iHostAddress = -1;
6561 ComPtr<PCIDeviceAttachment> pAttach;
6562 pAttach = *it;
6563 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6564 if (iHostAddress == aHostAddress)
6565 return setError(E_INVALIDARG,
6566 tr("Device with host PCI address already attached to this VM"));
6567 }
6568
6569 ComObjPtr<PCIDeviceAttachment> pda;
6570 char name[32];
6571
6572 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6573 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6574 pda.createObject();
6575 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6576 i_setModified(IsModified_MachineData);
6577 mHWData.backup();
6578 mHWData->mPCIDeviceAssignments.push_back(pda);
6579 }
6580
6581 return S_OK;
6582}
6583
6584/**
6585 * Currently this method doesn't detach device from the running VM,
6586 * just makes sure it's not plugged on next VM start.
6587 */
6588HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6589{
6590 ComObjPtr<PCIDeviceAttachment> pAttach;
6591 bool fRemoved = false;
6592 HRESULT rc;
6593
6594 // lock scope
6595 {
6596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6597
6598 rc = i_checkStateDependency(MutableStateDep);
6599 if (FAILED(rc)) return rc;
6600
6601 for (HWData::PCIDeviceAssignmentList::const_iterator
6602 it = mHWData->mPCIDeviceAssignments.begin();
6603 it != mHWData->mPCIDeviceAssignments.end();
6604 ++it)
6605 {
6606 LONG iHostAddress = -1;
6607 pAttach = *it;
6608 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6609 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6610 {
6611 i_setModified(IsModified_MachineData);
6612 mHWData.backup();
6613 mHWData->mPCIDeviceAssignments.remove(pAttach);
6614 fRemoved = true;
6615 break;
6616 }
6617 }
6618 }
6619
6620
6621 /* Fire event outside of the lock */
6622 if (fRemoved)
6623 {
6624 Assert(!pAttach.isNull());
6625 ComPtr<IEventSource> es;
6626 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6627 Assert(SUCCEEDED(rc));
6628 Bstr mid;
6629 rc = this->COMGETTER(Id)(mid.asOutParam());
6630 Assert(SUCCEEDED(rc));
6631 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6632 }
6633
6634 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6635 tr("No host PCI device %08x attached"),
6636 aHostAddress
6637 );
6638}
6639
6640HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6641{
6642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6643
6644 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6645 size_t i = 0;
6646 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6647 it = mHWData->mPCIDeviceAssignments.begin();
6648 it != mHWData->mPCIDeviceAssignments.end();
6649 ++it, ++i)
6650 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6651
6652 return S_OK;
6653}
6654
6655HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6656{
6657 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6658
6659 return S_OK;
6660}
6661
6662HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6663{
6664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6665
6666 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6667
6668 return S_OK;
6669}
6670
6671HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6672{
6673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6674 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6675 if (SUCCEEDED(hrc))
6676 {
6677 hrc = mHWData.backupEx();
6678 if (SUCCEEDED(hrc))
6679 {
6680 i_setModified(IsModified_MachineData);
6681 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6682 }
6683 }
6684 return hrc;
6685}
6686
6687HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6688{
6689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6690 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6691 return S_OK;
6692}
6693
6694HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
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 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6704 if (SUCCEEDED(hrc))
6705 i_setModified(IsModified_MachineData);
6706 }
6707 }
6708 return hrc;
6709}
6710
6711HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6712{
6713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6714
6715 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6716
6717 return S_OK;
6718}
6719
6720HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6721{
6722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6723 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6724 if (SUCCEEDED(hrc))
6725 {
6726 hrc = mHWData.backupEx();
6727 if (SUCCEEDED(hrc))
6728 {
6729 i_setModified(IsModified_MachineData);
6730 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6731 }
6732 }
6733 return hrc;
6734}
6735
6736HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6737{
6738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6739
6740 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6741
6742 return S_OK;
6743}
6744
6745HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6746{
6747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6748
6749 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6750 if ( SUCCEEDED(hrc)
6751 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6752 {
6753 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6754 int vrc;
6755
6756 if (aAutostartEnabled)
6757 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6758 else
6759 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6760
6761 if (RT_SUCCESS(vrc))
6762 {
6763 hrc = mHWData.backupEx();
6764 if (SUCCEEDED(hrc))
6765 {
6766 i_setModified(IsModified_MachineData);
6767 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6768 }
6769 }
6770 else if (vrc == VERR_NOT_SUPPORTED)
6771 hrc = setError(VBOX_E_NOT_SUPPORTED,
6772 tr("The VM autostart feature is not supported on this platform"));
6773 else if (vrc == VERR_PATH_NOT_FOUND)
6774 hrc = setError(E_FAIL,
6775 tr("The path to the autostart database is not set"));
6776 else
6777 hrc = setError(E_UNEXPECTED,
6778 aAutostartEnabled ?
6779 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6780 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6781 mUserData->s.strName.c_str(), vrc);
6782 }
6783 return hrc;
6784}
6785
6786HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6787{
6788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6789
6790 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6791
6792 return S_OK;
6793}
6794
6795HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6796{
6797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6798 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6799 if (SUCCEEDED(hrc))
6800 {
6801 hrc = mHWData.backupEx();
6802 if (SUCCEEDED(hrc))
6803 {
6804 i_setModified(IsModified_MachineData);
6805 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6806 }
6807 }
6808 return hrc;
6809}
6810
6811HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6812{
6813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6814
6815 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6816
6817 return S_OK;
6818}
6819
6820HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6821{
6822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6823 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6824 if ( SUCCEEDED(hrc)
6825 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6826 {
6827 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6828 int vrc;
6829
6830 if (aAutostopType != AutostopType_Disabled)
6831 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6832 else
6833 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6834
6835 if (RT_SUCCESS(vrc))
6836 {
6837 hrc = mHWData.backupEx();
6838 if (SUCCEEDED(hrc))
6839 {
6840 i_setModified(IsModified_MachineData);
6841 mHWData->mAutostart.enmAutostopType = aAutostopType;
6842 }
6843 }
6844 else if (vrc == VERR_NOT_SUPPORTED)
6845 hrc = setError(VBOX_E_NOT_SUPPORTED,
6846 tr("The VM autostop feature is not supported on this platform"));
6847 else if (vrc == VERR_PATH_NOT_FOUND)
6848 hrc = setError(E_FAIL,
6849 tr("The path to the autostart database is not set"));
6850 else
6851 hrc = setError(E_UNEXPECTED,
6852 aAutostopType != AutostopType_Disabled ?
6853 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6854 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6855 mUserData->s.strName.c_str(), vrc);
6856 }
6857 return hrc;
6858}
6859
6860HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6861{
6862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6863
6864 aDefaultFrontend = mHWData->mDefaultFrontend;
6865
6866 return S_OK;
6867}
6868
6869HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6870{
6871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6872 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6873 if (SUCCEEDED(hrc))
6874 {
6875 hrc = mHWData.backupEx();
6876 if (SUCCEEDED(hrc))
6877 {
6878 i_setModified(IsModified_MachineData);
6879 mHWData->mDefaultFrontend = aDefaultFrontend;
6880 }
6881 }
6882 return hrc;
6883}
6884
6885HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6886{
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888 size_t cbIcon = mUserData->s.ovIcon.size();
6889 aIcon.resize(cbIcon);
6890 if (cbIcon)
6891 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6892 return S_OK;
6893}
6894
6895HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6896{
6897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6898 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6899 if (SUCCEEDED(hrc))
6900 {
6901 i_setModified(IsModified_MachineData);
6902 mUserData.backup();
6903 size_t cbIcon = aIcon.size();
6904 mUserData->s.ovIcon.resize(cbIcon);
6905 if (cbIcon)
6906 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6907 }
6908 return hrc;
6909}
6910
6911HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6912{
6913#ifdef VBOX_WITH_USB
6914 *aUSBProxyAvailable = true;
6915#else
6916 *aUSBProxyAvailable = false;
6917#endif
6918 return S_OK;
6919}
6920
6921HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6922{
6923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6924
6925 *aVMProcessPriority = mUserData->s.enmVMPriority;
6926
6927 return S_OK;
6928}
6929
6930HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6931{
6932 RT_NOREF(aVMProcessPriority);
6933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6934 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6935 if (SUCCEEDED(hrc))
6936 {
6937 hrc = mUserData.backupEx();
6938 if (SUCCEEDED(hrc))
6939 {
6940 i_setModified(IsModified_MachineData);
6941 mUserData->s.enmVMPriority = aVMProcessPriority;
6942 }
6943 }
6944 alock.release();
6945 if (SUCCEEDED(hrc))
6946 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6947 return hrc;
6948}
6949
6950HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6951 ComPtr<IProgress> &aProgress)
6952{
6953 ComObjPtr<Progress> pP;
6954 Progress *ppP = pP;
6955 IProgress *iP = static_cast<IProgress *>(ppP);
6956 IProgress **pProgress = &iP;
6957
6958 IMachine *pTarget = aTarget;
6959
6960 /* Convert the options. */
6961 RTCList<CloneOptions_T> optList;
6962 if (aOptions.size())
6963 for (size_t i = 0; i < aOptions.size(); ++i)
6964 optList.append(aOptions[i]);
6965
6966 if (optList.contains(CloneOptions_Link))
6967 {
6968 if (!i_isSnapshotMachine())
6969 return setError(E_INVALIDARG,
6970 tr("Linked clone can only be created from a snapshot"));
6971 if (aMode != CloneMode_MachineState)
6972 return setError(E_INVALIDARG,
6973 tr("Linked clone can only be created for a single machine state"));
6974 }
6975 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6976
6977 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6978
6979 HRESULT rc = pWorker->start(pProgress);
6980
6981 pP = static_cast<Progress *>(*pProgress);
6982 pP.queryInterfaceTo(aProgress.asOutParam());
6983
6984 return rc;
6985
6986}
6987
6988HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6989 const com::Utf8Str &aType,
6990 ComPtr<IProgress> &aProgress)
6991{
6992 LogFlowThisFuncEnter();
6993
6994 ComObjPtr<Progress> ptrProgress;
6995 HRESULT hrc = ptrProgress.createObject();
6996 if (SUCCEEDED(hrc))
6997 {
6998 com::Utf8Str strDefaultPath;
6999 if (aTargetPath.isEmpty())
7000 i_calculateFullPath(".", strDefaultPath);
7001
7002 /* Initialize our worker task */
7003 MachineMoveVM *pTask = NULL;
7004 try
7005 {
7006 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7007 }
7008 catch (std::bad_alloc &)
7009 {
7010 return E_OUTOFMEMORY;
7011 }
7012
7013 hrc = pTask->init();//no exceptions are thrown
7014
7015 if (SUCCEEDED(hrc))
7016 {
7017 hrc = pTask->createThread();
7018 pTask = NULL; /* Consumed by createThread(). */
7019 if (SUCCEEDED(hrc))
7020 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7021 else
7022 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7023 }
7024 else
7025 delete pTask;
7026 }
7027
7028 LogFlowThisFuncLeave();
7029 return hrc;
7030
7031}
7032
7033HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7034{
7035 NOREF(aProgress);
7036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7037
7038 // This check should always fail.
7039 HRESULT rc = i_checkStateDependency(MutableStateDep);
7040 if (FAILED(rc)) return rc;
7041
7042 AssertFailedReturn(E_NOTIMPL);
7043}
7044
7045HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7046{
7047 NOREF(aSavedStateFile);
7048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7049
7050 // This check should always fail.
7051 HRESULT rc = i_checkStateDependency(MutableStateDep);
7052 if (FAILED(rc)) return rc;
7053
7054 AssertFailedReturn(E_NOTIMPL);
7055}
7056
7057HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7058{
7059 NOREF(aFRemoveFile);
7060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7061
7062 // This check should always fail.
7063 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7064 if (FAILED(rc)) return rc;
7065
7066 AssertFailedReturn(E_NOTIMPL);
7067}
7068
7069// public methods for internal purposes
7070/////////////////////////////////////////////////////////////////////////////
7071
7072/**
7073 * Adds the given IsModified_* flag to the dirty flags of the machine.
7074 * This must be called either during i_loadSettings or under the machine write lock.
7075 * @param fl Flag
7076 * @param fAllowStateModification If state modifications are allowed.
7077 */
7078void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7079{
7080 mData->flModifications |= fl;
7081 if (fAllowStateModification && i_isStateModificationAllowed())
7082 mData->mCurrentStateModified = true;
7083}
7084
7085/**
7086 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7087 * care of the write locking.
7088 *
7089 * @param fModification The flag to add.
7090 * @param fAllowStateModification If state modifications are allowed.
7091 */
7092void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7093{
7094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7095 i_setModified(fModification, fAllowStateModification);
7096}
7097
7098/**
7099 * Saves the registry entry of this machine to the given configuration node.
7100 *
7101 * @param data Machine registry data.
7102 *
7103 * @note locks this object for reading.
7104 */
7105HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7106{
7107 AutoLimitedCaller autoCaller(this);
7108 AssertComRCReturnRC(autoCaller.rc());
7109
7110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7111
7112 data.uuid = mData->mUuid;
7113 data.strSettingsFile = mData->m_strConfigFile;
7114
7115 return S_OK;
7116}
7117
7118/**
7119 * Calculates the absolute path of the given path taking the directory of the
7120 * machine settings file as the current directory.
7121 *
7122 * @param strPath Path to calculate the absolute path for.
7123 * @param aResult Where to put the result (used only on success, can be the
7124 * same Utf8Str instance as passed in @a aPath).
7125 * @return IPRT result.
7126 *
7127 * @note Locks this object for reading.
7128 */
7129int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7130{
7131 AutoCaller autoCaller(this);
7132 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7133
7134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7135
7136 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7137
7138 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7139
7140 strSettingsDir.stripFilename();
7141 char szFolder[RTPATH_MAX];
7142 size_t cbFolder = sizeof(szFolder);
7143 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7144 if (RT_SUCCESS(vrc))
7145 aResult = szFolder;
7146
7147 return vrc;
7148}
7149
7150/**
7151 * Copies strSource to strTarget, making it relative to the machine folder
7152 * if it is a subdirectory thereof, or simply copying it otherwise.
7153 *
7154 * @param strSource Path to evaluate and copy.
7155 * @param strTarget Buffer to receive target path.
7156 *
7157 * @note Locks this object for reading.
7158 */
7159void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7160 Utf8Str &strTarget)
7161{
7162 AutoCaller autoCaller(this);
7163 AssertComRCReturn(autoCaller.rc(), (void)0);
7164
7165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7166
7167 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7168 // use strTarget as a temporary buffer to hold the machine settings dir
7169 strTarget = mData->m_strConfigFileFull;
7170 strTarget.stripFilename();
7171 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7172 {
7173 // is relative: then append what's left
7174 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7175 // for empty paths (only possible for subdirs) use "." to avoid
7176 // triggering default settings for not present config attributes.
7177 if (strTarget.isEmpty())
7178 strTarget = ".";
7179 }
7180 else
7181 // is not relative: then overwrite
7182 strTarget = strSource;
7183}
7184
7185/**
7186 * Returns the full path to the machine's log folder in the
7187 * \a aLogFolder argument.
7188 */
7189void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7190{
7191 AutoCaller autoCaller(this);
7192 AssertComRCReturnVoid(autoCaller.rc());
7193
7194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7195
7196 char szTmp[RTPATH_MAX];
7197 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7198 if (RT_SUCCESS(vrc))
7199 {
7200 if (szTmp[0] && !mUserData.isNull())
7201 {
7202 char szTmp2[RTPATH_MAX];
7203 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7204 if (RT_SUCCESS(vrc))
7205 aLogFolder.printf("%s%c%s",
7206 szTmp2,
7207 RTPATH_DELIMITER,
7208 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7209 }
7210 else
7211 vrc = VERR_PATH_IS_RELATIVE;
7212 }
7213
7214 if (RT_FAILURE(vrc))
7215 {
7216 // fallback if VBOX_USER_LOGHOME is not set or invalid
7217 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7218 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7219 aLogFolder.append(RTPATH_DELIMITER);
7220 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7221 }
7222}
7223
7224/**
7225 * Returns the full path to the machine's log file for an given index.
7226 */
7227Utf8Str Machine::i_getLogFilename(ULONG idx)
7228{
7229 Utf8Str logFolder;
7230 getLogFolder(logFolder);
7231 Assert(logFolder.length());
7232
7233 Utf8Str log;
7234 if (idx == 0)
7235 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7236#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7237 else if (idx == 1)
7238 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7239 else
7240 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7241#else
7242 else
7243 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7244#endif
7245 return log;
7246}
7247
7248/**
7249 * Returns the full path to the machine's hardened log file.
7250 */
7251Utf8Str Machine::i_getHardeningLogFilename(void)
7252{
7253 Utf8Str strFilename;
7254 getLogFolder(strFilename);
7255 Assert(strFilename.length());
7256 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7257 return strFilename;
7258}
7259
7260/**
7261 * Returns the default NVRAM filename based on the location of the VM config.
7262 * Note that this is a relative path.
7263 */
7264Utf8Str Machine::i_getDefaultNVRAMFilename()
7265{
7266 AutoCaller autoCaller(this);
7267 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7268
7269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7270
7271 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7272 || i_isSnapshotMachine())
7273 return Utf8Str::Empty;
7274
7275 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7276 strNVRAMFilePath.stripPath();
7277 strNVRAMFilePath.stripSuffix();
7278 strNVRAMFilePath += ".nvram";
7279
7280 return strNVRAMFilePath;
7281}
7282
7283/**
7284 * Returns the NVRAM filename for a new snapshot. This intentionally works
7285 * similarly to the saved state file naming. Note that this is usually
7286 * a relative path, unless the snapshot folder is absolute.
7287 */
7288Utf8Str Machine::i_getSnapshotNVRAMFilename()
7289{
7290 AutoCaller autoCaller(this);
7291 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7292
7293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7294
7295 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7296 return Utf8Str::Empty;
7297
7298 RTTIMESPEC ts;
7299 RTTimeNow(&ts);
7300 RTTIME time;
7301 RTTimeExplode(&time, &ts);
7302
7303 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7304 strNVRAMFilePath += RTPATH_DELIMITER;
7305 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7306 time.i32Year, time.u8Month, time.u8MonthDay,
7307 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7308
7309 return strNVRAMFilePath;
7310}
7311
7312/**
7313 * Returns the version of the settings file.
7314 */
7315SettingsVersion_T Machine::i_getSettingsVersion(void)
7316{
7317 AutoCaller autoCaller(this);
7318 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7319
7320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7321
7322 return mData->pMachineConfigFile->getSettingsVersion();
7323}
7324
7325/**
7326 * Composes a unique saved state filename based on the current system time. The filename is
7327 * granular to the second so this will work so long as no more than one snapshot is taken on
7328 * a machine per second.
7329 *
7330 * Before version 4.1, we used this formula for saved state files:
7331 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7332 * which no longer works because saved state files can now be shared between the saved state of the
7333 * "saved" machine and an online snapshot, and the following would cause problems:
7334 * 1) save machine
7335 * 2) create online snapshot from that machine state --> reusing saved state file
7336 * 3) save machine again --> filename would be reused, breaking the online snapshot
7337 *
7338 * So instead we now use a timestamp.
7339 *
7340 * @param strStateFilePath
7341 */
7342
7343void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7344{
7345 AutoCaller autoCaller(this);
7346 AssertComRCReturnVoid(autoCaller.rc());
7347
7348 {
7349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7350 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7351 }
7352
7353 RTTIMESPEC ts;
7354 RTTimeNow(&ts);
7355 RTTIME time;
7356 RTTimeExplode(&time, &ts);
7357
7358 strStateFilePath += RTPATH_DELIMITER;
7359 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7360 time.i32Year, time.u8Month, time.u8MonthDay,
7361 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7362}
7363
7364/**
7365 * Returns whether at least one USB controller is present for the VM.
7366 */
7367bool Machine::i_isUSBControllerPresent()
7368{
7369 AutoCaller autoCaller(this);
7370 AssertComRCReturn(autoCaller.rc(), false);
7371
7372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7373
7374 return (mUSBControllers->size() > 0);
7375}
7376
7377
7378/**
7379 * @note Locks this object for writing, calls the client process
7380 * (inside the lock).
7381 */
7382HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7383 const Utf8Str &strFrontend,
7384 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7385 ProgressProxy *aProgress)
7386{
7387 LogFlowThisFuncEnter();
7388
7389 AssertReturn(aControl, E_FAIL);
7390 AssertReturn(aProgress, E_FAIL);
7391 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7392
7393 AutoCaller autoCaller(this);
7394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7395
7396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7397
7398 if (!mData->mRegistered)
7399 return setError(E_UNEXPECTED,
7400 tr("The machine '%s' is not registered"),
7401 mUserData->s.strName.c_str());
7402
7403 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7404
7405 /* The process started when launching a VM with separate UI/VM processes is always
7406 * the UI process, i.e. needs special handling as it won't claim the session. */
7407 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7408
7409 if (fSeparate)
7410 {
7411 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7412 return setError(VBOX_E_INVALID_OBJECT_STATE,
7413 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7414 mUserData->s.strName.c_str());
7415 }
7416 else
7417 {
7418 if ( mData->mSession.mState == SessionState_Locked
7419 || mData->mSession.mState == SessionState_Spawning
7420 || mData->mSession.mState == SessionState_Unlocking)
7421 return setError(VBOX_E_INVALID_OBJECT_STATE,
7422 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7423 mUserData->s.strName.c_str());
7424
7425 /* may not be busy */
7426 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7427 }
7428
7429 /* Hardening logging */
7430#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7431 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7432 {
7433 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7434 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7435 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7436 {
7437 Utf8Str strStartupLogDir = strHardeningLogFile;
7438 strStartupLogDir.stripFilename();
7439 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7440 file without stripping the file. */
7441 }
7442 strSupHardeningLogArg.append(strHardeningLogFile);
7443
7444 /* Remove legacy log filename to avoid confusion. */
7445 Utf8Str strOldStartupLogFile;
7446 getLogFolder(strOldStartupLogFile);
7447 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7448 RTFileDelete(strOldStartupLogFile.c_str());
7449 }
7450#else
7451 Utf8Str strSupHardeningLogArg;
7452#endif
7453
7454 Utf8Str strAppOverride;
7455#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7456 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7457#endif
7458
7459 bool fUseVBoxSDS = false;
7460 Utf8Str strCanonicalName;
7461 if (false)
7462 { }
7463#ifdef VBOX_WITH_QTGUI
7464 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7465 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7466 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7467 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7468 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7469 {
7470 strCanonicalName = "GUI/Qt";
7471 fUseVBoxSDS = true;
7472 }
7473#endif
7474#ifdef VBOX_WITH_VBOXSDL
7475 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7476 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7477 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7478 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7479 {
7480 strCanonicalName = "GUI/SDL";
7481 fUseVBoxSDS = true;
7482 }
7483#endif
7484#ifdef VBOX_WITH_HEADLESS
7485 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7486 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7487 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7488 {
7489 strCanonicalName = "headless";
7490 }
7491#endif
7492 else
7493 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7494
7495 Utf8Str idStr = mData->mUuid.toString();
7496 Utf8Str const &strMachineName = mUserData->s.strName;
7497 RTPROCESS pid = NIL_RTPROCESS;
7498
7499#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7500 RT_NOREF(fUseVBoxSDS);
7501#else
7502 DWORD idCallerSession = ~(DWORD)0;
7503 if (fUseVBoxSDS)
7504 {
7505 /*
7506 * The VBoxSDS should be used for process launching the VM with
7507 * GUI only if the caller and the VBoxSDS are in different Windows
7508 * sessions and the caller in the interactive one.
7509 */
7510 fUseVBoxSDS = false;
7511
7512 /* Get windows session of the current process. The process token used
7513 due to several reasons:
7514 1. The token is absent for the current thread except someone set it
7515 for us.
7516 2. Needs to get the id of the session where the process is started.
7517 We only need to do this once, though. */
7518 static DWORD s_idCurrentSession = ~(DWORD)0;
7519 DWORD idCurrentSession = s_idCurrentSession;
7520 if (idCurrentSession == ~(DWORD)0)
7521 {
7522 HANDLE hCurrentProcessToken = NULL;
7523 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7524 {
7525 DWORD cbIgn = 0;
7526 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7527 s_idCurrentSession = idCurrentSession;
7528 else
7529 {
7530 idCurrentSession = ~(DWORD)0;
7531 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7532 }
7533 CloseHandle(hCurrentProcessToken);
7534 }
7535 else
7536 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7537 }
7538
7539 /* get the caller's session */
7540 HRESULT hrc = CoImpersonateClient();
7541 if (SUCCEEDED(hrc))
7542 {
7543 HANDLE hCallerThreadToken;
7544 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7545 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7546 &hCallerThreadToken))
7547 {
7548 SetLastError(NO_ERROR);
7549 DWORD cbIgn = 0;
7550 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7551 {
7552 /* Only need to use SDS if the session ID differs: */
7553 if (idCurrentSession != idCallerSession)
7554 {
7555 fUseVBoxSDS = false;
7556
7557 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7558 DWORD cbTokenGroups = 0;
7559 PTOKEN_GROUPS pTokenGroups = NULL;
7560 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7561 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7562 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7563 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7564 {
7565 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7566 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7567 PSID pInteractiveSid = NULL;
7568 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7569 {
7570 /* Iterate over the groups looking for the interactive SID: */
7571 fUseVBoxSDS = false;
7572 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7573 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7574 {
7575 fUseVBoxSDS = true;
7576 break;
7577 }
7578 FreeSid(pInteractiveSid);
7579 }
7580 }
7581 else
7582 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7583 RTMemTmpFree(pTokenGroups);
7584 }
7585 }
7586 else
7587 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7588 CloseHandle(hCallerThreadToken);
7589 }
7590 else
7591 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7592 CoRevertToSelf();
7593 }
7594 else
7595 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7596 }
7597 if (fUseVBoxSDS)
7598 {
7599 /* connect to VBoxSDS */
7600 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7601 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7602 if (FAILED(rc))
7603 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7604 strMachineName.c_str());
7605
7606 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7607 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7608 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7609 service to access the files. */
7610 rc = CoSetProxyBlanket(pVBoxSDS,
7611 RPC_C_AUTHN_DEFAULT,
7612 RPC_C_AUTHZ_DEFAULT,
7613 COLE_DEFAULT_PRINCIPAL,
7614 RPC_C_AUTHN_LEVEL_DEFAULT,
7615 RPC_C_IMP_LEVEL_IMPERSONATE,
7616 NULL,
7617 EOAC_DEFAULT);
7618 if (FAILED(rc))
7619 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7620
7621 size_t const cEnvVars = aEnvironmentChanges.size();
7622 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7623 for (size_t i = 0; i < cEnvVars; i++)
7624 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7625
7626 ULONG uPid = 0;
7627 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7628 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7629 idCallerSession, &uPid);
7630 if (FAILED(rc))
7631 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7632 pid = (RTPROCESS)uPid;
7633 }
7634 else
7635#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7636 {
7637 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7638 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7639 if (RT_FAILURE(vrc))
7640 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7641 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7642 }
7643
7644 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7645 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7646
7647 if (!fSeparate)
7648 {
7649 /*
7650 * Note that we don't release the lock here before calling the client,
7651 * because it doesn't need to call us back if called with a NULL argument.
7652 * Releasing the lock here is dangerous because we didn't prepare the
7653 * launch data yet, but the client we've just started may happen to be
7654 * too fast and call LockMachine() that will fail (because of PID, etc.),
7655 * so that the Machine will never get out of the Spawning session state.
7656 */
7657
7658 /* inform the session that it will be a remote one */
7659 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7660#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7661 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7662#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7663 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7664#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7665 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7666
7667 if (FAILED(rc))
7668 {
7669 /* restore the session state */
7670 mData->mSession.mState = SessionState_Unlocked;
7671 alock.release();
7672 mParent->i_addProcessToReap(pid);
7673 /* The failure may occur w/o any error info (from RPC), so provide one */
7674 return setError(VBOX_E_VM_ERROR,
7675 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7676 }
7677
7678 /* attach launch data to the machine */
7679 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7680 mData->mSession.mRemoteControls.push_back(aControl);
7681 mData->mSession.mProgress = aProgress;
7682 mData->mSession.mPID = pid;
7683 mData->mSession.mState = SessionState_Spawning;
7684 Assert(strCanonicalName.isNotEmpty());
7685 mData->mSession.mName = strCanonicalName;
7686 }
7687 else
7688 {
7689 /* For separate UI process we declare the launch as completed instantly, as the
7690 * actual headless VM start may or may not come. No point in remembering anything
7691 * yet, as what matters for us is when the headless VM gets started. */
7692 aProgress->i_notifyComplete(S_OK);
7693 }
7694
7695 alock.release();
7696 mParent->i_addProcessToReap(pid);
7697
7698 LogFlowThisFuncLeave();
7699 return S_OK;
7700}
7701
7702/**
7703 * Returns @c true if the given session machine instance has an open direct
7704 * session (and optionally also for direct sessions which are closing) and
7705 * returns the session control machine instance if so.
7706 *
7707 * Note that when the method returns @c false, the arguments remain unchanged.
7708 *
7709 * @param aMachine Session machine object.
7710 * @param aControl Direct session control object (optional).
7711 * @param aRequireVM If true then only allow VM sessions.
7712 * @param aAllowClosing If true then additionally a session which is currently
7713 * being closed will also be allowed.
7714 *
7715 * @note locks this object for reading.
7716 */
7717bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7718 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7719 bool aRequireVM /*= false*/,
7720 bool aAllowClosing /*= false*/)
7721{
7722 AutoLimitedCaller autoCaller(this);
7723 AssertComRCReturn(autoCaller.rc(), false);
7724
7725 /* just return false for inaccessible machines */
7726 if (getObjectState().getState() != ObjectState::Ready)
7727 return false;
7728
7729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7730
7731 if ( ( mData->mSession.mState == SessionState_Locked
7732 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7733 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7734 )
7735 {
7736 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7737
7738 aMachine = mData->mSession.mMachine;
7739
7740 if (aControl != NULL)
7741 *aControl = mData->mSession.mDirectControl;
7742
7743 return true;
7744 }
7745
7746 return false;
7747}
7748
7749/**
7750 * Returns @c true if the given machine has an spawning direct session.
7751 *
7752 * @note locks this object for reading.
7753 */
7754bool Machine::i_isSessionSpawning()
7755{
7756 AutoLimitedCaller autoCaller(this);
7757 AssertComRCReturn(autoCaller.rc(), false);
7758
7759 /* just return false for inaccessible machines */
7760 if (getObjectState().getState() != ObjectState::Ready)
7761 return false;
7762
7763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7764
7765 if (mData->mSession.mState == SessionState_Spawning)
7766 return true;
7767
7768 return false;
7769}
7770
7771/**
7772 * Called from the client watcher thread to check for unexpected client process
7773 * death during Session_Spawning state (e.g. before it successfully opened a
7774 * direct session).
7775 *
7776 * On Win32 and on OS/2, this method is called only when we've got the
7777 * direct client's process termination notification, so it always returns @c
7778 * true.
7779 *
7780 * On other platforms, this method returns @c true if the client process is
7781 * terminated and @c false if it's still alive.
7782 *
7783 * @note Locks this object for writing.
7784 */
7785bool Machine::i_checkForSpawnFailure()
7786{
7787 AutoCaller autoCaller(this);
7788 if (!autoCaller.isOk())
7789 {
7790 /* nothing to do */
7791 LogFlowThisFunc(("Already uninitialized!\n"));
7792 return true;
7793 }
7794
7795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7796
7797 if (mData->mSession.mState != SessionState_Spawning)
7798 {
7799 /* nothing to do */
7800 LogFlowThisFunc(("Not spawning any more!\n"));
7801 return true;
7802 }
7803
7804 HRESULT rc = S_OK;
7805
7806 /* PID not yet initialized, skip check. */
7807 if (mData->mSession.mPID == NIL_RTPROCESS)
7808 return false;
7809
7810 RTPROCSTATUS status;
7811 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7812
7813 if (vrc != VERR_PROCESS_RUNNING)
7814 {
7815 Utf8Str strExtraInfo;
7816
7817#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7818 /* If the startup logfile exists and is of non-zero length, tell the
7819 user to look there for more details to encourage them to attach it
7820 when reporting startup issues. */
7821 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7822 uint64_t cbStartupLogFile = 0;
7823 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7824 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7825 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7826#endif
7827
7828 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7829 rc = setError(E_FAIL,
7830 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7831 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7832 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7833 rc = setError(E_FAIL,
7834 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7835 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7836 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7837 rc = setError(E_FAIL,
7838 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7839 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7840 else
7841 rc = setErrorBoth(E_FAIL, vrc,
7842 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7843 i_getName().c_str(), vrc, strExtraInfo.c_str());
7844 }
7845
7846 if (FAILED(rc))
7847 {
7848 /* Close the remote session, remove the remote control from the list
7849 * and reset session state to Closed (@note keep the code in sync with
7850 * the relevant part in LockMachine()). */
7851
7852 Assert(mData->mSession.mRemoteControls.size() == 1);
7853 if (mData->mSession.mRemoteControls.size() == 1)
7854 {
7855 ErrorInfoKeeper eik;
7856 mData->mSession.mRemoteControls.front()->Uninitialize();
7857 }
7858
7859 mData->mSession.mRemoteControls.clear();
7860 mData->mSession.mState = SessionState_Unlocked;
7861
7862 /* finalize the progress after setting the state */
7863 if (!mData->mSession.mProgress.isNull())
7864 {
7865 mData->mSession.mProgress->notifyComplete(rc);
7866 mData->mSession.mProgress.setNull();
7867 }
7868
7869 mData->mSession.mPID = NIL_RTPROCESS;
7870
7871 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7872 return true;
7873 }
7874
7875 return false;
7876}
7877
7878/**
7879 * Checks whether the machine can be registered. If so, commits and saves
7880 * all settings.
7881 *
7882 * @note Must be called from mParent's write lock. Locks this object and
7883 * children for writing.
7884 */
7885HRESULT Machine::i_prepareRegister()
7886{
7887 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7888
7889 AutoLimitedCaller autoCaller(this);
7890 AssertComRCReturnRC(autoCaller.rc());
7891
7892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7893
7894 /* wait for state dependents to drop to zero */
7895 i_ensureNoStateDependencies(alock);
7896
7897 if (!mData->mAccessible)
7898 return setError(VBOX_E_INVALID_OBJECT_STATE,
7899 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7900 mUserData->s.strName.c_str(),
7901 mData->mUuid.toString().c_str());
7902
7903 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7904
7905 if (mData->mRegistered)
7906 return setError(VBOX_E_INVALID_OBJECT_STATE,
7907 tr("The machine '%s' with UUID {%s} is already registered"),
7908 mUserData->s.strName.c_str(),
7909 mData->mUuid.toString().c_str());
7910
7911 HRESULT rc = S_OK;
7912
7913 // Ensure the settings are saved. If we are going to be registered and
7914 // no config file exists yet, create it by calling i_saveSettings() too.
7915 if ( (mData->flModifications)
7916 || (!mData->pMachineConfigFile->fileExists())
7917 )
7918 {
7919 rc = i_saveSettings(NULL, alock);
7920 // no need to check whether VirtualBox.xml needs saving too since
7921 // we can't have a machine XML file rename pending
7922 if (FAILED(rc)) return rc;
7923 }
7924
7925 /* more config checking goes here */
7926
7927 if (SUCCEEDED(rc))
7928 {
7929 /* we may have had implicit modifications we want to fix on success */
7930 i_commit();
7931
7932 mData->mRegistered = true;
7933 }
7934 else
7935 {
7936 /* we may have had implicit modifications we want to cancel on failure*/
7937 i_rollback(false /* aNotify */);
7938 }
7939
7940 return rc;
7941}
7942
7943/**
7944 * Increases the number of objects dependent on the machine state or on the
7945 * registered state. Guarantees that these two states will not change at least
7946 * until #i_releaseStateDependency() is called.
7947 *
7948 * Depending on the @a aDepType value, additional state checks may be made.
7949 * These checks will set extended error info on failure. See
7950 * #i_checkStateDependency() for more info.
7951 *
7952 * If this method returns a failure, the dependency is not added and the caller
7953 * is not allowed to rely on any particular machine state or registration state
7954 * value and may return the failed result code to the upper level.
7955 *
7956 * @param aDepType Dependency type to add.
7957 * @param aState Current machine state (NULL if not interested).
7958 * @param aRegistered Current registered state (NULL if not interested).
7959 *
7960 * @note Locks this object for writing.
7961 */
7962HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7963 MachineState_T *aState /* = NULL */,
7964 BOOL *aRegistered /* = NULL */)
7965{
7966 AutoCaller autoCaller(this);
7967 AssertComRCReturnRC(autoCaller.rc());
7968
7969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7970
7971 HRESULT rc = i_checkStateDependency(aDepType);
7972 if (FAILED(rc)) return rc;
7973
7974 {
7975 if (mData->mMachineStateChangePending != 0)
7976 {
7977 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7978 * drop to zero so don't add more. It may make sense to wait a bit
7979 * and retry before reporting an error (since the pending state
7980 * transition should be really quick) but let's just assert for
7981 * now to see if it ever happens on practice. */
7982
7983 AssertFailed();
7984
7985 return setError(E_ACCESSDENIED,
7986 tr("Machine state change is in progress. Please retry the operation later."));
7987 }
7988
7989 ++mData->mMachineStateDeps;
7990 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7991 }
7992
7993 if (aState)
7994 *aState = mData->mMachineState;
7995 if (aRegistered)
7996 *aRegistered = mData->mRegistered;
7997
7998 return S_OK;
7999}
8000
8001/**
8002 * Decreases the number of objects dependent on the machine state.
8003 * Must always complete the #i_addStateDependency() call after the state
8004 * dependency is no more necessary.
8005 */
8006void Machine::i_releaseStateDependency()
8007{
8008 AutoCaller autoCaller(this);
8009 AssertComRCReturnVoid(autoCaller.rc());
8010
8011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8012
8013 /* releaseStateDependency() w/o addStateDependency()? */
8014 AssertReturnVoid(mData->mMachineStateDeps != 0);
8015 -- mData->mMachineStateDeps;
8016
8017 if (mData->mMachineStateDeps == 0)
8018 {
8019 /* inform i_ensureNoStateDependencies() that there are no more deps */
8020 if (mData->mMachineStateChangePending != 0)
8021 {
8022 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8023 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8024 }
8025 }
8026}
8027
8028Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8029{
8030 /* start with nothing found */
8031 Utf8Str strResult("");
8032
8033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8034
8035 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8036 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8037 // found:
8038 strResult = it->second; // source is a Utf8Str
8039
8040 return strResult;
8041}
8042
8043// protected methods
8044/////////////////////////////////////////////////////////////////////////////
8045
8046/**
8047 * Performs machine state checks based on the @a aDepType value. If a check
8048 * fails, this method will set extended error info, otherwise it will return
8049 * S_OK. It is supposed, that on failure, the caller will immediately return
8050 * the return value of this method to the upper level.
8051 *
8052 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8053 *
8054 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8055 * current state of this machine object allows to change settings of the
8056 * machine (i.e. the machine is not registered, or registered but not running
8057 * and not saved). It is useful to call this method from Machine setters
8058 * before performing any change.
8059 *
8060 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8061 * as for MutableStateDep except that if the machine is saved, S_OK is also
8062 * returned. This is useful in setters which allow changing machine
8063 * properties when it is in the saved state.
8064 *
8065 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8066 * if the current state of this machine object allows to change runtime
8067 * changeable settings of the machine (i.e. the machine is not registered, or
8068 * registered but either running or not running and not saved). It is useful
8069 * to call this method from Machine setters before performing any changes to
8070 * runtime changeable settings.
8071 *
8072 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8073 * the same as for MutableOrRunningStateDep except that if the machine is
8074 * saved, S_OK is also returned. This is useful in setters which allow
8075 * changing runtime and saved state changeable machine properties.
8076 *
8077 * @param aDepType Dependency type to check.
8078 *
8079 * @note Non Machine based classes should use #i_addStateDependency() and
8080 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8081 * template.
8082 *
8083 * @note This method must be called from under this object's read or write
8084 * lock.
8085 */
8086HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8087{
8088 switch (aDepType)
8089 {
8090 case AnyStateDep:
8091 {
8092 break;
8093 }
8094 case MutableStateDep:
8095 {
8096 if ( mData->mRegistered
8097 && ( !i_isSessionMachine()
8098 || ( mData->mMachineState != MachineState_Aborted
8099 && mData->mMachineState != MachineState_Teleported
8100 && mData->mMachineState != MachineState_PoweredOff
8101 )
8102 )
8103 )
8104 return setError(VBOX_E_INVALID_VM_STATE,
8105 tr("The machine is not mutable (state is %s)"),
8106 Global::stringifyMachineState(mData->mMachineState));
8107 break;
8108 }
8109 case MutableOrSavedStateDep:
8110 {
8111 if ( mData->mRegistered
8112 && ( !i_isSessionMachine()
8113 || ( mData->mMachineState != MachineState_Aborted
8114 && mData->mMachineState != MachineState_Teleported
8115 && mData->mMachineState != MachineState_Saved
8116 && mData->mMachineState != MachineState_AbortedSaved
8117 && mData->mMachineState != MachineState_PoweredOff
8118 )
8119 )
8120 )
8121 return setError(VBOX_E_INVALID_VM_STATE,
8122 tr("The machine is not mutable or saved (state is %s)"),
8123 Global::stringifyMachineState(mData->mMachineState));
8124 break;
8125 }
8126 case MutableOrRunningStateDep:
8127 {
8128 if ( mData->mRegistered
8129 && ( !i_isSessionMachine()
8130 || ( mData->mMachineState != MachineState_Aborted
8131 && mData->mMachineState != MachineState_Teleported
8132 && mData->mMachineState != MachineState_PoweredOff
8133 && !Global::IsOnline(mData->mMachineState)
8134 )
8135 )
8136 )
8137 return setError(VBOX_E_INVALID_VM_STATE,
8138 tr("The machine is not mutable or running (state is %s)"),
8139 Global::stringifyMachineState(mData->mMachineState));
8140 break;
8141 }
8142 case MutableOrSavedOrRunningStateDep:
8143 {
8144 if ( mData->mRegistered
8145 && ( !i_isSessionMachine()
8146 || ( mData->mMachineState != MachineState_Aborted
8147 && mData->mMachineState != MachineState_Teleported
8148 && mData->mMachineState != MachineState_Saved
8149 && mData->mMachineState != MachineState_AbortedSaved
8150 && mData->mMachineState != MachineState_PoweredOff
8151 && !Global::IsOnline(mData->mMachineState)
8152 )
8153 )
8154 )
8155 return setError(VBOX_E_INVALID_VM_STATE,
8156 tr("The machine is not mutable, saved or running (state is %s)"),
8157 Global::stringifyMachineState(mData->mMachineState));
8158 break;
8159 }
8160 }
8161
8162 return S_OK;
8163}
8164
8165/**
8166 * Helper to initialize all associated child objects and allocate data
8167 * structures.
8168 *
8169 * This method must be called as a part of the object's initialization procedure
8170 * (usually done in the #init() method).
8171 *
8172 * @note Must be called only from #init() or from #i_registeredInit().
8173 */
8174HRESULT Machine::initDataAndChildObjects()
8175{
8176 AutoCaller autoCaller(this);
8177 AssertComRCReturnRC(autoCaller.rc());
8178 AssertReturn( getObjectState().getState() == ObjectState::InInit
8179 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8180
8181 AssertReturn(!mData->mAccessible, E_FAIL);
8182
8183 /* allocate data structures */
8184 mSSData.allocate();
8185 mUserData.allocate();
8186 mHWData.allocate();
8187 mMediumAttachments.allocate();
8188 mStorageControllers.allocate();
8189 mUSBControllers.allocate();
8190
8191 /* initialize mOSTypeId */
8192 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8193
8194/** @todo r=bird: init() methods never fails, right? Why don't we make them
8195 * return void then! */
8196
8197 /* create associated BIOS settings object */
8198 unconst(mBIOSSettings).createObject();
8199 mBIOSSettings->init(this);
8200
8201 /* create associated trusted platform module object */
8202 unconst(mTrustedPlatformModule).createObject();
8203 mTrustedPlatformModule->init(this);
8204
8205 /* create associated NVRAM store object */
8206 unconst(mNvramStore).createObject();
8207 mNvramStore->init(this);
8208
8209 /* create associated record settings object */
8210 unconst(mRecordingSettings).createObject();
8211 mRecordingSettings->init(this);
8212
8213 /* create the graphics adapter object (always present) */
8214 unconst(mGraphicsAdapter).createObject();
8215 mGraphicsAdapter->init(this);
8216
8217 /* create an associated VRDE object (default is disabled) */
8218 unconst(mVRDEServer).createObject();
8219 mVRDEServer->init(this);
8220
8221 /* create associated serial port objects */
8222 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8223 {
8224 unconst(mSerialPorts[slot]).createObject();
8225 mSerialPorts[slot]->init(this, slot);
8226 }
8227
8228 /* create associated parallel port objects */
8229 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8230 {
8231 unconst(mParallelPorts[slot]).createObject();
8232 mParallelPorts[slot]->init(this, slot);
8233 }
8234
8235 /* create the audio adapter object (always present, default is disabled) */
8236 unconst(mAudioAdapter).createObject();
8237 mAudioAdapter->init(this);
8238
8239 /* create the USB device filters object (always present) */
8240 unconst(mUSBDeviceFilters).createObject();
8241 mUSBDeviceFilters->init(this);
8242
8243 /* create associated network adapter objects */
8244 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8245 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8246 {
8247 unconst(mNetworkAdapters[slot]).createObject();
8248 mNetworkAdapters[slot]->init(this, slot);
8249 }
8250
8251 /* create the bandwidth control */
8252 unconst(mBandwidthControl).createObject();
8253 mBandwidthControl->init(this);
8254
8255 return S_OK;
8256}
8257
8258/**
8259 * Helper to uninitialize all associated child objects and to free all data
8260 * structures.
8261 *
8262 * This method must be called as a part of the object's uninitialization
8263 * procedure (usually done in the #uninit() method).
8264 *
8265 * @note Must be called only from #uninit() or from #i_registeredInit().
8266 */
8267void Machine::uninitDataAndChildObjects()
8268{
8269 AutoCaller autoCaller(this);
8270 AssertComRCReturnVoid(autoCaller.rc());
8271 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8272 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8273 || getObjectState().getState() == ObjectState::InUninit
8274 || getObjectState().getState() == ObjectState::Limited);
8275
8276 /* tell all our other child objects we've been uninitialized */
8277 if (mBandwidthControl)
8278 {
8279 mBandwidthControl->uninit();
8280 unconst(mBandwidthControl).setNull();
8281 }
8282
8283 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8284 {
8285 if (mNetworkAdapters[slot])
8286 {
8287 mNetworkAdapters[slot]->uninit();
8288 unconst(mNetworkAdapters[slot]).setNull();
8289 }
8290 }
8291
8292 if (mUSBDeviceFilters)
8293 {
8294 mUSBDeviceFilters->uninit();
8295 unconst(mUSBDeviceFilters).setNull();
8296 }
8297
8298 if (mAudioAdapter)
8299 {
8300 mAudioAdapter->uninit();
8301 unconst(mAudioAdapter).setNull();
8302 }
8303
8304 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8305 {
8306 if (mParallelPorts[slot])
8307 {
8308 mParallelPorts[slot]->uninit();
8309 unconst(mParallelPorts[slot]).setNull();
8310 }
8311 }
8312
8313 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8314 {
8315 if (mSerialPorts[slot])
8316 {
8317 mSerialPorts[slot]->uninit();
8318 unconst(mSerialPorts[slot]).setNull();
8319 }
8320 }
8321
8322 if (mVRDEServer)
8323 {
8324 mVRDEServer->uninit();
8325 unconst(mVRDEServer).setNull();
8326 }
8327
8328 if (mGraphicsAdapter)
8329 {
8330 mGraphicsAdapter->uninit();
8331 unconst(mGraphicsAdapter).setNull();
8332 }
8333
8334 if (mBIOSSettings)
8335 {
8336 mBIOSSettings->uninit();
8337 unconst(mBIOSSettings).setNull();
8338 }
8339
8340 if (mTrustedPlatformModule)
8341 {
8342 mTrustedPlatformModule->uninit();
8343 unconst(mTrustedPlatformModule).setNull();
8344 }
8345
8346 if (mNvramStore)
8347 {
8348 mNvramStore->uninit();
8349 unconst(mNvramStore).setNull();
8350 }
8351
8352 if (mRecordingSettings)
8353 {
8354 mRecordingSettings->uninit();
8355 unconst(mRecordingSettings).setNull();
8356 }
8357
8358 /* Deassociate media (only when a real Machine or a SnapshotMachine
8359 * instance is uninitialized; SessionMachine instances refer to real
8360 * Machine media). This is necessary for a clean re-initialization of
8361 * the VM after successfully re-checking the accessibility state. Note
8362 * that in case of normal Machine or SnapshotMachine uninitialization (as
8363 * a result of unregistering or deleting the snapshot), outdated media
8364 * attachments will already be uninitialized and deleted, so this
8365 * code will not affect them. */
8366 if ( !mMediumAttachments.isNull()
8367 && !i_isSessionMachine()
8368 )
8369 {
8370 for (MediumAttachmentList::const_iterator
8371 it = mMediumAttachments->begin();
8372 it != mMediumAttachments->end();
8373 ++it)
8374 {
8375 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8376 if (pMedium.isNull())
8377 continue;
8378 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8379 AssertComRC(rc);
8380 }
8381 }
8382
8383 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8384 {
8385 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8386 if (mData->mFirstSnapshot)
8387 {
8388 // snapshots tree is protected by machine write lock; strictly
8389 // this isn't necessary here since we're deleting the entire
8390 // machine, but otherwise we assert in Snapshot::uninit()
8391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8392 mData->mFirstSnapshot->uninit();
8393 mData->mFirstSnapshot.setNull();
8394 }
8395
8396 mData->mCurrentSnapshot.setNull();
8397 }
8398
8399 /* free data structures (the essential mData structure is not freed here
8400 * since it may be still in use) */
8401 mMediumAttachments.free();
8402 mStorageControllers.free();
8403 mUSBControllers.free();
8404 mHWData.free();
8405 mUserData.free();
8406 mSSData.free();
8407}
8408
8409/**
8410 * Returns a pointer to the Machine object for this machine that acts like a
8411 * parent for complex machine data objects such as shared folders, etc.
8412 *
8413 * For primary Machine objects and for SnapshotMachine objects, returns this
8414 * object's pointer itself. For SessionMachine objects, returns the peer
8415 * (primary) machine pointer.
8416 */
8417Machine *Machine::i_getMachine()
8418{
8419 if (i_isSessionMachine())
8420 return (Machine*)mPeer;
8421 return this;
8422}
8423
8424/**
8425 * Makes sure that there are no machine state dependents. If necessary, waits
8426 * for the number of dependents to drop to zero.
8427 *
8428 * Make sure this method is called from under this object's write lock to
8429 * guarantee that no new dependents may be added when this method returns
8430 * control to the caller.
8431 *
8432 * @note Receives a lock to this object for writing. The lock will be released
8433 * while waiting (if necessary).
8434 *
8435 * @warning To be used only in methods that change the machine state!
8436 */
8437void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8438{
8439 AssertReturnVoid(isWriteLockOnCurrentThread());
8440
8441 /* Wait for all state dependents if necessary */
8442 if (mData->mMachineStateDeps != 0)
8443 {
8444 /* lazy semaphore creation */
8445 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8446 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8447
8448 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8449 mData->mMachineStateDeps));
8450
8451 ++mData->mMachineStateChangePending;
8452
8453 /* reset the semaphore before waiting, the last dependent will signal
8454 * it */
8455 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8456
8457 alock.release();
8458
8459 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8460
8461 alock.acquire();
8462
8463 -- mData->mMachineStateChangePending;
8464 }
8465}
8466
8467/**
8468 * Changes the machine state and informs callbacks.
8469 *
8470 * This method is not intended to fail so it either returns S_OK or asserts (and
8471 * returns a failure).
8472 *
8473 * @note Locks this object for writing.
8474 */
8475HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8476{
8477 LogFlowThisFuncEnter();
8478 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8479 Assert(aMachineState != MachineState_Null);
8480
8481 AutoCaller autoCaller(this);
8482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8483
8484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8485
8486 /* wait for state dependents to drop to zero */
8487 i_ensureNoStateDependencies(alock);
8488
8489 MachineState_T const enmOldState = mData->mMachineState;
8490 if (enmOldState != aMachineState)
8491 {
8492 mData->mMachineState = aMachineState;
8493 RTTimeNow(&mData->mLastStateChange);
8494
8495#ifdef VBOX_WITH_DTRACE_R3_MAIN
8496 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8497#endif
8498 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8499 }
8500
8501 LogFlowThisFuncLeave();
8502 return S_OK;
8503}
8504
8505/**
8506 * Searches for a shared folder with the given logical name
8507 * in the collection of shared folders.
8508 *
8509 * @param aName logical name of the shared folder
8510 * @param aSharedFolder where to return the found object
8511 * @param aSetError whether to set the error info if the folder is
8512 * not found
8513 * @return
8514 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8515 *
8516 * @note
8517 * must be called from under the object's lock!
8518 */
8519HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8520 ComObjPtr<SharedFolder> &aSharedFolder,
8521 bool aSetError /* = false */)
8522{
8523 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8524 for (HWData::SharedFolderList::const_iterator
8525 it = mHWData->mSharedFolders.begin();
8526 it != mHWData->mSharedFolders.end();
8527 ++it)
8528 {
8529 SharedFolder *pSF = *it;
8530 AutoCaller autoCaller(pSF);
8531 if (pSF->i_getName() == aName)
8532 {
8533 aSharedFolder = pSF;
8534 rc = S_OK;
8535 break;
8536 }
8537 }
8538
8539 if (aSetError && FAILED(rc))
8540 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8541
8542 return rc;
8543}
8544
8545/**
8546 * Initializes all machine instance data from the given settings structures
8547 * from XML. The exception is the machine UUID which needs special handling
8548 * depending on the caller's use case, so the caller needs to set that herself.
8549 *
8550 * This gets called in several contexts during machine initialization:
8551 *
8552 * -- When machine XML exists on disk already and needs to be loaded into memory,
8553 * for example, from #i_registeredInit() to load all registered machines on
8554 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8555 * attached to the machine should be part of some media registry already.
8556 *
8557 * -- During OVF import, when a machine config has been constructed from an
8558 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8559 * ensure that the media listed as attachments in the config (which have
8560 * been imported from the OVF) receive the correct registry ID.
8561 *
8562 * -- During VM cloning.
8563 *
8564 * @param config Machine settings from XML.
8565 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8566 * for each attached medium in the config.
8567 * @return
8568 */
8569HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8570 const Guid *puuidRegistry)
8571{
8572 // copy name, description, OS type, teleporter, UTC etc.
8573 mUserData->s = config.machineUserData;
8574
8575 // look up the object by Id to check it is valid
8576 ComObjPtr<GuestOSType> pGuestOSType;
8577 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8578 if (!pGuestOSType.isNull())
8579 mUserData->s.strOsType = pGuestOSType->i_id();
8580
8581 // stateFile (optional)
8582 if (config.strStateFile.isEmpty())
8583 mSSData->strStateFilePath.setNull();
8584 else
8585 {
8586 Utf8Str stateFilePathFull(config.strStateFile);
8587 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8588 if (RT_FAILURE(vrc))
8589 return setErrorBoth(E_FAIL, vrc,
8590 tr("Invalid saved state file path '%s' (%Rrc)"),
8591 config.strStateFile.c_str(),
8592 vrc);
8593 mSSData->strStateFilePath = stateFilePathFull;
8594 }
8595
8596 // snapshot folder needs special processing so set it again
8597 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8598 if (FAILED(rc)) return rc;
8599
8600 /* Copy the extra data items (config may or may not be the same as
8601 * mData->pMachineConfigFile) if necessary. When loading the XML files
8602 * from disk they are the same, but not for OVF import. */
8603 if (mData->pMachineConfigFile != &config)
8604 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8605
8606 /* currentStateModified (optional, default is true) */
8607 mData->mCurrentStateModified = config.fCurrentStateModified;
8608
8609 mData->mLastStateChange = config.timeLastStateChange;
8610
8611 /*
8612 * note: all mUserData members must be assigned prior this point because
8613 * we need to commit changes in order to let mUserData be shared by all
8614 * snapshot machine instances.
8615 */
8616 mUserData.commitCopy();
8617
8618 // machine registry, if present (must be loaded before snapshots)
8619 if (config.canHaveOwnMediaRegistry())
8620 {
8621 // determine machine folder
8622 Utf8Str strMachineFolder = i_getSettingsFileFull();
8623 strMachineFolder.stripFilename();
8624 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8625 config.mediaRegistry,
8626 strMachineFolder);
8627 if (FAILED(rc)) return rc;
8628 }
8629
8630 /* Snapshot node (optional) */
8631 size_t cRootSnapshots;
8632 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8633 {
8634 // there must be only one root snapshot
8635 Assert(cRootSnapshots == 1);
8636
8637 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8638
8639 rc = i_loadSnapshot(snap,
8640 config.uuidCurrentSnapshot,
8641 NULL); // no parent == first snapshot
8642 if (FAILED(rc)) return rc;
8643 }
8644
8645 // hardware data
8646 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8647 if (FAILED(rc)) return rc;
8648
8649 /*
8650 * NOTE: the assignment below must be the last thing to do,
8651 * otherwise it will be not possible to change the settings
8652 * somewhere in the code above because all setters will be
8653 * blocked by i_checkStateDependency(MutableStateDep).
8654 */
8655
8656 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8657 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8658 {
8659 /* no need to use i_setMachineState() during init() */
8660 mData->mMachineState = MachineState_AbortedSaved;
8661 }
8662 else if (config.fAborted)
8663 {
8664 mSSData->strStateFilePath.setNull();
8665
8666 /* no need to use i_setMachineState() during init() */
8667 mData->mMachineState = MachineState_Aborted;
8668 }
8669 else if (!mSSData->strStateFilePath.isEmpty())
8670 {
8671 /* no need to use i_setMachineState() during init() */
8672 mData->mMachineState = MachineState_Saved;
8673 }
8674
8675 // after loading settings, we are no longer different from the XML on disk
8676 mData->flModifications = 0;
8677
8678 return S_OK;
8679}
8680
8681/**
8682 * Recursively loads all snapshots starting from the given.
8683 *
8684 * @param data snapshot settings.
8685 * @param aCurSnapshotId Current snapshot ID from the settings file.
8686 * @param aParentSnapshot Parent snapshot.
8687 */
8688HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8689 const Guid &aCurSnapshotId,
8690 Snapshot *aParentSnapshot)
8691{
8692 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8693 AssertReturn(!i_isSessionMachine(), E_FAIL);
8694
8695 HRESULT rc = S_OK;
8696
8697 Utf8Str strStateFile;
8698 if (!data.strStateFile.isEmpty())
8699 {
8700 /* optional */
8701 strStateFile = data.strStateFile;
8702 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8703 if (RT_FAILURE(vrc))
8704 return setErrorBoth(E_FAIL, vrc,
8705 tr("Invalid saved state file path '%s' (%Rrc)"),
8706 strStateFile.c_str(),
8707 vrc);
8708 }
8709
8710 /* create a snapshot machine object */
8711 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8712 pSnapshotMachine.createObject();
8713 rc = pSnapshotMachine->initFromSettings(this,
8714 data.hardware,
8715 &data.debugging,
8716 &data.autostart,
8717 data.uuid.ref(),
8718 strStateFile);
8719 if (FAILED(rc)) return rc;
8720
8721 /* create a snapshot object */
8722 ComObjPtr<Snapshot> pSnapshot;
8723 pSnapshot.createObject();
8724 /* initialize the snapshot */
8725 rc = pSnapshot->init(mParent, // VirtualBox object
8726 data.uuid,
8727 data.strName,
8728 data.strDescription,
8729 data.timestamp,
8730 pSnapshotMachine,
8731 aParentSnapshot);
8732 if (FAILED(rc)) return rc;
8733
8734 /* memorize the first snapshot if necessary */
8735 if (!mData->mFirstSnapshot)
8736 mData->mFirstSnapshot = pSnapshot;
8737
8738 /* memorize the current snapshot when appropriate */
8739 if ( !mData->mCurrentSnapshot
8740 && pSnapshot->i_getId() == aCurSnapshotId
8741 )
8742 mData->mCurrentSnapshot = pSnapshot;
8743
8744 // now create the children
8745 for (settings::SnapshotsList::const_iterator
8746 it = data.llChildSnapshots.begin();
8747 it != data.llChildSnapshots.end();
8748 ++it)
8749 {
8750 const settings::Snapshot &childData = *it;
8751 // recurse
8752 rc = i_loadSnapshot(childData,
8753 aCurSnapshotId,
8754 pSnapshot); // parent = the one we created above
8755 if (FAILED(rc)) return rc;
8756 }
8757
8758 return rc;
8759}
8760
8761/**
8762 * Loads settings into mHWData.
8763 *
8764 * @param puuidRegistry Registry ID.
8765 * @param puuidSnapshot Snapshot ID
8766 * @param data Reference to the hardware settings.
8767 * @param pDbg Pointer to the debugging settings.
8768 * @param pAutostart Pointer to the autostart settings.
8769 */
8770HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8771 const Guid *puuidSnapshot,
8772 const settings::Hardware &data,
8773 const settings::Debugging *pDbg,
8774 const settings::Autostart *pAutostart)
8775{
8776 AssertReturn(!i_isSessionMachine(), E_FAIL);
8777
8778 HRESULT rc = S_OK;
8779
8780 try
8781 {
8782 ComObjPtr<GuestOSType> pGuestOSType;
8783 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8784
8785 /* The hardware version attribute (optional). */
8786 mHWData->mHWVersion = data.strVersion;
8787 mHWData->mHardwareUUID = data.uuid;
8788
8789 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8790 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8791 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8792 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8793 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8794 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8795 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8796 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8797 mHWData->mPAEEnabled = data.fPAE;
8798 mHWData->mLongMode = data.enmLongMode;
8799 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8800 mHWData->mAPIC = data.fAPIC;
8801 mHWData->mX2APIC = data.fX2APIC;
8802 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8803 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8804 mHWData->mSpecCtrl = data.fSpecCtrl;
8805 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8806 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8807 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8808 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8809 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8810 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8811 mHWData->mCPUCount = data.cCPUs;
8812 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8813 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8814 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8815 mHWData->mCpuProfile = data.strCpuProfile;
8816
8817 // cpu
8818 if (mHWData->mCPUHotPlugEnabled)
8819 {
8820 for (settings::CpuList::const_iterator
8821 it = data.llCpus.begin();
8822 it != data.llCpus.end();
8823 ++it)
8824 {
8825 const settings::Cpu &cpu = *it;
8826
8827 mHWData->mCPUAttached[cpu.ulId] = true;
8828 }
8829 }
8830
8831 // cpuid leafs
8832 for (settings::CpuIdLeafsList::const_iterator
8833 it = data.llCpuIdLeafs.begin();
8834 it != data.llCpuIdLeafs.end();
8835 ++it)
8836 {
8837 const settings::CpuIdLeaf &rLeaf= *it;
8838 if ( rLeaf.idx < UINT32_C(0x20)
8839 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8840 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8841 mHWData->mCpuIdLeafList.push_back(rLeaf);
8842 /* else: just ignore */
8843 }
8844
8845 mHWData->mMemorySize = data.ulMemorySizeMB;
8846 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8847
8848 // boot order
8849 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8850 {
8851 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8852 if (it == data.mapBootOrder.end())
8853 mHWData->mBootOrder[i] = DeviceType_Null;
8854 else
8855 mHWData->mBootOrder[i] = it->second;
8856 }
8857
8858 mHWData->mFirmwareType = data.firmwareType;
8859 mHWData->mPointingHIDType = data.pointingHIDType;
8860 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8861 mHWData->mChipsetType = data.chipsetType;
8862 mHWData->mIommuType = data.iommuType;
8863 mHWData->mParavirtProvider = data.paravirtProvider;
8864 mHWData->mParavirtDebug = data.strParavirtDebug;
8865 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8866 mHWData->mHPETEnabled = data.fHPETEnabled;
8867
8868 /* GraphicsAdapter */
8869 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8870 if (FAILED(rc)) return rc;
8871
8872 /* VRDEServer */
8873 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8874 if (FAILED(rc)) return rc;
8875
8876 /* BIOS */
8877 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8878 if (FAILED(rc)) return rc;
8879
8880 /* Trusted Platform Module */
8881 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8882 if (FAILED(rc)) return rc;
8883
8884 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8885 if (FAILED(rc)) return rc;
8886
8887 /* Recording settings */
8888 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8889 if (FAILED(rc)) return rc;
8890
8891 // Bandwidth control (must come before network adapters)
8892 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8893 if (FAILED(rc)) return rc;
8894
8895 /* USB controllers */
8896 for (settings::USBControllerList::const_iterator
8897 it = data.usbSettings.llUSBControllers.begin();
8898 it != data.usbSettings.llUSBControllers.end();
8899 ++it)
8900 {
8901 const settings::USBController &settingsCtrl = *it;
8902 ComObjPtr<USBController> newCtrl;
8903
8904 newCtrl.createObject();
8905 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8906 mUSBControllers->push_back(newCtrl);
8907 }
8908
8909 /* USB device filters */
8910 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8911 if (FAILED(rc)) return rc;
8912
8913 // network adapters (establish array size first and apply defaults, to
8914 // ensure reading the same settings as we saved, since the list skips
8915 // adapters having defaults)
8916 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8917 size_t oldCount = mNetworkAdapters.size();
8918 if (newCount > oldCount)
8919 {
8920 mNetworkAdapters.resize(newCount);
8921 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8922 {
8923 unconst(mNetworkAdapters[slot]).createObject();
8924 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8925 }
8926 }
8927 else if (newCount < oldCount)
8928 mNetworkAdapters.resize(newCount);
8929 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8930 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8931 for (settings::NetworkAdaptersList::const_iterator
8932 it = data.llNetworkAdapters.begin();
8933 it != data.llNetworkAdapters.end();
8934 ++it)
8935 {
8936 const settings::NetworkAdapter &nic = *it;
8937
8938 /* slot uniqueness is guaranteed by XML Schema */
8939 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8940 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8941 if (FAILED(rc)) return rc;
8942 }
8943
8944 // serial ports (establish defaults first, to ensure reading the same
8945 // settings as we saved, since the list skips ports having defaults)
8946 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8947 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8948 for (settings::SerialPortsList::const_iterator
8949 it = data.llSerialPorts.begin();
8950 it != data.llSerialPorts.end();
8951 ++it)
8952 {
8953 const settings::SerialPort &s = *it;
8954
8955 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8956 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8957 if (FAILED(rc)) return rc;
8958 }
8959
8960 // parallel ports (establish defaults first, to ensure reading the same
8961 // settings as we saved, since the list skips ports having defaults)
8962 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8963 mParallelPorts[i]->i_applyDefaults();
8964 for (settings::ParallelPortsList::const_iterator
8965 it = data.llParallelPorts.begin();
8966 it != data.llParallelPorts.end();
8967 ++it)
8968 {
8969 const settings::ParallelPort &p = *it;
8970
8971 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8972 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8973 if (FAILED(rc)) return rc;
8974 }
8975
8976 /* AudioAdapter */
8977 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8978 if (FAILED(rc)) return rc;
8979
8980 /* storage controllers */
8981 rc = i_loadStorageControllers(data.storage,
8982 puuidRegistry,
8983 puuidSnapshot);
8984 if (FAILED(rc)) return rc;
8985
8986 /* Shared folders */
8987 for (settings::SharedFoldersList::const_iterator
8988 it = data.llSharedFolders.begin();
8989 it != data.llSharedFolders.end();
8990 ++it)
8991 {
8992 const settings::SharedFolder &sf = *it;
8993
8994 ComObjPtr<SharedFolder> sharedFolder;
8995 /* Check for double entries. Not allowed! */
8996 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8997 if (SUCCEEDED(rc))
8998 return setError(VBOX_E_OBJECT_IN_USE,
8999 tr("Shared folder named '%s' already exists"),
9000 sf.strName.c_str());
9001
9002 /* Create the new shared folder. Don't break on error. This will be
9003 * reported when the machine starts. */
9004 sharedFolder.createObject();
9005 rc = sharedFolder->init(i_getMachine(),
9006 sf.strName,
9007 sf.strHostPath,
9008 RT_BOOL(sf.fWritable),
9009 RT_BOOL(sf.fAutoMount),
9010 sf.strAutoMountPoint,
9011 false /* fFailOnError */);
9012 if (FAILED(rc)) return rc;
9013 mHWData->mSharedFolders.push_back(sharedFolder);
9014 }
9015
9016 // Clipboard
9017 mHWData->mClipboardMode = data.clipboardMode;
9018 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9019
9020 // drag'n'drop
9021 mHWData->mDnDMode = data.dndMode;
9022
9023 // guest settings
9024 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9025
9026 // IO settings
9027 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9028 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9029
9030 // Host PCI devices
9031 for (settings::HostPCIDeviceAttachmentList::const_iterator
9032 it = data.pciAttachments.begin();
9033 it != data.pciAttachments.end();
9034 ++it)
9035 {
9036 const settings::HostPCIDeviceAttachment &hpda = *it;
9037 ComObjPtr<PCIDeviceAttachment> pda;
9038
9039 pda.createObject();
9040 pda->i_loadSettings(this, hpda);
9041 mHWData->mPCIDeviceAssignments.push_back(pda);
9042 }
9043
9044 /*
9045 * (The following isn't really real hardware, but it lives in HWData
9046 * for reasons of convenience.)
9047 */
9048
9049#ifdef VBOX_WITH_GUEST_PROPS
9050 /* Guest properties (optional) */
9051
9052 /* Only load transient guest properties for configs which have saved
9053 * state, because there shouldn't be any for powered off VMs. The same
9054 * logic applies for snapshots, as offline snapshots shouldn't have
9055 * any such properties. They confuse the code in various places.
9056 * Note: can't rely on the machine state, as it isn't set yet. */
9057 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9058 /* apologies for the hacky unconst() usage, but this needs hacking
9059 * actually inconsistent settings into consistency, otherwise there
9060 * will be some corner cases where the inconsistency survives
9061 * surprisingly long without getting fixed, especially for snapshots
9062 * as there are no config changes. */
9063 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9064 for (settings::GuestPropertiesList::iterator
9065 it = llGuestProperties.begin();
9066 it != llGuestProperties.end();
9067 /*nothing*/)
9068 {
9069 const settings::GuestProperty &prop = *it;
9070 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9071 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9072 if ( fSkipTransientGuestProperties
9073 && ( fFlags & GUEST_PROP_F_TRANSIENT
9074 || fFlags & GUEST_PROP_F_TRANSRESET))
9075 {
9076 it = llGuestProperties.erase(it);
9077 continue;
9078 }
9079 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9080 mHWData->mGuestProperties[prop.strName] = property;
9081 ++it;
9082 }
9083#endif /* VBOX_WITH_GUEST_PROPS defined */
9084
9085 rc = i_loadDebugging(pDbg);
9086 if (FAILED(rc))
9087 return rc;
9088
9089 mHWData->mAutostart = *pAutostart;
9090
9091 /* default frontend */
9092 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9093 }
9094 catch (std::bad_alloc &)
9095 {
9096 return E_OUTOFMEMORY;
9097 }
9098
9099 AssertComRC(rc);
9100 return rc;
9101}
9102
9103/**
9104 * Called from i_loadHardware() to load the debugging settings of the
9105 * machine.
9106 *
9107 * @param pDbg Pointer to the settings.
9108 */
9109HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9110{
9111 mHWData->mDebugging = *pDbg;
9112 /* no more processing currently required, this will probably change. */
9113 return S_OK;
9114}
9115
9116/**
9117 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9118 *
9119 * @param data storage settings.
9120 * @param puuidRegistry media registry ID to set media to or NULL;
9121 * see Machine::i_loadMachineDataFromSettings()
9122 * @param puuidSnapshot snapshot ID
9123 * @return
9124 */
9125HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9126 const Guid *puuidRegistry,
9127 const Guid *puuidSnapshot)
9128{
9129 AssertReturn(!i_isSessionMachine(), E_FAIL);
9130
9131 HRESULT rc = S_OK;
9132
9133 for (settings::StorageControllersList::const_iterator
9134 it = data.llStorageControllers.begin();
9135 it != data.llStorageControllers.end();
9136 ++it)
9137 {
9138 const settings::StorageController &ctlData = *it;
9139
9140 ComObjPtr<StorageController> pCtl;
9141 /* Try to find one with the name first. */
9142 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9143 if (SUCCEEDED(rc))
9144 return setError(VBOX_E_OBJECT_IN_USE,
9145 tr("Storage controller named '%s' already exists"),
9146 ctlData.strName.c_str());
9147
9148 pCtl.createObject();
9149 rc = pCtl->init(this,
9150 ctlData.strName,
9151 ctlData.storageBus,
9152 ctlData.ulInstance,
9153 ctlData.fBootable);
9154 if (FAILED(rc)) return rc;
9155
9156 mStorageControllers->push_back(pCtl);
9157
9158 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9159 if (FAILED(rc)) return rc;
9160
9161 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9162 if (FAILED(rc)) return rc;
9163
9164 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9165 if (FAILED(rc)) return rc;
9166
9167 /* Load the attached devices now. */
9168 rc = i_loadStorageDevices(pCtl,
9169 ctlData,
9170 puuidRegistry,
9171 puuidSnapshot);
9172 if (FAILED(rc)) return rc;
9173 }
9174
9175 return S_OK;
9176}
9177
9178/**
9179 * Called from i_loadStorageControllers for a controller's devices.
9180 *
9181 * @param aStorageController
9182 * @param data
9183 * @param puuidRegistry media registry ID to set media to or NULL; see
9184 * Machine::i_loadMachineDataFromSettings()
9185 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9186 * @return
9187 */
9188HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9189 const settings::StorageController &data,
9190 const Guid *puuidRegistry,
9191 const Guid *puuidSnapshot)
9192{
9193 HRESULT rc = S_OK;
9194
9195 /* paranoia: detect duplicate attachments */
9196 for (settings::AttachedDevicesList::const_iterator
9197 it = data.llAttachedDevices.begin();
9198 it != data.llAttachedDevices.end();
9199 ++it)
9200 {
9201 const settings::AttachedDevice &ad = *it;
9202
9203 for (settings::AttachedDevicesList::const_iterator it2 = it;
9204 it2 != data.llAttachedDevices.end();
9205 ++it2)
9206 {
9207 if (it == it2)
9208 continue;
9209
9210 const settings::AttachedDevice &ad2 = *it2;
9211
9212 if ( ad.lPort == ad2.lPort
9213 && ad.lDevice == ad2.lDevice)
9214 {
9215 return setError(E_FAIL,
9216 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9217 aStorageController->i_getName().c_str(),
9218 ad.lPort,
9219 ad.lDevice,
9220 mUserData->s.strName.c_str());
9221 }
9222 }
9223 }
9224
9225 for (settings::AttachedDevicesList::const_iterator
9226 it = data.llAttachedDevices.begin();
9227 it != data.llAttachedDevices.end();
9228 ++it)
9229 {
9230 const settings::AttachedDevice &dev = *it;
9231 ComObjPtr<Medium> medium;
9232
9233 switch (dev.deviceType)
9234 {
9235 case DeviceType_Floppy:
9236 case DeviceType_DVD:
9237 if (dev.strHostDriveSrc.isNotEmpty())
9238 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9239 false /* fRefresh */, medium);
9240 else
9241 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9242 dev.uuid,
9243 false /* fRefresh */,
9244 false /* aSetError */,
9245 medium);
9246 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9247 // This is not an error. The host drive or UUID might have vanished, so just go
9248 // ahead without this removeable medium attachment
9249 rc = S_OK;
9250 break;
9251
9252 case DeviceType_HardDisk:
9253 {
9254 /* find a hard disk by UUID */
9255 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9256 if (FAILED(rc))
9257 {
9258 if (i_isSnapshotMachine())
9259 {
9260 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9261 // so the user knows that the bad disk is in a snapshot somewhere
9262 com::ErrorInfo info;
9263 return setError(E_FAIL,
9264 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9265 puuidSnapshot->raw(),
9266 info.getText().raw());
9267 }
9268 else
9269 return rc;
9270 }
9271
9272 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9273
9274 if (medium->i_getType() == MediumType_Immutable)
9275 {
9276 if (i_isSnapshotMachine())
9277 return setError(E_FAIL,
9278 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9279 "of the virtual machine '%s' ('%s')"),
9280 medium->i_getLocationFull().c_str(),
9281 dev.uuid.raw(),
9282 puuidSnapshot->raw(),
9283 mUserData->s.strName.c_str(),
9284 mData->m_strConfigFileFull.c_str());
9285
9286 return setError(E_FAIL,
9287 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9288 medium->i_getLocationFull().c_str(),
9289 dev.uuid.raw(),
9290 mUserData->s.strName.c_str(),
9291 mData->m_strConfigFileFull.c_str());
9292 }
9293
9294 if (medium->i_getType() == MediumType_MultiAttach)
9295 {
9296 if (i_isSnapshotMachine())
9297 return setError(E_FAIL,
9298 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9299 "of the virtual machine '%s' ('%s')"),
9300 medium->i_getLocationFull().c_str(),
9301 dev.uuid.raw(),
9302 puuidSnapshot->raw(),
9303 mUserData->s.strName.c_str(),
9304 mData->m_strConfigFileFull.c_str());
9305
9306 return setError(E_FAIL,
9307 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9308 medium->i_getLocationFull().c_str(),
9309 dev.uuid.raw(),
9310 mUserData->s.strName.c_str(),
9311 mData->m_strConfigFileFull.c_str());
9312 }
9313
9314 if ( !i_isSnapshotMachine()
9315 && medium->i_getChildren().size() != 0
9316 )
9317 return setError(E_FAIL,
9318 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9319 "because it has %d differencing child hard disks"),
9320 medium->i_getLocationFull().c_str(),
9321 dev.uuid.raw(),
9322 mUserData->s.strName.c_str(),
9323 mData->m_strConfigFileFull.c_str(),
9324 medium->i_getChildren().size());
9325
9326 if (i_findAttachment(*mMediumAttachments.data(),
9327 medium))
9328 return setError(E_FAIL,
9329 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9330 medium->i_getLocationFull().c_str(),
9331 dev.uuid.raw(),
9332 mUserData->s.strName.c_str(),
9333 mData->m_strConfigFileFull.c_str());
9334
9335 break;
9336 }
9337
9338 default:
9339 return setError(E_FAIL,
9340 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9341 medium->i_getLocationFull().c_str(),
9342 mUserData->s.strName.c_str(),
9343 mData->m_strConfigFileFull.c_str());
9344 }
9345
9346 if (FAILED(rc))
9347 break;
9348
9349 /* Bandwidth groups are loaded at this point. */
9350 ComObjPtr<BandwidthGroup> pBwGroup;
9351
9352 if (!dev.strBwGroup.isEmpty())
9353 {
9354 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9355 if (FAILED(rc))
9356 return setError(E_FAIL,
9357 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9358 medium->i_getLocationFull().c_str(),
9359 dev.strBwGroup.c_str(),
9360 mUserData->s.strName.c_str(),
9361 mData->m_strConfigFileFull.c_str());
9362 pBwGroup->i_reference();
9363 }
9364
9365 const Utf8Str controllerName = aStorageController->i_getName();
9366 ComObjPtr<MediumAttachment> pAttachment;
9367 pAttachment.createObject();
9368 rc = pAttachment->init(this,
9369 medium,
9370 controllerName,
9371 dev.lPort,
9372 dev.lDevice,
9373 dev.deviceType,
9374 false,
9375 dev.fPassThrough,
9376 dev.fTempEject,
9377 dev.fNonRotational,
9378 dev.fDiscard,
9379 dev.fHotPluggable,
9380 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9381 if (FAILED(rc)) break;
9382
9383 /* associate the medium with this machine and snapshot */
9384 if (!medium.isNull())
9385 {
9386 AutoCaller medCaller(medium);
9387 if (FAILED(medCaller.rc())) return medCaller.rc();
9388 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9389
9390 if (i_isSnapshotMachine())
9391 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9392 else
9393 rc = medium->i_addBackReference(mData->mUuid);
9394 /* If the medium->addBackReference fails it sets an appropriate
9395 * error message, so no need to do any guesswork here. */
9396
9397 if (puuidRegistry)
9398 // caller wants registry ID to be set on all attached media (OVF import case)
9399 medium->i_addRegistry(*puuidRegistry);
9400 }
9401
9402 if (FAILED(rc))
9403 break;
9404
9405 /* back up mMediumAttachments to let registeredInit() properly rollback
9406 * on failure (= limited accessibility) */
9407 i_setModified(IsModified_Storage);
9408 mMediumAttachments.backup();
9409 mMediumAttachments->push_back(pAttachment);
9410 }
9411
9412 return rc;
9413}
9414
9415/**
9416 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9417 *
9418 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9419 * @param aSnapshot where to return the found snapshot
9420 * @param aSetError true to set extended error info on failure
9421 */
9422HRESULT Machine::i_findSnapshotById(const Guid &aId,
9423 ComObjPtr<Snapshot> &aSnapshot,
9424 bool aSetError /* = false */)
9425{
9426 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9427
9428 if (!mData->mFirstSnapshot)
9429 {
9430 if (aSetError)
9431 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9432 return E_FAIL;
9433 }
9434
9435 if (aId.isZero())
9436 aSnapshot = mData->mFirstSnapshot;
9437 else
9438 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9439
9440 if (!aSnapshot)
9441 {
9442 if (aSetError)
9443 return setError(E_FAIL,
9444 tr("Could not find a snapshot with UUID {%s}"),
9445 aId.toString().c_str());
9446 return E_FAIL;
9447 }
9448
9449 return S_OK;
9450}
9451
9452/**
9453 * Returns the snapshot with the given name or fails of no such snapshot.
9454 *
9455 * @param strName snapshot name to find
9456 * @param aSnapshot where to return the found snapshot
9457 * @param aSetError true to set extended error info on failure
9458 */
9459HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9460 ComObjPtr<Snapshot> &aSnapshot,
9461 bool aSetError /* = false */)
9462{
9463 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9464
9465 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9466
9467 if (!mData->mFirstSnapshot)
9468 {
9469 if (aSetError)
9470 return setError(VBOX_E_OBJECT_NOT_FOUND,
9471 tr("This machine does not have any snapshots"));
9472 return VBOX_E_OBJECT_NOT_FOUND;
9473 }
9474
9475 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9476
9477 if (!aSnapshot)
9478 {
9479 if (aSetError)
9480 return setError(VBOX_E_OBJECT_NOT_FOUND,
9481 tr("Could not find a snapshot named '%s'"), strName.c_str());
9482 return VBOX_E_OBJECT_NOT_FOUND;
9483 }
9484
9485 return S_OK;
9486}
9487
9488/**
9489 * Returns a storage controller object with the given name.
9490 *
9491 * @param aName storage controller name to find
9492 * @param aStorageController where to return the found storage controller
9493 * @param aSetError true to set extended error info on failure
9494 */
9495HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9496 ComObjPtr<StorageController> &aStorageController,
9497 bool aSetError /* = false */)
9498{
9499 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9500
9501 for (StorageControllerList::const_iterator
9502 it = mStorageControllers->begin();
9503 it != mStorageControllers->end();
9504 ++it)
9505 {
9506 if ((*it)->i_getName() == aName)
9507 {
9508 aStorageController = (*it);
9509 return S_OK;
9510 }
9511 }
9512
9513 if (aSetError)
9514 return setError(VBOX_E_OBJECT_NOT_FOUND,
9515 tr("Could not find a storage controller named '%s'"),
9516 aName.c_str());
9517 return VBOX_E_OBJECT_NOT_FOUND;
9518}
9519
9520/**
9521 * Returns a USB controller object with the given name.
9522 *
9523 * @param aName USB controller name to find
9524 * @param aUSBController where to return the found USB controller
9525 * @param aSetError true to set extended error info on failure
9526 */
9527HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9528 ComObjPtr<USBController> &aUSBController,
9529 bool aSetError /* = false */)
9530{
9531 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9532
9533 for (USBControllerList::const_iterator
9534 it = mUSBControllers->begin();
9535 it != mUSBControllers->end();
9536 ++it)
9537 {
9538 if ((*it)->i_getName() == aName)
9539 {
9540 aUSBController = (*it);
9541 return S_OK;
9542 }
9543 }
9544
9545 if (aSetError)
9546 return setError(VBOX_E_OBJECT_NOT_FOUND,
9547 tr("Could not find a storage controller named '%s'"),
9548 aName.c_str());
9549 return VBOX_E_OBJECT_NOT_FOUND;
9550}
9551
9552/**
9553 * Returns the number of USB controller instance of the given type.
9554 *
9555 * @param enmType USB controller type.
9556 */
9557ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9558{
9559 ULONG cCtrls = 0;
9560
9561 for (USBControllerList::const_iterator
9562 it = mUSBControllers->begin();
9563 it != mUSBControllers->end();
9564 ++it)
9565 {
9566 if ((*it)->i_getControllerType() == enmType)
9567 cCtrls++;
9568 }
9569
9570 return cCtrls;
9571}
9572
9573HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9574 MediumAttachmentList &atts)
9575{
9576 AutoCaller autoCaller(this);
9577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9578
9579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9580
9581 for (MediumAttachmentList::const_iterator
9582 it = mMediumAttachments->begin();
9583 it != mMediumAttachments->end();
9584 ++it)
9585 {
9586 const ComObjPtr<MediumAttachment> &pAtt = *it;
9587 // should never happen, but deal with NULL pointers in the list.
9588 AssertContinue(!pAtt.isNull());
9589
9590 // getControllerName() needs caller+read lock
9591 AutoCaller autoAttCaller(pAtt);
9592 if (FAILED(autoAttCaller.rc()))
9593 {
9594 atts.clear();
9595 return autoAttCaller.rc();
9596 }
9597 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9598
9599 if (pAtt->i_getControllerName() == aName)
9600 atts.push_back(pAtt);
9601 }
9602
9603 return S_OK;
9604}
9605
9606
9607/**
9608 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9609 * file if the machine name was changed and about creating a new settings file
9610 * if this is a new machine.
9611 *
9612 * @note Must be never called directly but only from #saveSettings().
9613 */
9614HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9615{
9616 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9617
9618 HRESULT rc = S_OK;
9619
9620 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9621
9622 /// @todo need to handle primary group change, too
9623
9624 /* attempt to rename the settings file if machine name is changed */
9625 if ( mUserData->s.fNameSync
9626 && mUserData.isBackedUp()
9627 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9628 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9629 )
9630 {
9631 bool dirRenamed = false;
9632 bool fileRenamed = false;
9633
9634 Utf8Str configFile, newConfigFile;
9635 Utf8Str configFilePrev, newConfigFilePrev;
9636 Utf8Str NVRAMFile, newNVRAMFile;
9637 Utf8Str configDir, newConfigDir;
9638
9639 do
9640 {
9641 int vrc = VINF_SUCCESS;
9642
9643 Utf8Str name = mUserData.backedUpData()->s.strName;
9644 Utf8Str newName = mUserData->s.strName;
9645 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9646 if (group == "/")
9647 group.setNull();
9648 Utf8Str newGroup = mUserData->s.llGroups.front();
9649 if (newGroup == "/")
9650 newGroup.setNull();
9651
9652 configFile = mData->m_strConfigFileFull;
9653
9654 /* first, rename the directory if it matches the group and machine name */
9655 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9656 /** @todo hack, make somehow use of ComposeMachineFilename */
9657 if (mUserData->s.fDirectoryIncludesUUID)
9658 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9659 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9660 /** @todo hack, make somehow use of ComposeMachineFilename */
9661 if (mUserData->s.fDirectoryIncludesUUID)
9662 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9663 configDir = configFile;
9664 configDir.stripFilename();
9665 newConfigDir = configDir;
9666 if ( configDir.length() >= groupPlusName.length()
9667 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9668 groupPlusName.c_str()))
9669 {
9670 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9671 Utf8Str newConfigBaseDir(newConfigDir);
9672 newConfigDir.append(newGroupPlusName);
9673 /* consistency: use \ if appropriate on the platform */
9674 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9675 /* new dir and old dir cannot be equal here because of 'if'
9676 * above and because name != newName */
9677 Assert(configDir != newConfigDir);
9678 if (!fSettingsFileIsNew)
9679 {
9680 /* perform real rename only if the machine is not new */
9681 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9682 if ( vrc == VERR_FILE_NOT_FOUND
9683 || vrc == VERR_PATH_NOT_FOUND)
9684 {
9685 /* create the parent directory, then retry renaming */
9686 Utf8Str parent(newConfigDir);
9687 parent.stripFilename();
9688 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9689 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9690 }
9691 if (RT_FAILURE(vrc))
9692 {
9693 rc = setErrorBoth(E_FAIL, vrc,
9694 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9695 configDir.c_str(),
9696 newConfigDir.c_str(),
9697 vrc);
9698 break;
9699 }
9700 /* delete subdirectories which are no longer needed */
9701 Utf8Str dir(configDir);
9702 dir.stripFilename();
9703 while (dir != newConfigBaseDir && dir != ".")
9704 {
9705 vrc = RTDirRemove(dir.c_str());
9706 if (RT_FAILURE(vrc))
9707 break;
9708 dir.stripFilename();
9709 }
9710 dirRenamed = true;
9711 }
9712 }
9713
9714 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9715
9716 /* then try to rename the settings file itself */
9717 if (newConfigFile != configFile)
9718 {
9719 /* get the path to old settings file in renamed directory */
9720 Assert(mData->m_strConfigFileFull == configFile);
9721 configFile.printf("%s%c%s",
9722 newConfigDir.c_str(),
9723 RTPATH_DELIMITER,
9724 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9725 if (!fSettingsFileIsNew)
9726 {
9727 /* perform real rename only if the machine is not new */
9728 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9729 if (RT_FAILURE(vrc))
9730 {
9731 rc = setErrorBoth(E_FAIL, vrc,
9732 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9733 configFile.c_str(),
9734 newConfigFile.c_str(),
9735 vrc);
9736 break;
9737 }
9738 fileRenamed = true;
9739 configFilePrev = configFile;
9740 configFilePrev += "-prev";
9741 newConfigFilePrev = newConfigFile;
9742 newConfigFilePrev += "-prev";
9743 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9744 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9745 if (NVRAMFile.isNotEmpty())
9746 {
9747 // in the NVRAM file path, replace the old directory with the new directory
9748 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9749 {
9750 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9751 NVRAMFile = newConfigDir + strNVRAMFile;
9752 }
9753 newNVRAMFile = newConfigFile;
9754 newNVRAMFile.stripSuffix();
9755 newNVRAMFile += ".nvram";
9756 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9757 }
9758 }
9759 }
9760
9761 // update m_strConfigFileFull amd mConfigFile
9762 mData->m_strConfigFileFull = newConfigFile;
9763 // compute the relative path too
9764 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9765
9766 // store the old and new so that VirtualBox::i_saveSettings() can update
9767 // the media registry
9768 if ( mData->mRegistered
9769 && (configDir != newConfigDir || configFile != newConfigFile))
9770 {
9771 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9772
9773 if (pfNeedsGlobalSaveSettings)
9774 *pfNeedsGlobalSaveSettings = true;
9775 }
9776
9777 // in the saved state file path, replace the old directory with the new directory
9778 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9779 {
9780 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9781 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9782 }
9783 if (newNVRAMFile.isNotEmpty())
9784 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9785
9786 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9787 if (mData->mFirstSnapshot)
9788 {
9789 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9790 newConfigDir.c_str());
9791 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9792 newConfigDir.c_str());
9793 }
9794 }
9795 while (0);
9796
9797 if (FAILED(rc))
9798 {
9799 /* silently try to rename everything back */
9800 if (fileRenamed)
9801 {
9802 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9803 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9804 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9805 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9806 }
9807 if (dirRenamed)
9808 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9809 }
9810
9811 if (FAILED(rc)) return rc;
9812 }
9813
9814 if (fSettingsFileIsNew)
9815 {
9816 /* create a virgin config file */
9817 int vrc = VINF_SUCCESS;
9818
9819 /* ensure the settings directory exists */
9820 Utf8Str path(mData->m_strConfigFileFull);
9821 path.stripFilename();
9822 if (!RTDirExists(path.c_str()))
9823 {
9824 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9825 if (RT_FAILURE(vrc))
9826 {
9827 return setErrorBoth(E_FAIL, vrc,
9828 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9829 path.c_str(),
9830 vrc);
9831 }
9832 }
9833
9834 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9835 path = Utf8Str(mData->m_strConfigFileFull);
9836 RTFILE f = NIL_RTFILE;
9837 vrc = RTFileOpen(&f, path.c_str(),
9838 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9839 if (RT_FAILURE(vrc))
9840 return setErrorBoth(E_FAIL, vrc,
9841 tr("Could not create the settings file '%s' (%Rrc)"),
9842 path.c_str(),
9843 vrc);
9844 RTFileClose(f);
9845 }
9846
9847 return rc;
9848}
9849
9850/**
9851 * Saves and commits machine data, user data and hardware data.
9852 *
9853 * Note that on failure, the data remains uncommitted.
9854 *
9855 * @a aFlags may combine the following flags:
9856 *
9857 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9858 * Used when saving settings after an operation that makes them 100%
9859 * correspond to the settings from the current snapshot.
9860 * - SaveS_Force: settings will be saved without doing a deep compare of the
9861 * settings structures. This is used when this is called because snapshots
9862 * have changed to avoid the overhead of the deep compare.
9863 *
9864 * @note Must be called from under this object's write lock. Locks children for
9865 * writing.
9866 *
9867 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9868 * initialized to false and that will be set to true by this function if
9869 * the caller must invoke VirtualBox::i_saveSettings() because the global
9870 * settings have changed. This will happen if a machine rename has been
9871 * saved and the global machine and media registries will therefore need
9872 * updating.
9873 * @param alock Reference to the lock for this machine object.
9874 * @param aFlags Flags.
9875 */
9876HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9877 AutoWriteLock &alock,
9878 int aFlags /*= 0*/)
9879{
9880 LogFlowThisFuncEnter();
9881
9882 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9883
9884 /* make sure child objects are unable to modify the settings while we are
9885 * saving them */
9886 i_ensureNoStateDependencies(alock);
9887
9888 AssertReturn(!i_isSnapshotMachine(),
9889 E_FAIL);
9890
9891 if (!mData->mAccessible)
9892 return setError(VBOX_E_INVALID_VM_STATE,
9893 tr("The machine is not accessible, so cannot save settings"));
9894
9895 HRESULT rc = S_OK;
9896 bool fNeedsWrite = false;
9897
9898 /* First, prepare to save settings. It will care about renaming the
9899 * settings directory and file if the machine name was changed and about
9900 * creating a new settings file if this is a new machine. */
9901 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9902 if (FAILED(rc)) return rc;
9903
9904 // keep a pointer to the current settings structures
9905 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9906 settings::MachineConfigFile *pNewConfig = NULL;
9907
9908 try
9909 {
9910 // make a fresh one to have everyone write stuff into
9911 pNewConfig = new settings::MachineConfigFile(NULL);
9912 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9913
9914 // now go and copy all the settings data from COM to the settings structures
9915 // (this calls i_saveSettings() on all the COM objects in the machine)
9916 i_copyMachineDataToSettings(*pNewConfig);
9917
9918 if (aFlags & SaveS_ResetCurStateModified)
9919 {
9920 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9921 mData->mCurrentStateModified = FALSE;
9922 fNeedsWrite = true; // always, no need to compare
9923 }
9924 else if (aFlags & SaveS_Force)
9925 {
9926 fNeedsWrite = true; // always, no need to compare
9927 }
9928 else
9929 {
9930 if (!mData->mCurrentStateModified)
9931 {
9932 // do a deep compare of the settings that we just saved with the settings
9933 // previously stored in the config file; this invokes MachineConfigFile::operator==
9934 // which does a deep compare of all the settings, which is expensive but less expensive
9935 // than writing out XML in vain
9936 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9937
9938 // could still be modified if any settings changed
9939 mData->mCurrentStateModified = fAnySettingsChanged;
9940
9941 fNeedsWrite = fAnySettingsChanged;
9942 }
9943 else
9944 fNeedsWrite = true;
9945 }
9946
9947 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9948
9949 if (fNeedsWrite)
9950 // now spit it all out!
9951 pNewConfig->write(mData->m_strConfigFileFull);
9952
9953 mData->pMachineConfigFile = pNewConfig;
9954 delete pOldConfig;
9955 i_commit();
9956
9957 // after saving settings, we are no longer different from the XML on disk
9958 mData->flModifications = 0;
9959 }
9960 catch (HRESULT err)
9961 {
9962 // we assume that error info is set by the thrower
9963 rc = err;
9964
9965 // restore old config
9966 delete pNewConfig;
9967 mData->pMachineConfigFile = pOldConfig;
9968 }
9969 catch (...)
9970 {
9971 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9972 }
9973
9974 if (fNeedsWrite)
9975 {
9976 /* Fire the data change event, even on failure (since we've already
9977 * committed all data). This is done only for SessionMachines because
9978 * mutable Machine instances are always not registered (i.e. private
9979 * to the client process that creates them) and thus don't need to
9980 * inform callbacks. */
9981 if (i_isSessionMachine())
9982 mParent->i_onMachineDataChanged(mData->mUuid);
9983 }
9984
9985 LogFlowThisFunc(("rc=%08X\n", rc));
9986 LogFlowThisFuncLeave();
9987 return rc;
9988}
9989
9990/**
9991 * Implementation for saving the machine settings into the given
9992 * settings::MachineConfigFile instance. This copies machine extradata
9993 * from the previous machine config file in the instance data, if any.
9994 *
9995 * This gets called from two locations:
9996 *
9997 * -- Machine::i_saveSettings(), during the regular XML writing;
9998 *
9999 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10000 * exported to OVF and we write the VirtualBox proprietary XML
10001 * into a <vbox:Machine> tag.
10002 *
10003 * This routine fills all the fields in there, including snapshots, *except*
10004 * for the following:
10005 *
10006 * -- fCurrentStateModified. There is some special logic associated with that.
10007 *
10008 * The caller can then call MachineConfigFile::write() or do something else
10009 * with it.
10010 *
10011 * Caller must hold the machine lock!
10012 *
10013 * This throws XML errors and HRESULT, so the caller must have a catch block!
10014 */
10015void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10016{
10017 // deep copy extradata, being extra careful with self assignment (the STL
10018 // map assignment on Mac OS X clang based Xcode isn't checking)
10019 if (&config != mData->pMachineConfigFile)
10020 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10021
10022 config.uuid = mData->mUuid;
10023
10024 // copy name, description, OS type, teleport, UTC etc.
10025 config.machineUserData = mUserData->s;
10026
10027 if ( mData->mMachineState == MachineState_Saved
10028 || mData->mMachineState == MachineState_AbortedSaved
10029 || mData->mMachineState == MachineState_Restoring
10030 // when doing certain snapshot operations we may or may not have
10031 // a saved state in the current state, so keep everything as is
10032 || ( ( mData->mMachineState == MachineState_Snapshotting
10033 || mData->mMachineState == MachineState_DeletingSnapshot
10034 || mData->mMachineState == MachineState_RestoringSnapshot)
10035 && (!mSSData->strStateFilePath.isEmpty())
10036 )
10037 )
10038 {
10039 Assert(!mSSData->strStateFilePath.isEmpty());
10040 /* try to make the file name relative to the settings file dir */
10041 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10042 }
10043 else
10044 {
10045 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10046 config.strStateFile.setNull();
10047 }
10048
10049 if (mData->mCurrentSnapshot)
10050 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10051 else
10052 config.uuidCurrentSnapshot.clear();
10053
10054 config.timeLastStateChange = mData->mLastStateChange;
10055 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10056 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10057
10058 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10059 if (FAILED(rc)) throw rc;
10060
10061 // save machine's media registry if this is VirtualBox 4.0 or later
10062 if (config.canHaveOwnMediaRegistry())
10063 {
10064 // determine machine folder
10065 Utf8Str strMachineFolder = i_getSettingsFileFull();
10066 strMachineFolder.stripFilename();
10067 mParent->i_saveMediaRegistry(config.mediaRegistry,
10068 i_getId(), // only media with registry ID == machine UUID
10069 strMachineFolder);
10070 // this throws HRESULT
10071 }
10072
10073 // save snapshots
10074 rc = i_saveAllSnapshots(config);
10075 if (FAILED(rc)) throw rc;
10076}
10077
10078/**
10079 * Saves all snapshots of the machine into the given machine config file. Called
10080 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10081 * @param config
10082 * @return
10083 */
10084HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10085{
10086 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10087
10088 HRESULT rc = S_OK;
10089
10090 try
10091 {
10092 config.llFirstSnapshot.clear();
10093
10094 if (mData->mFirstSnapshot)
10095 {
10096 // the settings use a list for "the first snapshot"
10097 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10098
10099 // get reference to the snapshot on the list and work on that
10100 // element straight in the list to avoid excessive copying later
10101 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10102 if (FAILED(rc)) throw rc;
10103 }
10104
10105// if (mType == IsSessionMachine)
10106// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10107
10108 }
10109 catch (HRESULT err)
10110 {
10111 /* we assume that error info is set by the thrower */
10112 rc = err;
10113 }
10114 catch (...)
10115 {
10116 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10117 }
10118
10119 return rc;
10120}
10121
10122/**
10123 * Saves the VM hardware configuration. It is assumed that the
10124 * given node is empty.
10125 *
10126 * @param data Reference to the settings object for the hardware config.
10127 * @param pDbg Pointer to the settings object for the debugging config
10128 * which happens to live in mHWData.
10129 * @param pAutostart Pointer to the settings object for the autostart config
10130 * which happens to live in mHWData.
10131 */
10132HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10133 settings::Autostart *pAutostart)
10134{
10135 HRESULT rc = S_OK;
10136
10137 try
10138 {
10139 /* The hardware version attribute (optional).
10140 Automatically upgrade from 1 to current default hardware version
10141 when there is no saved state. (ugly!) */
10142 if ( mHWData->mHWVersion == "1"
10143 && mSSData->strStateFilePath.isEmpty()
10144 )
10145 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10146
10147 data.strVersion = mHWData->mHWVersion;
10148 data.uuid = mHWData->mHardwareUUID;
10149
10150 // CPU
10151 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10152 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10153 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10154 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10155 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10156 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10157 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10158 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10159 data.fPAE = !!mHWData->mPAEEnabled;
10160 data.enmLongMode = mHWData->mLongMode;
10161 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10162 data.fAPIC = !!mHWData->mAPIC;
10163 data.fX2APIC = !!mHWData->mX2APIC;
10164 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10165 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10166 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10167 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10168 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10169 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10170 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10171 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10172 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10173 data.cCPUs = mHWData->mCPUCount;
10174 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10175 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10176 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10177 data.strCpuProfile = mHWData->mCpuProfile;
10178
10179 data.llCpus.clear();
10180 if (data.fCpuHotPlug)
10181 {
10182 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10183 {
10184 if (mHWData->mCPUAttached[idx])
10185 {
10186 settings::Cpu cpu;
10187 cpu.ulId = idx;
10188 data.llCpus.push_back(cpu);
10189 }
10190 }
10191 }
10192
10193 /* Standard and Extended CPUID leafs. */
10194 data.llCpuIdLeafs.clear();
10195 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10196
10197 // memory
10198 data.ulMemorySizeMB = mHWData->mMemorySize;
10199 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10200
10201 // firmware
10202 data.firmwareType = mHWData->mFirmwareType;
10203
10204 // HID
10205 data.pointingHIDType = mHWData->mPointingHIDType;
10206 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10207
10208 // chipset
10209 data.chipsetType = mHWData->mChipsetType;
10210
10211 // iommu
10212 data.iommuType = mHWData->mIommuType;
10213
10214 // paravirt
10215 data.paravirtProvider = mHWData->mParavirtProvider;
10216 data.strParavirtDebug = mHWData->mParavirtDebug;
10217
10218 // emulated USB card reader
10219 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10220
10221 // HPET
10222 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10223
10224 // boot order
10225 data.mapBootOrder.clear();
10226 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10227 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10228
10229 /* VRDEServer settings (optional) */
10230 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10231 if (FAILED(rc)) throw rc;
10232
10233 /* BIOS settings (required) */
10234 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10235 if (FAILED(rc)) throw rc;
10236
10237 /* Trusted Platform Module settings (required) */
10238 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10239 if (FAILED(rc)) throw rc;
10240
10241 /* NVRAM settings (required) */
10242 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10243 if (FAILED(rc)) throw rc;
10244
10245 /* Recording settings (required) */
10246 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10247 if (FAILED(rc)) throw rc;
10248
10249 /* GraphicsAdapter settings (required) */
10250 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10251 if (FAILED(rc)) throw rc;
10252
10253 /* USB Controller (required) */
10254 data.usbSettings.llUSBControllers.clear();
10255 for (USBControllerList::const_iterator
10256 it = mUSBControllers->begin();
10257 it != mUSBControllers->end();
10258 ++it)
10259 {
10260 ComObjPtr<USBController> ctrl = *it;
10261 settings::USBController settingsCtrl;
10262
10263 settingsCtrl.strName = ctrl->i_getName();
10264 settingsCtrl.enmType = ctrl->i_getControllerType();
10265
10266 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10267 }
10268
10269 /* USB device filters (required) */
10270 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10271 if (FAILED(rc)) throw rc;
10272
10273 /* Network adapters (required) */
10274 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10275 data.llNetworkAdapters.clear();
10276 /* Write out only the nominal number of network adapters for this
10277 * chipset type. Since Machine::commit() hasn't been called there
10278 * may be extra NIC settings in the vector. */
10279 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10280 {
10281 settings::NetworkAdapter nic;
10282 nic.ulSlot = (uint32_t)slot;
10283 /* paranoia check... must not be NULL, but must not crash either. */
10284 if (mNetworkAdapters[slot])
10285 {
10286 if (mNetworkAdapters[slot]->i_hasDefaults())
10287 continue;
10288
10289 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10290 if (FAILED(rc)) throw rc;
10291
10292 data.llNetworkAdapters.push_back(nic);
10293 }
10294 }
10295
10296 /* Serial ports */
10297 data.llSerialPorts.clear();
10298 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10299 {
10300 if (mSerialPorts[slot]->i_hasDefaults())
10301 continue;
10302
10303 settings::SerialPort s;
10304 s.ulSlot = slot;
10305 rc = mSerialPorts[slot]->i_saveSettings(s);
10306 if (FAILED(rc)) return rc;
10307
10308 data.llSerialPorts.push_back(s);
10309 }
10310
10311 /* Parallel ports */
10312 data.llParallelPorts.clear();
10313 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10314 {
10315 if (mParallelPorts[slot]->i_hasDefaults())
10316 continue;
10317
10318 settings::ParallelPort p;
10319 p.ulSlot = slot;
10320 rc = mParallelPorts[slot]->i_saveSettings(p);
10321 if (FAILED(rc)) return rc;
10322
10323 data.llParallelPorts.push_back(p);
10324 }
10325
10326 /* Audio adapter */
10327 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10328 if (FAILED(rc)) return rc;
10329
10330 rc = i_saveStorageControllers(data.storage);
10331 if (FAILED(rc)) return rc;
10332
10333 /* Shared folders */
10334 data.llSharedFolders.clear();
10335 for (HWData::SharedFolderList::const_iterator
10336 it = mHWData->mSharedFolders.begin();
10337 it != mHWData->mSharedFolders.end();
10338 ++it)
10339 {
10340 SharedFolder *pSF = *it;
10341 AutoCaller sfCaller(pSF);
10342 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10343 settings::SharedFolder sf;
10344 sf.strName = pSF->i_getName();
10345 sf.strHostPath = pSF->i_getHostPath();
10346 sf.fWritable = !!pSF->i_isWritable();
10347 sf.fAutoMount = !!pSF->i_isAutoMounted();
10348 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10349
10350 data.llSharedFolders.push_back(sf);
10351 }
10352
10353 // clipboard
10354 data.clipboardMode = mHWData->mClipboardMode;
10355 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10356
10357 // drag'n'drop
10358 data.dndMode = mHWData->mDnDMode;
10359
10360 /* Guest */
10361 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10362
10363 // IO settings
10364 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10365 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10366
10367 /* BandwidthControl (required) */
10368 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10369 if (FAILED(rc)) throw rc;
10370
10371 /* Host PCI devices */
10372 data.pciAttachments.clear();
10373 for (HWData::PCIDeviceAssignmentList::const_iterator
10374 it = mHWData->mPCIDeviceAssignments.begin();
10375 it != mHWData->mPCIDeviceAssignments.end();
10376 ++it)
10377 {
10378 ComObjPtr<PCIDeviceAttachment> pda = *it;
10379 settings::HostPCIDeviceAttachment hpda;
10380
10381 rc = pda->i_saveSettings(hpda);
10382 if (FAILED(rc)) throw rc;
10383
10384 data.pciAttachments.push_back(hpda);
10385 }
10386
10387 // guest properties
10388 data.llGuestProperties.clear();
10389#ifdef VBOX_WITH_GUEST_PROPS
10390 for (HWData::GuestPropertyMap::const_iterator
10391 it = mHWData->mGuestProperties.begin();
10392 it != mHWData->mGuestProperties.end();
10393 ++it)
10394 {
10395 HWData::GuestProperty property = it->second;
10396
10397 /* Remove transient guest properties at shutdown unless we
10398 * are saving state. Note that restoring snapshot intentionally
10399 * keeps them, they will be removed if appropriate once the final
10400 * machine state is set (as crashes etc. need to work). */
10401 if ( ( mData->mMachineState == MachineState_PoweredOff
10402 || mData->mMachineState == MachineState_Aborted
10403 || mData->mMachineState == MachineState_Teleported)
10404 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10405 continue;
10406 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10407 prop.strName = it->first;
10408 prop.strValue = property.strValue;
10409 prop.timestamp = (uint64_t)property.mTimestamp;
10410 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10411 GuestPropWriteFlags(property.mFlags, szFlags);
10412 prop.strFlags = szFlags;
10413
10414 data.llGuestProperties.push_back(prop);
10415 }
10416
10417 /* I presume this doesn't require a backup(). */
10418 mData->mGuestPropertiesModified = FALSE;
10419#endif /* VBOX_WITH_GUEST_PROPS defined */
10420
10421 *pDbg = mHWData->mDebugging;
10422 *pAutostart = mHWData->mAutostart;
10423
10424 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10425 }
10426 catch (std::bad_alloc &)
10427 {
10428 return E_OUTOFMEMORY;
10429 }
10430
10431 AssertComRC(rc);
10432 return rc;
10433}
10434
10435/**
10436 * Saves the storage controller configuration.
10437 *
10438 * @param data storage settings.
10439 */
10440HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10441{
10442 data.llStorageControllers.clear();
10443
10444 for (StorageControllerList::const_iterator
10445 it = mStorageControllers->begin();
10446 it != mStorageControllers->end();
10447 ++it)
10448 {
10449 HRESULT rc;
10450 ComObjPtr<StorageController> pCtl = *it;
10451
10452 settings::StorageController ctl;
10453 ctl.strName = pCtl->i_getName();
10454 ctl.controllerType = pCtl->i_getControllerType();
10455 ctl.storageBus = pCtl->i_getStorageBus();
10456 ctl.ulInstance = pCtl->i_getInstance();
10457 ctl.fBootable = pCtl->i_getBootable();
10458
10459 /* Save the port count. */
10460 ULONG portCount;
10461 rc = pCtl->COMGETTER(PortCount)(&portCount);
10462 ComAssertComRCRet(rc, rc);
10463 ctl.ulPortCount = portCount;
10464
10465 /* Save fUseHostIOCache */
10466 BOOL fUseHostIOCache;
10467 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10468 ComAssertComRCRet(rc, rc);
10469 ctl.fUseHostIOCache = !!fUseHostIOCache;
10470
10471 /* save the devices now. */
10472 rc = i_saveStorageDevices(pCtl, ctl);
10473 ComAssertComRCRet(rc, rc);
10474
10475 data.llStorageControllers.push_back(ctl);
10476 }
10477
10478 return S_OK;
10479}
10480
10481/**
10482 * Saves the hard disk configuration.
10483 */
10484HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10485 settings::StorageController &data)
10486{
10487 MediumAttachmentList atts;
10488
10489 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10490 if (FAILED(rc)) return rc;
10491
10492 data.llAttachedDevices.clear();
10493 for (MediumAttachmentList::const_iterator
10494 it = atts.begin();
10495 it != atts.end();
10496 ++it)
10497 {
10498 settings::AttachedDevice dev;
10499 IMediumAttachment *iA = *it;
10500 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10501 Medium *pMedium = pAttach->i_getMedium();
10502
10503 dev.deviceType = pAttach->i_getType();
10504 dev.lPort = pAttach->i_getPort();
10505 dev.lDevice = pAttach->i_getDevice();
10506 dev.fPassThrough = pAttach->i_getPassthrough();
10507 dev.fHotPluggable = pAttach->i_getHotPluggable();
10508 if (pMedium)
10509 {
10510 if (pMedium->i_isHostDrive())
10511 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10512 else
10513 dev.uuid = pMedium->i_getId();
10514 dev.fTempEject = pAttach->i_getTempEject();
10515 dev.fNonRotational = pAttach->i_getNonRotational();
10516 dev.fDiscard = pAttach->i_getDiscard();
10517 }
10518
10519 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10520
10521 data.llAttachedDevices.push_back(dev);
10522 }
10523
10524 return S_OK;
10525}
10526
10527/**
10528 * Saves machine state settings as defined by aFlags
10529 * (SaveSTS_* values).
10530 *
10531 * @param aFlags Combination of SaveSTS_* flags.
10532 *
10533 * @note Locks objects for writing.
10534 */
10535HRESULT Machine::i_saveStateSettings(int aFlags)
10536{
10537 if (aFlags == 0)
10538 return S_OK;
10539
10540 AutoCaller autoCaller(this);
10541 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10542
10543 /* This object's write lock is also necessary to serialize file access
10544 * (prevent concurrent reads and writes) */
10545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10546
10547 HRESULT rc = S_OK;
10548
10549 Assert(mData->pMachineConfigFile);
10550
10551 try
10552 {
10553 if (aFlags & SaveSTS_CurStateModified)
10554 mData->pMachineConfigFile->fCurrentStateModified = true;
10555
10556 if (aFlags & SaveSTS_StateFilePath)
10557 {
10558 if (!mSSData->strStateFilePath.isEmpty())
10559 /* try to make the file name relative to the settings file dir */
10560 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10561 else
10562 mData->pMachineConfigFile->strStateFile.setNull();
10563 }
10564
10565 if (aFlags & SaveSTS_StateTimeStamp)
10566 {
10567 Assert( mData->mMachineState != MachineState_Aborted
10568 || mSSData->strStateFilePath.isEmpty());
10569
10570 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10571
10572 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10573 || mData->mMachineState == MachineState_AbortedSaved);
10574/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10575 }
10576
10577 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10578 }
10579 catch (...)
10580 {
10581 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10582 }
10583
10584 return rc;
10585}
10586
10587/**
10588 * Ensures that the given medium is added to a media registry. If this machine
10589 * was created with 4.0 or later, then the machine registry is used. Otherwise
10590 * the global VirtualBox media registry is used.
10591 *
10592 * Caller must NOT hold machine lock, media tree or any medium locks!
10593 *
10594 * @param pMedium
10595 */
10596void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10597{
10598 /* Paranoia checks: do not hold machine or media tree locks. */
10599 AssertReturnVoid(!isWriteLockOnCurrentThread());
10600 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10601
10602 ComObjPtr<Medium> pBase;
10603 {
10604 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10605 pBase = pMedium->i_getBase();
10606 }
10607
10608 /* Paranoia checks: do not hold medium locks. */
10609 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10610 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10611
10612 // decide which medium registry to use now that the medium is attached:
10613 Guid uuid;
10614 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10615 if (fCanHaveOwnMediaRegistry)
10616 // machine XML is VirtualBox 4.0 or higher:
10617 uuid = i_getId(); // machine UUID
10618 else
10619 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10620
10621 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10622 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10623 if (pMedium->i_addRegistry(uuid))
10624 mParent->i_markRegistryModified(uuid);
10625
10626 /* For more complex hard disk structures it can happen that the base
10627 * medium isn't yet associated with any medium registry. Do that now. */
10628 if (pMedium != pBase)
10629 {
10630 /* Tree lock needed by Medium::addRegistry when recursing. */
10631 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10632 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10633 {
10634 treeLock.release();
10635 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10636 treeLock.acquire();
10637 }
10638 if (pBase->i_addRegistryRecursive(uuid))
10639 {
10640 treeLock.release();
10641 mParent->i_markRegistryModified(uuid);
10642 }
10643 }
10644}
10645
10646/**
10647 * Creates differencing hard disks for all normal hard disks attached to this
10648 * machine and a new set of attachments to refer to created disks.
10649 *
10650 * Used when taking a snapshot or when deleting the current state. Gets called
10651 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10652 *
10653 * This method assumes that mMediumAttachments contains the original hard disk
10654 * attachments it needs to create diffs for. On success, these attachments will
10655 * be replaced with the created diffs.
10656 *
10657 * Attachments with non-normal hard disks are left as is.
10658 *
10659 * If @a aOnline is @c false then the original hard disks that require implicit
10660 * diffs will be locked for reading. Otherwise it is assumed that they are
10661 * already locked for writing (when the VM was started). Note that in the latter
10662 * case it is responsibility of the caller to lock the newly created diffs for
10663 * writing if this method succeeds.
10664 *
10665 * @param aProgress Progress object to run (must contain at least as
10666 * many operations left as the number of hard disks
10667 * attached).
10668 * @param aWeight Weight of this operation.
10669 * @param aOnline Whether the VM was online prior to this operation.
10670 *
10671 * @note The progress object is not marked as completed, neither on success nor
10672 * on failure. This is a responsibility of the caller.
10673 *
10674 * @note Locks this object and the media tree for writing.
10675 */
10676HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10677 ULONG aWeight,
10678 bool aOnline)
10679{
10680 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10681
10682 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10683 AssertReturn(!!pProgressControl, E_INVALIDARG);
10684
10685 AutoCaller autoCaller(this);
10686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10687
10688 AutoMultiWriteLock2 alock(this->lockHandle(),
10689 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10690
10691 /* must be in a protective state because we release the lock below */
10692 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10693 || mData->mMachineState == MachineState_OnlineSnapshotting
10694 || mData->mMachineState == MachineState_LiveSnapshotting
10695 || mData->mMachineState == MachineState_RestoringSnapshot
10696 || mData->mMachineState == MachineState_DeletingSnapshot
10697 , E_FAIL);
10698
10699 HRESULT rc = S_OK;
10700
10701 // use appropriate locked media map (online or offline)
10702 MediumLockListMap lockedMediaOffline;
10703 MediumLockListMap *lockedMediaMap;
10704 if (aOnline)
10705 lockedMediaMap = &mData->mSession.mLockedMedia;
10706 else
10707 lockedMediaMap = &lockedMediaOffline;
10708
10709 try
10710 {
10711 if (!aOnline)
10712 {
10713 /* lock all attached hard disks early to detect "in use"
10714 * situations before creating actual diffs */
10715 for (MediumAttachmentList::const_iterator
10716 it = mMediumAttachments->begin();
10717 it != mMediumAttachments->end();
10718 ++it)
10719 {
10720 MediumAttachment *pAtt = *it;
10721 if (pAtt->i_getType() == DeviceType_HardDisk)
10722 {
10723 Medium *pMedium = pAtt->i_getMedium();
10724 Assert(pMedium);
10725
10726 MediumLockList *pMediumLockList(new MediumLockList());
10727 alock.release();
10728 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10729 NULL /* pToLockWrite */,
10730 false /* fMediumLockWriteAll */,
10731 NULL,
10732 *pMediumLockList);
10733 alock.acquire();
10734 if (FAILED(rc))
10735 {
10736 delete pMediumLockList;
10737 throw rc;
10738 }
10739 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10740 if (FAILED(rc))
10741 {
10742 throw setError(rc,
10743 tr("Collecting locking information for all attached media failed"));
10744 }
10745 }
10746 }
10747
10748 /* Now lock all media. If this fails, nothing is locked. */
10749 alock.release();
10750 rc = lockedMediaMap->Lock();
10751 alock.acquire();
10752 if (FAILED(rc))
10753 {
10754 throw setError(rc,
10755 tr("Locking of attached media failed"));
10756 }
10757 }
10758
10759 /* remember the current list (note that we don't use backup() since
10760 * mMediumAttachments may be already backed up) */
10761 MediumAttachmentList atts = *mMediumAttachments.data();
10762
10763 /* start from scratch */
10764 mMediumAttachments->clear();
10765
10766 /* go through remembered attachments and create diffs for normal hard
10767 * disks and attach them */
10768 for (MediumAttachmentList::const_iterator
10769 it = atts.begin();
10770 it != atts.end();
10771 ++it)
10772 {
10773 MediumAttachment *pAtt = *it;
10774
10775 DeviceType_T devType = pAtt->i_getType();
10776 Medium *pMedium = pAtt->i_getMedium();
10777
10778 if ( devType != DeviceType_HardDisk
10779 || pMedium == NULL
10780 || pMedium->i_getType() != MediumType_Normal)
10781 {
10782 /* copy the attachment as is */
10783
10784 /** @todo the progress object created in SessionMachine::TakeSnaphot
10785 * only expects operations for hard disks. Later other
10786 * device types need to show up in the progress as well. */
10787 if (devType == DeviceType_HardDisk)
10788 {
10789 if (pMedium == NULL)
10790 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10791 aWeight); // weight
10792 else
10793 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10794 pMedium->i_getBase()->i_getName().c_str()).raw(),
10795 aWeight); // weight
10796 }
10797
10798 mMediumAttachments->push_back(pAtt);
10799 continue;
10800 }
10801
10802 /* need a diff */
10803 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10804 pMedium->i_getBase()->i_getName().c_str()).raw(),
10805 aWeight); // weight
10806
10807 Utf8Str strFullSnapshotFolder;
10808 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10809
10810 ComObjPtr<Medium> diff;
10811 diff.createObject();
10812 // store the diff in the same registry as the parent
10813 // (this cannot fail here because we can't create implicit diffs for
10814 // unregistered images)
10815 Guid uuidRegistryParent;
10816 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10817 Assert(fInRegistry); NOREF(fInRegistry);
10818 rc = diff->init(mParent,
10819 pMedium->i_getPreferredDiffFormat(),
10820 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10821 uuidRegistryParent,
10822 DeviceType_HardDisk);
10823 if (FAILED(rc)) throw rc;
10824
10825 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10826 * the push_back? Looks like we're going to release medium with the
10827 * wrong kind of lock (general issue with if we fail anywhere at all)
10828 * and an orphaned VDI in the snapshots folder. */
10829
10830 /* update the appropriate lock list */
10831 MediumLockList *pMediumLockList;
10832 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10833 AssertComRCThrowRC(rc);
10834 if (aOnline)
10835 {
10836 alock.release();
10837 /* The currently attached medium will be read-only, change
10838 * the lock type to read. */
10839 rc = pMediumLockList->Update(pMedium, false);
10840 alock.acquire();
10841 AssertComRCThrowRC(rc);
10842 }
10843
10844 /* release the locks before the potentially lengthy operation */
10845 alock.release();
10846 rc = pMedium->i_createDiffStorage(diff,
10847 pMedium->i_getPreferredDiffVariant(),
10848 pMediumLockList,
10849 NULL /* aProgress */,
10850 true /* aWait */,
10851 false /* aNotify */);
10852 alock.acquire();
10853 if (FAILED(rc)) throw rc;
10854
10855 /* actual lock list update is done in Machine::i_commitMedia */
10856
10857 rc = diff->i_addBackReference(mData->mUuid);
10858 AssertComRCThrowRC(rc);
10859
10860 /* add a new attachment */
10861 ComObjPtr<MediumAttachment> attachment;
10862 attachment.createObject();
10863 rc = attachment->init(this,
10864 diff,
10865 pAtt->i_getControllerName(),
10866 pAtt->i_getPort(),
10867 pAtt->i_getDevice(),
10868 DeviceType_HardDisk,
10869 true /* aImplicit */,
10870 false /* aPassthrough */,
10871 false /* aTempEject */,
10872 pAtt->i_getNonRotational(),
10873 pAtt->i_getDiscard(),
10874 pAtt->i_getHotPluggable(),
10875 pAtt->i_getBandwidthGroup());
10876 if (FAILED(rc)) throw rc;
10877
10878 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10879 AssertComRCThrowRC(rc);
10880 mMediumAttachments->push_back(attachment);
10881 }
10882 }
10883 catch (HRESULT aRC) { rc = aRC; }
10884
10885 /* unlock all hard disks we locked when there is no VM */
10886 if (!aOnline)
10887 {
10888 ErrorInfoKeeper eik;
10889
10890 HRESULT rc1 = lockedMediaMap->Clear();
10891 AssertComRC(rc1);
10892 }
10893
10894 return rc;
10895}
10896
10897/**
10898 * Deletes implicit differencing hard disks created either by
10899 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10900 * mMediumAttachments.
10901 *
10902 * Note that to delete hard disks created by #attachDevice() this method is
10903 * called from #i_rollbackMedia() when the changes are rolled back.
10904 *
10905 * @note Locks this object and the media tree for writing.
10906 */
10907HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10908{
10909 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10910
10911 AutoCaller autoCaller(this);
10912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10913
10914 AutoMultiWriteLock2 alock(this->lockHandle(),
10915 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10916
10917 /* We absolutely must have backed up state. */
10918 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10919
10920 /* Check if there are any implicitly created diff images. */
10921 bool fImplicitDiffs = false;
10922 for (MediumAttachmentList::const_iterator
10923 it = mMediumAttachments->begin();
10924 it != mMediumAttachments->end();
10925 ++it)
10926 {
10927 const ComObjPtr<MediumAttachment> &pAtt = *it;
10928 if (pAtt->i_isImplicit())
10929 {
10930 fImplicitDiffs = true;
10931 break;
10932 }
10933 }
10934 /* If there is nothing to do, leave early. This saves lots of image locking
10935 * effort. It also avoids a MachineStateChanged event without real reason.
10936 * This is important e.g. when loading a VM config, because there should be
10937 * no events. Otherwise API clients can become thoroughly confused for
10938 * inaccessible VMs (the code for loading VM configs uses this method for
10939 * cleanup if the config makes no sense), as they take such events as an
10940 * indication that the VM is alive, and they would force the VM config to
10941 * be reread, leading to an endless loop. */
10942 if (!fImplicitDiffs)
10943 return S_OK;
10944
10945 HRESULT rc = S_OK;
10946 MachineState_T oldState = mData->mMachineState;
10947
10948 /* will release the lock before the potentially lengthy operation,
10949 * so protect with the special state (unless already protected) */
10950 if ( oldState != MachineState_Snapshotting
10951 && oldState != MachineState_OnlineSnapshotting
10952 && oldState != MachineState_LiveSnapshotting
10953 && oldState != MachineState_RestoringSnapshot
10954 && oldState != MachineState_DeletingSnapshot
10955 && oldState != MachineState_DeletingSnapshotOnline
10956 && oldState != MachineState_DeletingSnapshotPaused
10957 )
10958 i_setMachineState(MachineState_SettingUp);
10959
10960 // use appropriate locked media map (online or offline)
10961 MediumLockListMap lockedMediaOffline;
10962 MediumLockListMap *lockedMediaMap;
10963 if (aOnline)
10964 lockedMediaMap = &mData->mSession.mLockedMedia;
10965 else
10966 lockedMediaMap = &lockedMediaOffline;
10967
10968 try
10969 {
10970 if (!aOnline)
10971 {
10972 /* lock all attached hard disks early to detect "in use"
10973 * situations before deleting actual diffs */
10974 for (MediumAttachmentList::const_iterator
10975 it = mMediumAttachments->begin();
10976 it != mMediumAttachments->end();
10977 ++it)
10978 {
10979 MediumAttachment *pAtt = *it;
10980 if (pAtt->i_getType() == DeviceType_HardDisk)
10981 {
10982 Medium *pMedium = pAtt->i_getMedium();
10983 Assert(pMedium);
10984
10985 MediumLockList *pMediumLockList(new MediumLockList());
10986 alock.release();
10987 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10988 NULL /* pToLockWrite */,
10989 false /* fMediumLockWriteAll */,
10990 NULL,
10991 *pMediumLockList);
10992 alock.acquire();
10993
10994 if (FAILED(rc))
10995 {
10996 delete pMediumLockList;
10997 throw rc;
10998 }
10999
11000 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11001 if (FAILED(rc))
11002 throw rc;
11003 }
11004 }
11005
11006 if (FAILED(rc))
11007 throw rc;
11008 } // end of offline
11009
11010 /* Lock lists are now up to date and include implicitly created media */
11011
11012 /* Go through remembered attachments and delete all implicitly created
11013 * diffs and fix up the attachment information */
11014 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11015 MediumAttachmentList implicitAtts;
11016 for (MediumAttachmentList::const_iterator
11017 it = mMediumAttachments->begin();
11018 it != mMediumAttachments->end();
11019 ++it)
11020 {
11021 ComObjPtr<MediumAttachment> pAtt = *it;
11022 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11023 if (pMedium.isNull())
11024 continue;
11025
11026 // Implicit attachments go on the list for deletion and back references are removed.
11027 if (pAtt->i_isImplicit())
11028 {
11029 /* Deassociate and mark for deletion */
11030 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11031 rc = pMedium->i_removeBackReference(mData->mUuid);
11032 if (FAILED(rc))
11033 throw rc;
11034 implicitAtts.push_back(pAtt);
11035 continue;
11036 }
11037
11038 /* Was this medium attached before? */
11039 if (!i_findAttachment(oldAtts, pMedium))
11040 {
11041 /* no: de-associate */
11042 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11043 rc = pMedium->i_removeBackReference(mData->mUuid);
11044 if (FAILED(rc))
11045 throw rc;
11046 continue;
11047 }
11048 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11049 }
11050
11051 /* If there are implicit attachments to delete, throw away the lock
11052 * map contents (which will unlock all media) since the medium
11053 * attachments will be rolled back. Below we need to completely
11054 * recreate the lock map anyway since it is infinitely complex to
11055 * do this incrementally (would need reconstructing each attachment
11056 * change, which would be extremely hairy). */
11057 if (implicitAtts.size() != 0)
11058 {
11059 ErrorInfoKeeper eik;
11060
11061 HRESULT rc1 = lockedMediaMap->Clear();
11062 AssertComRC(rc1);
11063 }
11064
11065 /* rollback hard disk changes */
11066 mMediumAttachments.rollback();
11067
11068 MultiResult mrc(S_OK);
11069
11070 // Delete unused implicit diffs.
11071 if (implicitAtts.size() != 0)
11072 {
11073 alock.release();
11074
11075 for (MediumAttachmentList::const_iterator
11076 it = implicitAtts.begin();
11077 it != implicitAtts.end();
11078 ++it)
11079 {
11080 // Remove medium associated with this attachment.
11081 ComObjPtr<MediumAttachment> pAtt = *it;
11082 Assert(pAtt);
11083 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11084 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11085 Assert(pMedium);
11086
11087 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11088 // continue on delete failure, just collect error messages
11089 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11090 pMedium->i_getLocationFull().c_str() ));
11091 mrc = rc;
11092 }
11093 // Clear the list of deleted implicit attachments now, while not
11094 // holding the lock, as it will ultimately trigger Medium::uninit()
11095 // calls which assume that the media tree lock isn't held.
11096 implicitAtts.clear();
11097
11098 alock.acquire();
11099
11100 /* if there is a VM recreate media lock map as mentioned above,
11101 * otherwise it is a waste of time and we leave things unlocked */
11102 if (aOnline)
11103 {
11104 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11105 /* must never be NULL, but better safe than sorry */
11106 if (!pMachine.isNull())
11107 {
11108 alock.release();
11109 rc = mData->mSession.mMachine->i_lockMedia();
11110 alock.acquire();
11111 if (FAILED(rc))
11112 throw rc;
11113 }
11114 }
11115 }
11116 }
11117 catch (HRESULT aRC) {rc = aRC;}
11118
11119 if (mData->mMachineState == MachineState_SettingUp)
11120 i_setMachineState(oldState);
11121
11122 /* unlock all hard disks we locked when there is no VM */
11123 if (!aOnline)
11124 {
11125 ErrorInfoKeeper eik;
11126
11127 HRESULT rc1 = lockedMediaMap->Clear();
11128 AssertComRC(rc1);
11129 }
11130
11131 return rc;
11132}
11133
11134
11135/**
11136 * Looks through the given list of media attachments for one with the given parameters
11137 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11138 * can be searched as well if needed.
11139 *
11140 * @param ll
11141 * @param aControllerName
11142 * @param aControllerPort
11143 * @param aDevice
11144 * @return
11145 */
11146MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11147 const Utf8Str &aControllerName,
11148 LONG aControllerPort,
11149 LONG aDevice)
11150{
11151 for (MediumAttachmentList::const_iterator
11152 it = ll.begin();
11153 it != ll.end();
11154 ++it)
11155 {
11156 MediumAttachment *pAttach = *it;
11157 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11158 return pAttach;
11159 }
11160
11161 return NULL;
11162}
11163
11164/**
11165 * Looks through the given list of media attachments for one with the given parameters
11166 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11167 * can be searched as well if needed.
11168 *
11169 * @param ll
11170 * @param pMedium
11171 * @return
11172 */
11173MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11174 ComObjPtr<Medium> pMedium)
11175{
11176 for (MediumAttachmentList::const_iterator
11177 it = ll.begin();
11178 it != ll.end();
11179 ++it)
11180 {
11181 MediumAttachment *pAttach = *it;
11182 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11183 if (pMediumThis == pMedium)
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 id
11197 * @return
11198 */
11199MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11200 Guid &id)
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->i_getId() == id)
11210 return pAttach;
11211 }
11212
11213 return NULL;
11214}
11215
11216/**
11217 * Main implementation for Machine::DetachDevice. This also gets called
11218 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11219 *
11220 * @param pAttach Medium attachment to detach.
11221 * @param writeLock Machine write lock which the caller must have locked once.
11222 * This may be released temporarily in here.
11223 * @param pSnapshot If NULL, then the detachment is for the current machine.
11224 * Otherwise this is for a SnapshotMachine, and this must be
11225 * its snapshot.
11226 * @return
11227 */
11228HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11229 AutoWriteLock &writeLock,
11230 Snapshot *pSnapshot)
11231{
11232 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11233 DeviceType_T mediumType = pAttach->i_getType();
11234
11235 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11236
11237 if (pAttach->i_isImplicit())
11238 {
11239 /* attempt to implicitly delete the implicitly created diff */
11240
11241 /// @todo move the implicit flag from MediumAttachment to Medium
11242 /// and forbid any hard disk operation when it is implicit. Or maybe
11243 /// a special media state for it to make it even more simple.
11244
11245 Assert(mMediumAttachments.isBackedUp());
11246
11247 /* will release the lock before the potentially lengthy operation, so
11248 * protect with the special state */
11249 MachineState_T oldState = mData->mMachineState;
11250 i_setMachineState(MachineState_SettingUp);
11251
11252 writeLock.release();
11253
11254 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11255 true /*aWait*/,
11256 false /*aNotify*/);
11257
11258 writeLock.acquire();
11259
11260 i_setMachineState(oldState);
11261
11262 if (FAILED(rc)) return rc;
11263 }
11264
11265 i_setModified(IsModified_Storage);
11266 mMediumAttachments.backup();
11267 mMediumAttachments->remove(pAttach);
11268
11269 if (!oldmedium.isNull())
11270 {
11271 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11272 if (pSnapshot)
11273 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11274 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11275 else if (mediumType != DeviceType_HardDisk)
11276 oldmedium->i_removeBackReference(mData->mUuid);
11277 }
11278
11279 return S_OK;
11280}
11281
11282/**
11283 * Goes thru all media of the given list and
11284 *
11285 * 1) calls i_detachDevice() on each of them for this machine and
11286 * 2) adds all Medium objects found in the process to the given list,
11287 * depending on cleanupMode.
11288 *
11289 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11290 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11291 * media to the list.
11292 *
11293 * This gets called from Machine::Unregister, both for the actual Machine and
11294 * the SnapshotMachine objects that might be found in the snapshots.
11295 *
11296 * Requires caller and locking. The machine lock must be passed in because it
11297 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11298 *
11299 * @param writeLock Machine lock from top-level caller; this gets passed to
11300 * i_detachDevice.
11301 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11302 * object if called for a SnapshotMachine.
11303 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11304 * added to llMedia; if Full, then all media get added;
11305 * otherwise no media get added.
11306 * @param llMedia Caller's list to receive Medium objects which got detached so
11307 * caller can close() them, depending on cleanupMode.
11308 * @return
11309 */
11310HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11311 Snapshot *pSnapshot,
11312 CleanupMode_T cleanupMode,
11313 MediaList &llMedia)
11314{
11315 Assert(isWriteLockOnCurrentThread());
11316
11317 HRESULT rc;
11318
11319 // make a temporary list because i_detachDevice invalidates iterators into
11320 // mMediumAttachments
11321 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11322
11323 for (MediumAttachmentList::iterator
11324 it = llAttachments2.begin();
11325 it != llAttachments2.end();
11326 ++it)
11327 {
11328 ComObjPtr<MediumAttachment> &pAttach = *it;
11329 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11330
11331 if (!pMedium.isNull())
11332 {
11333 AutoCaller mac(pMedium);
11334 if (FAILED(mac.rc())) return mac.rc();
11335 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11336 DeviceType_T devType = pMedium->i_getDeviceType();
11337 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11338 && devType == DeviceType_HardDisk)
11339 || (cleanupMode == CleanupMode_Full)
11340 )
11341 {
11342 llMedia.push_back(pMedium);
11343 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11344 /* Not allowed to keep this lock as below we need the parent
11345 * medium lock, and the lock order is parent to child. */
11346 lock.release();
11347 /*
11348 * Search for medias which are not attached to any machine, but
11349 * in the chain to an attached disk. Mediums are only consided
11350 * if they are:
11351 * - have only one child
11352 * - no references to any machines
11353 * - are of normal medium type
11354 */
11355 while (!pParent.isNull())
11356 {
11357 AutoCaller mac1(pParent);
11358 if (FAILED(mac1.rc())) return mac1.rc();
11359 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11360 if (pParent->i_getChildren().size() == 1)
11361 {
11362 if ( pParent->i_getMachineBackRefCount() == 0
11363 && pParent->i_getType() == MediumType_Normal
11364 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11365 llMedia.push_back(pParent);
11366 }
11367 else
11368 break;
11369 pParent = pParent->i_getParent();
11370 }
11371 }
11372 }
11373
11374 // real machine: then we need to use the proper method
11375 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11376
11377 if (FAILED(rc))
11378 return rc;
11379 }
11380
11381 return S_OK;
11382}
11383
11384/**
11385 * Perform deferred hard disk detachments.
11386 *
11387 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11388 * changed (not backed up).
11389 *
11390 * If @a aOnline is @c true then this method will also unlock the old hard
11391 * disks for which the new implicit diffs were created and will lock these new
11392 * diffs for writing.
11393 *
11394 * @param aOnline Whether the VM was online prior to this operation.
11395 *
11396 * @note Locks this object for writing!
11397 */
11398void Machine::i_commitMedia(bool aOnline /*= false*/)
11399{
11400 AutoCaller autoCaller(this);
11401 AssertComRCReturnVoid(autoCaller.rc());
11402
11403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11404
11405 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11406
11407 HRESULT rc = S_OK;
11408
11409 /* no attach/detach operations -- nothing to do */
11410 if (!mMediumAttachments.isBackedUp())
11411 return;
11412
11413 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11414 bool fMediaNeedsLocking = false;
11415
11416 /* enumerate new attachments */
11417 for (MediumAttachmentList::const_iterator
11418 it = mMediumAttachments->begin();
11419 it != mMediumAttachments->end();
11420 ++it)
11421 {
11422 MediumAttachment *pAttach = *it;
11423
11424 pAttach->i_commit();
11425
11426 Medium *pMedium = pAttach->i_getMedium();
11427 bool fImplicit = pAttach->i_isImplicit();
11428
11429 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11430 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11431 fImplicit));
11432
11433 /** @todo convert all this Machine-based voodoo to MediumAttachment
11434 * based commit logic. */
11435 if (fImplicit)
11436 {
11437 /* convert implicit attachment to normal */
11438 pAttach->i_setImplicit(false);
11439
11440 if ( aOnline
11441 && pMedium
11442 && pAttach->i_getType() == DeviceType_HardDisk
11443 )
11444 {
11445 /* update the appropriate lock list */
11446 MediumLockList *pMediumLockList;
11447 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11448 AssertComRC(rc);
11449 if (pMediumLockList)
11450 {
11451 /* unlock if there's a need to change the locking */
11452 if (!fMediaNeedsLocking)
11453 {
11454 rc = mData->mSession.mLockedMedia.Unlock();
11455 AssertComRC(rc);
11456 fMediaNeedsLocking = true;
11457 }
11458 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11459 AssertComRC(rc);
11460 rc = pMediumLockList->Append(pMedium, true);
11461 AssertComRC(rc);
11462 }
11463 }
11464
11465 continue;
11466 }
11467
11468 if (pMedium)
11469 {
11470 /* was this medium attached before? */
11471 for (MediumAttachmentList::iterator
11472 oldIt = oldAtts.begin();
11473 oldIt != oldAtts.end();
11474 ++oldIt)
11475 {
11476 MediumAttachment *pOldAttach = *oldIt;
11477 if (pOldAttach->i_getMedium() == pMedium)
11478 {
11479 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11480
11481 /* yes: remove from old to avoid de-association */
11482 oldAtts.erase(oldIt);
11483 break;
11484 }
11485 }
11486 }
11487 }
11488
11489 /* enumerate remaining old attachments and de-associate from the
11490 * current machine state */
11491 for (MediumAttachmentList::const_iterator
11492 it = oldAtts.begin();
11493 it != oldAtts.end();
11494 ++it)
11495 {
11496 MediumAttachment *pAttach = *it;
11497 Medium *pMedium = pAttach->i_getMedium();
11498
11499 /* Detach only hard disks, since DVD/floppy media is detached
11500 * instantly in MountMedium. */
11501 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11502 {
11503 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11504
11505 /* now de-associate from the current machine state */
11506 rc = pMedium->i_removeBackReference(mData->mUuid);
11507 AssertComRC(rc);
11508
11509 if (aOnline)
11510 {
11511 /* unlock since medium is not used anymore */
11512 MediumLockList *pMediumLockList;
11513 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11514 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11515 {
11516 /* this happens for online snapshots, there the attachment
11517 * is changing, but only to a diff image created under
11518 * the old one, so there is no separate lock list */
11519 Assert(!pMediumLockList);
11520 }
11521 else
11522 {
11523 AssertComRC(rc);
11524 if (pMediumLockList)
11525 {
11526 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11527 AssertComRC(rc);
11528 }
11529 }
11530 }
11531 }
11532 }
11533
11534 /* take media locks again so that the locking state is consistent */
11535 if (fMediaNeedsLocking)
11536 {
11537 Assert(aOnline);
11538 rc = mData->mSession.mLockedMedia.Lock();
11539 AssertComRC(rc);
11540 }
11541
11542 /* commit the hard disk changes */
11543 mMediumAttachments.commit();
11544
11545 if (i_isSessionMachine())
11546 {
11547 /*
11548 * Update the parent machine to point to the new owner.
11549 * This is necessary because the stored parent will point to the
11550 * session machine otherwise and cause crashes or errors later
11551 * when the session machine gets invalid.
11552 */
11553 /** @todo Change the MediumAttachment class to behave like any other
11554 * class in this regard by creating peer MediumAttachment
11555 * objects for session machines and share the data with the peer
11556 * machine.
11557 */
11558 for (MediumAttachmentList::const_iterator
11559 it = mMediumAttachments->begin();
11560 it != mMediumAttachments->end();
11561 ++it)
11562 (*it)->i_updateParentMachine(mPeer);
11563
11564 /* attach new data to the primary machine and reshare it */
11565 mPeer->mMediumAttachments.attach(mMediumAttachments);
11566 }
11567
11568 return;
11569}
11570
11571/**
11572 * Perform deferred deletion of implicitly created diffs.
11573 *
11574 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11575 * changed (not backed up).
11576 *
11577 * @note Locks this object for writing!
11578 */
11579void Machine::i_rollbackMedia()
11580{
11581 AutoCaller autoCaller(this);
11582 AssertComRCReturnVoid(autoCaller.rc());
11583
11584 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11585 LogFlowThisFunc(("Entering rollbackMedia\n"));
11586
11587 HRESULT rc = S_OK;
11588
11589 /* no attach/detach operations -- nothing to do */
11590 if (!mMediumAttachments.isBackedUp())
11591 return;
11592
11593 /* enumerate new attachments */
11594 for (MediumAttachmentList::const_iterator
11595 it = mMediumAttachments->begin();
11596 it != mMediumAttachments->end();
11597 ++it)
11598 {
11599 MediumAttachment *pAttach = *it;
11600 /* Fix up the backrefs for DVD/floppy media. */
11601 if (pAttach->i_getType() != DeviceType_HardDisk)
11602 {
11603 Medium *pMedium = pAttach->i_getMedium();
11604 if (pMedium)
11605 {
11606 rc = pMedium->i_removeBackReference(mData->mUuid);
11607 AssertComRC(rc);
11608 }
11609 }
11610
11611 (*it)->i_rollback();
11612
11613 pAttach = *it;
11614 /* Fix up the backrefs for DVD/floppy media. */
11615 if (pAttach->i_getType() != DeviceType_HardDisk)
11616 {
11617 Medium *pMedium = pAttach->i_getMedium();
11618 if (pMedium)
11619 {
11620 rc = pMedium->i_addBackReference(mData->mUuid);
11621 AssertComRC(rc);
11622 }
11623 }
11624 }
11625
11626 /** @todo convert all this Machine-based voodoo to MediumAttachment
11627 * based rollback logic. */
11628 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11629
11630 return;
11631}
11632
11633/**
11634 * Returns true if the settings file is located in the directory named exactly
11635 * as the machine; this means, among other things, that the machine directory
11636 * should be auto-renamed.
11637 *
11638 * @param aSettingsDir if not NULL, the full machine settings file directory
11639 * name will be assigned there.
11640 *
11641 * @note Doesn't lock anything.
11642 * @note Not thread safe (must be called from this object's lock).
11643 */
11644bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11645{
11646 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11647 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11648 if (aSettingsDir)
11649 *aSettingsDir = strMachineDirName;
11650 strMachineDirName.stripPath(); // vmname
11651 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11652 strConfigFileOnly.stripPath() // vmname.vbox
11653 .stripSuffix(); // vmname
11654 /** @todo hack, make somehow use of ComposeMachineFilename */
11655 if (mUserData->s.fDirectoryIncludesUUID)
11656 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11657
11658 AssertReturn(!strMachineDirName.isEmpty(), false);
11659 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11660
11661 return strMachineDirName == strConfigFileOnly;
11662}
11663
11664/**
11665 * Discards all changes to machine settings.
11666 *
11667 * @param aNotify Whether to notify the direct session about changes or not.
11668 *
11669 * @note Locks objects for writing!
11670 */
11671void Machine::i_rollback(bool aNotify)
11672{
11673 AutoCaller autoCaller(this);
11674 AssertComRCReturn(autoCaller.rc(), (void)0);
11675
11676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11677
11678 if (!mStorageControllers.isNull())
11679 {
11680 if (mStorageControllers.isBackedUp())
11681 {
11682 /* unitialize all new devices (absent in the backed up list). */
11683 StorageControllerList *backedList = mStorageControllers.backedUpData();
11684 for (StorageControllerList::const_iterator
11685 it = mStorageControllers->begin();
11686 it != mStorageControllers->end();
11687 ++it)
11688 {
11689 if ( std::find(backedList->begin(), backedList->end(), *it)
11690 == backedList->end()
11691 )
11692 {
11693 (*it)->uninit();
11694 }
11695 }
11696
11697 /* restore the list */
11698 mStorageControllers.rollback();
11699 }
11700
11701 /* rollback any changes to devices after restoring the list */
11702 if (mData->flModifications & IsModified_Storage)
11703 {
11704 for (StorageControllerList::const_iterator
11705 it = mStorageControllers->begin();
11706 it != mStorageControllers->end();
11707 ++it)
11708 {
11709 (*it)->i_rollback();
11710 }
11711 }
11712 }
11713
11714 if (!mUSBControllers.isNull())
11715 {
11716 if (mUSBControllers.isBackedUp())
11717 {
11718 /* unitialize all new devices (absent in the backed up list). */
11719 USBControllerList *backedList = mUSBControllers.backedUpData();
11720 for (USBControllerList::const_iterator
11721 it = mUSBControllers->begin();
11722 it != mUSBControllers->end();
11723 ++it)
11724 {
11725 if ( std::find(backedList->begin(), backedList->end(), *it)
11726 == backedList->end()
11727 )
11728 {
11729 (*it)->uninit();
11730 }
11731 }
11732
11733 /* restore the list */
11734 mUSBControllers.rollback();
11735 }
11736
11737 /* rollback any changes to devices after restoring the list */
11738 if (mData->flModifications & IsModified_USB)
11739 {
11740 for (USBControllerList::const_iterator
11741 it = mUSBControllers->begin();
11742 it != mUSBControllers->end();
11743 ++it)
11744 {
11745 (*it)->i_rollback();
11746 }
11747 }
11748 }
11749
11750 mUserData.rollback();
11751
11752 mHWData.rollback();
11753
11754 if (mData->flModifications & IsModified_Storage)
11755 i_rollbackMedia();
11756
11757 if (mBIOSSettings)
11758 mBIOSSettings->i_rollback();
11759
11760 if (mTrustedPlatformModule)
11761 mTrustedPlatformModule->i_rollback();
11762
11763 if (mNvramStore)
11764 mNvramStore->i_rollback();
11765
11766 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11767 mRecordingSettings->i_rollback();
11768
11769 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11770 mGraphicsAdapter->i_rollback();
11771
11772 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11773 mVRDEServer->i_rollback();
11774
11775 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11776 mAudioAdapter->i_rollback();
11777
11778 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11779 mUSBDeviceFilters->i_rollback();
11780
11781 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11782 mBandwidthControl->i_rollback();
11783
11784 if (!mHWData.isNull())
11785 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11786 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11787 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11788 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11789
11790 if (mData->flModifications & IsModified_NetworkAdapters)
11791 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11792 if ( mNetworkAdapters[slot]
11793 && mNetworkAdapters[slot]->i_isModified())
11794 {
11795 mNetworkAdapters[slot]->i_rollback();
11796 networkAdapters[slot] = mNetworkAdapters[slot];
11797 }
11798
11799 if (mData->flModifications & IsModified_SerialPorts)
11800 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11801 if ( mSerialPorts[slot]
11802 && mSerialPorts[slot]->i_isModified())
11803 {
11804 mSerialPorts[slot]->i_rollback();
11805 serialPorts[slot] = mSerialPorts[slot];
11806 }
11807
11808 if (mData->flModifications & IsModified_ParallelPorts)
11809 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11810 if ( mParallelPorts[slot]
11811 && mParallelPorts[slot]->i_isModified())
11812 {
11813 mParallelPorts[slot]->i_rollback();
11814 parallelPorts[slot] = mParallelPorts[slot];
11815 }
11816
11817 if (aNotify)
11818 {
11819 /* inform the direct session about changes */
11820
11821 ComObjPtr<Machine> that = this;
11822 uint32_t flModifications = mData->flModifications;
11823 alock.release();
11824
11825 if (flModifications & IsModified_SharedFolders)
11826 that->i_onSharedFolderChange();
11827
11828 if (flModifications & IsModified_VRDEServer)
11829 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11830 if (flModifications & IsModified_USB)
11831 that->i_onUSBControllerChange();
11832
11833 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11834 if (networkAdapters[slot])
11835 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11836 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11837 if (serialPorts[slot])
11838 that->i_onSerialPortChange(serialPorts[slot]);
11839 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11840 if (parallelPorts[slot])
11841 that->i_onParallelPortChange(parallelPorts[slot]);
11842
11843 if (flModifications & IsModified_Storage)
11844 {
11845 for (StorageControllerList::const_iterator
11846 it = mStorageControllers->begin();
11847 it != mStorageControllers->end();
11848 ++it)
11849 {
11850 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11851 }
11852 }
11853
11854
11855#if 0
11856 if (flModifications & IsModified_BandwidthControl)
11857 that->onBandwidthControlChange();
11858#endif
11859 }
11860}
11861
11862/**
11863 * Commits all the changes to machine settings.
11864 *
11865 * Note that this operation is supposed to never fail.
11866 *
11867 * @note Locks this object and children for writing.
11868 */
11869void Machine::i_commit()
11870{
11871 AutoCaller autoCaller(this);
11872 AssertComRCReturnVoid(autoCaller.rc());
11873
11874 AutoCaller peerCaller(mPeer);
11875 AssertComRCReturnVoid(peerCaller.rc());
11876
11877 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11878
11879 /*
11880 * use safe commit to ensure Snapshot machines (that share mUserData)
11881 * will still refer to a valid memory location
11882 */
11883 mUserData.commitCopy();
11884
11885 mHWData.commit();
11886
11887 if (mMediumAttachments.isBackedUp())
11888 i_commitMedia(Global::IsOnline(mData->mMachineState));
11889
11890 mBIOSSettings->i_commit();
11891 mTrustedPlatformModule->i_commit();
11892 mNvramStore->i_commit();
11893 mRecordingSettings->i_commit();
11894 mGraphicsAdapter->i_commit();
11895 mVRDEServer->i_commit();
11896 mAudioAdapter->i_commit();
11897 mUSBDeviceFilters->i_commit();
11898 mBandwidthControl->i_commit();
11899
11900 /* Since mNetworkAdapters is a list which might have been changed (resized)
11901 * without using the Backupable<> template we need to handle the copying
11902 * of the list entries manually, including the creation of peers for the
11903 * new objects. */
11904 bool commitNetworkAdapters = false;
11905 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11906 if (mPeer)
11907 {
11908 /* commit everything, even the ones which will go away */
11909 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11910 mNetworkAdapters[slot]->i_commit();
11911 /* copy over the new entries, creating a peer and uninit the original */
11912 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11913 for (size_t slot = 0; slot < newSize; slot++)
11914 {
11915 /* look if this adapter has a peer device */
11916 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11917 if (!peer)
11918 {
11919 /* no peer means the adapter is a newly created one;
11920 * create a peer owning data this data share it with */
11921 peer.createObject();
11922 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11923 }
11924 mPeer->mNetworkAdapters[slot] = peer;
11925 }
11926 /* uninit any no longer needed network adapters */
11927 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11928 mNetworkAdapters[slot]->uninit();
11929 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11930 {
11931 if (mPeer->mNetworkAdapters[slot])
11932 mPeer->mNetworkAdapters[slot]->uninit();
11933 }
11934 /* Keep the original network adapter count until this point, so that
11935 * discarding a chipset type change will not lose settings. */
11936 mNetworkAdapters.resize(newSize);
11937 mPeer->mNetworkAdapters.resize(newSize);
11938 }
11939 else
11940 {
11941 /* we have no peer (our parent is the newly created machine);
11942 * just commit changes to the network adapters */
11943 commitNetworkAdapters = true;
11944 }
11945 if (commitNetworkAdapters)
11946 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11947 mNetworkAdapters[slot]->i_commit();
11948
11949 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11950 mSerialPorts[slot]->i_commit();
11951 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11952 mParallelPorts[slot]->i_commit();
11953
11954 bool commitStorageControllers = false;
11955
11956 if (mStorageControllers.isBackedUp())
11957 {
11958 mStorageControllers.commit();
11959
11960 if (mPeer)
11961 {
11962 /* Commit all changes to new controllers (this will reshare data with
11963 * peers for those who have peers) */
11964 StorageControllerList *newList = new StorageControllerList();
11965 for (StorageControllerList::const_iterator
11966 it = mStorageControllers->begin();
11967 it != mStorageControllers->end();
11968 ++it)
11969 {
11970 (*it)->i_commit();
11971
11972 /* look if this controller has a peer device */
11973 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11974 if (!peer)
11975 {
11976 /* no peer means the device is a newly created one;
11977 * create a peer owning data this device share it with */
11978 peer.createObject();
11979 peer->init(mPeer, *it, true /* aReshare */);
11980 }
11981 else
11982 {
11983 /* remove peer from the old list */
11984 mPeer->mStorageControllers->remove(peer);
11985 }
11986 /* and add it to the new list */
11987 newList->push_back(peer);
11988 }
11989
11990 /* uninit old peer's controllers that are left */
11991 for (StorageControllerList::const_iterator
11992 it = mPeer->mStorageControllers->begin();
11993 it != mPeer->mStorageControllers->end();
11994 ++it)
11995 {
11996 (*it)->uninit();
11997 }
11998
11999 /* attach new list of controllers to our peer */
12000 mPeer->mStorageControllers.attach(newList);
12001 }
12002 else
12003 {
12004 /* we have no peer (our parent is the newly created machine);
12005 * just commit changes to devices */
12006 commitStorageControllers = true;
12007 }
12008 }
12009 else
12010 {
12011 /* the list of controllers itself is not changed,
12012 * just commit changes to controllers themselves */
12013 commitStorageControllers = true;
12014 }
12015
12016 if (commitStorageControllers)
12017 {
12018 for (StorageControllerList::const_iterator
12019 it = mStorageControllers->begin();
12020 it != mStorageControllers->end();
12021 ++it)
12022 {
12023 (*it)->i_commit();
12024 }
12025 }
12026
12027 bool commitUSBControllers = false;
12028
12029 if (mUSBControllers.isBackedUp())
12030 {
12031 mUSBControllers.commit();
12032
12033 if (mPeer)
12034 {
12035 /* Commit all changes to new controllers (this will reshare data with
12036 * peers for those who have peers) */
12037 USBControllerList *newList = new USBControllerList();
12038 for (USBControllerList::const_iterator
12039 it = mUSBControllers->begin();
12040 it != mUSBControllers->end();
12041 ++it)
12042 {
12043 (*it)->i_commit();
12044
12045 /* look if this controller has a peer device */
12046 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12047 if (!peer)
12048 {
12049 /* no peer means the device is a newly created one;
12050 * create a peer owning data this device share it with */
12051 peer.createObject();
12052 peer->init(mPeer, *it, true /* aReshare */);
12053 }
12054 else
12055 {
12056 /* remove peer from the old list */
12057 mPeer->mUSBControllers->remove(peer);
12058 }
12059 /* and add it to the new list */
12060 newList->push_back(peer);
12061 }
12062
12063 /* uninit old peer's controllers that are left */
12064 for (USBControllerList::const_iterator
12065 it = mPeer->mUSBControllers->begin();
12066 it != mPeer->mUSBControllers->end();
12067 ++it)
12068 {
12069 (*it)->uninit();
12070 }
12071
12072 /* attach new list of controllers to our peer */
12073 mPeer->mUSBControllers.attach(newList);
12074 }
12075 else
12076 {
12077 /* we have no peer (our parent is the newly created machine);
12078 * just commit changes to devices */
12079 commitUSBControllers = true;
12080 }
12081 }
12082 else
12083 {
12084 /* the list of controllers itself is not changed,
12085 * just commit changes to controllers themselves */
12086 commitUSBControllers = true;
12087 }
12088
12089 if (commitUSBControllers)
12090 {
12091 for (USBControllerList::const_iterator
12092 it = mUSBControllers->begin();
12093 it != mUSBControllers->end();
12094 ++it)
12095 {
12096 (*it)->i_commit();
12097 }
12098 }
12099
12100 if (i_isSessionMachine())
12101 {
12102 /* attach new data to the primary machine and reshare it */
12103 mPeer->mUserData.attach(mUserData);
12104 mPeer->mHWData.attach(mHWData);
12105 /* mmMediumAttachments is reshared by fixupMedia */
12106 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12107 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12108 }
12109}
12110
12111/**
12112 * Copies all the hardware data from the given machine.
12113 *
12114 * Currently, only called when the VM is being restored from a snapshot. In
12115 * particular, this implies that the VM is not running during this method's
12116 * call.
12117 *
12118 * @note This method must be called from under this object's lock.
12119 *
12120 * @note This method doesn't call #i_commit(), so all data remains backed up and
12121 * unsaved.
12122 */
12123void Machine::i_copyFrom(Machine *aThat)
12124{
12125 AssertReturnVoid(!i_isSnapshotMachine());
12126 AssertReturnVoid(aThat->i_isSnapshotMachine());
12127
12128 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12129
12130 mHWData.assignCopy(aThat->mHWData);
12131
12132 // create copies of all shared folders (mHWData after attaching a copy
12133 // contains just references to original objects)
12134 for (HWData::SharedFolderList::iterator
12135 it = mHWData->mSharedFolders.begin();
12136 it != mHWData->mSharedFolders.end();
12137 ++it)
12138 {
12139 ComObjPtr<SharedFolder> folder;
12140 folder.createObject();
12141 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12142 AssertComRC(rc);
12143 *it = folder;
12144 }
12145
12146 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12147 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12148 mNvramStore->i_copyFrom(aThat->mNvramStore);
12149 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12150 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12151 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12152 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12153 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12154 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12155
12156 /* create private copies of all controllers */
12157 mStorageControllers.backup();
12158 mStorageControllers->clear();
12159 for (StorageControllerList::const_iterator
12160 it = aThat->mStorageControllers->begin();
12161 it != aThat->mStorageControllers->end();
12162 ++it)
12163 {
12164 ComObjPtr<StorageController> ctrl;
12165 ctrl.createObject();
12166 ctrl->initCopy(this, *it);
12167 mStorageControllers->push_back(ctrl);
12168 }
12169
12170 /* create private copies of all USB controllers */
12171 mUSBControllers.backup();
12172 mUSBControllers->clear();
12173 for (USBControllerList::const_iterator
12174 it = aThat->mUSBControllers->begin();
12175 it != aThat->mUSBControllers->end();
12176 ++it)
12177 {
12178 ComObjPtr<USBController> ctrl;
12179 ctrl.createObject();
12180 ctrl->initCopy(this, *it);
12181 mUSBControllers->push_back(ctrl);
12182 }
12183
12184 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12185 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12186 {
12187 if (mNetworkAdapters[slot].isNotNull())
12188 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12189 else
12190 {
12191 unconst(mNetworkAdapters[slot]).createObject();
12192 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12193 }
12194 }
12195 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12196 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12197 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12198 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12199}
12200
12201/**
12202 * Returns whether the given storage controller is hotplug capable.
12203 *
12204 * @returns true if the controller supports hotplugging
12205 * false otherwise.
12206 * @param enmCtrlType The controller type to check for.
12207 */
12208bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12209{
12210 ComPtr<ISystemProperties> systemProperties;
12211 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12212 if (FAILED(rc))
12213 return false;
12214
12215 BOOL aHotplugCapable = FALSE;
12216 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12217
12218 return RT_BOOL(aHotplugCapable);
12219}
12220
12221#ifdef VBOX_WITH_RESOURCE_USAGE_API
12222
12223void Machine::i_getDiskList(MediaList &list)
12224{
12225 for (MediumAttachmentList::const_iterator
12226 it = mMediumAttachments->begin();
12227 it != mMediumAttachments->end();
12228 ++it)
12229 {
12230 MediumAttachment *pAttach = *it;
12231 /* just in case */
12232 AssertContinue(pAttach);
12233
12234 AutoCaller localAutoCallerA(pAttach);
12235 if (FAILED(localAutoCallerA.rc())) continue;
12236
12237 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12238
12239 if (pAttach->i_getType() == DeviceType_HardDisk)
12240 list.push_back(pAttach->i_getMedium());
12241 }
12242}
12243
12244void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12245{
12246 AssertReturnVoid(isWriteLockOnCurrentThread());
12247 AssertPtrReturnVoid(aCollector);
12248
12249 pm::CollectorHAL *hal = aCollector->getHAL();
12250 /* Create sub metrics */
12251 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12252 "Percentage of processor time spent in user mode by the VM process.");
12253 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12254 "Percentage of processor time spent in kernel mode by the VM process.");
12255 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12256 "Size of resident portion of VM process in memory.");
12257 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12258 "Actual size of all VM disks combined.");
12259 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12260 "Network receive rate.");
12261 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12262 "Network transmit rate.");
12263 /* Create and register base metrics */
12264 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12265 cpuLoadUser, cpuLoadKernel);
12266 aCollector->registerBaseMetric(cpuLoad);
12267 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12268 ramUsageUsed);
12269 aCollector->registerBaseMetric(ramUsage);
12270 MediaList disks;
12271 i_getDiskList(disks);
12272 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12273 diskUsageUsed);
12274 aCollector->registerBaseMetric(diskUsage);
12275
12276 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12277 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12278 new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12280 new pm::AggregateMin()));
12281 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12282 new pm::AggregateMax()));
12283 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12284 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12285 new pm::AggregateAvg()));
12286 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12287 new pm::AggregateMin()));
12288 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12289 new pm::AggregateMax()));
12290
12291 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12292 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12293 new pm::AggregateAvg()));
12294 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12295 new pm::AggregateMin()));
12296 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12297 new pm::AggregateMax()));
12298
12299 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12300 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12301 new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12303 new pm::AggregateMin()));
12304 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12305 new pm::AggregateMax()));
12306
12307
12308 /* Guest metrics collector */
12309 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12310 aCollector->registerGuest(mCollectorGuest);
12311 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12312
12313 /* Create sub metrics */
12314 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12315 "Percentage of processor time spent in user mode as seen by the guest.");
12316 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12317 "Percentage of processor time spent in kernel mode as seen by the guest.");
12318 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12319 "Percentage of processor time spent idling as seen by the guest.");
12320
12321 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12322 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12323 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12324 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12325 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12326 pm::SubMetric *guestMemCache = new pm::SubMetric(
12327 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12328
12329 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12330 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12331
12332 /* Create and register base metrics */
12333 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12334 machineNetRx, machineNetTx);
12335 aCollector->registerBaseMetric(machineNetRate);
12336
12337 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12338 guestLoadUser, guestLoadKernel, guestLoadIdle);
12339 aCollector->registerBaseMetric(guestCpuLoad);
12340
12341 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12342 guestMemTotal, guestMemFree,
12343 guestMemBalloon, guestMemShared,
12344 guestMemCache, guestPagedTotal);
12345 aCollector->registerBaseMetric(guestCpuMem);
12346
12347 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12348 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12349 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12350 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12351
12352 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12353 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12354 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12355 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12356
12357 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12358 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12359 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12361
12362 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12366
12367 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12371
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12376
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12381
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12386
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12391
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12396
12397 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12400 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12401}
12402
12403void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12404{
12405 AssertReturnVoid(isWriteLockOnCurrentThread());
12406
12407 if (aCollector)
12408 {
12409 aCollector->unregisterMetricsFor(aMachine);
12410 aCollector->unregisterBaseMetricsFor(aMachine);
12411 }
12412}
12413
12414#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12415
12416
12417////////////////////////////////////////////////////////////////////////////////
12418
12419DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12420
12421HRESULT SessionMachine::FinalConstruct()
12422{
12423 LogFlowThisFunc(("\n"));
12424
12425 mClientToken = NULL;
12426
12427 return BaseFinalConstruct();
12428}
12429
12430void SessionMachine::FinalRelease()
12431{
12432 LogFlowThisFunc(("\n"));
12433
12434 Assert(!mClientToken);
12435 /* paranoia, should not hang around any more */
12436 if (mClientToken)
12437 {
12438 delete mClientToken;
12439 mClientToken = NULL;
12440 }
12441
12442 uninit(Uninit::Unexpected);
12443
12444 BaseFinalRelease();
12445}
12446
12447/**
12448 * @note Must be called only by Machine::LockMachine() from its own write lock.
12449 */
12450HRESULT SessionMachine::init(Machine *aMachine)
12451{
12452 LogFlowThisFuncEnter();
12453 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12454
12455 AssertReturn(aMachine, E_INVALIDARG);
12456
12457 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12458
12459 /* Enclose the state transition NotReady->InInit->Ready */
12460 AutoInitSpan autoInitSpan(this);
12461 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12462
12463 HRESULT rc = S_OK;
12464
12465 RT_ZERO(mAuthLibCtx);
12466
12467 /* create the machine client token */
12468 try
12469 {
12470 mClientToken = new ClientToken(aMachine, this);
12471 if (!mClientToken->isReady())
12472 {
12473 delete mClientToken;
12474 mClientToken = NULL;
12475 rc = E_FAIL;
12476 }
12477 }
12478 catch (std::bad_alloc &)
12479 {
12480 rc = E_OUTOFMEMORY;
12481 }
12482 if (FAILED(rc))
12483 return rc;
12484
12485 /* memorize the peer Machine */
12486 unconst(mPeer) = aMachine;
12487 /* share the parent pointer */
12488 unconst(mParent) = aMachine->mParent;
12489
12490 /* take the pointers to data to share */
12491 mData.share(aMachine->mData);
12492 mSSData.share(aMachine->mSSData);
12493
12494 mUserData.share(aMachine->mUserData);
12495 mHWData.share(aMachine->mHWData);
12496 mMediumAttachments.share(aMachine->mMediumAttachments);
12497
12498 mStorageControllers.allocate();
12499 for (StorageControllerList::const_iterator
12500 it = aMachine->mStorageControllers->begin();
12501 it != aMachine->mStorageControllers->end();
12502 ++it)
12503 {
12504 ComObjPtr<StorageController> ctl;
12505 ctl.createObject();
12506 ctl->init(this, *it);
12507 mStorageControllers->push_back(ctl);
12508 }
12509
12510 mUSBControllers.allocate();
12511 for (USBControllerList::const_iterator
12512 it = aMachine->mUSBControllers->begin();
12513 it != aMachine->mUSBControllers->end();
12514 ++it)
12515 {
12516 ComObjPtr<USBController> ctl;
12517 ctl.createObject();
12518 ctl->init(this, *it);
12519 mUSBControllers->push_back(ctl);
12520 }
12521
12522 unconst(mBIOSSettings).createObject();
12523 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12524
12525 unconst(mTrustedPlatformModule).createObject();
12526 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12527
12528 unconst(mNvramStore).createObject();
12529 mNvramStore->init(this, aMachine->mNvramStore);
12530
12531 unconst(mRecordingSettings).createObject();
12532 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12533 /* create another GraphicsAdapter object that will be mutable */
12534 unconst(mGraphicsAdapter).createObject();
12535 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12536 /* create another VRDEServer object that will be mutable */
12537 unconst(mVRDEServer).createObject();
12538 mVRDEServer->init(this, aMachine->mVRDEServer);
12539 /* create another audio adapter object that will be mutable */
12540 unconst(mAudioAdapter).createObject();
12541 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12542 /* create a list of serial ports that will be mutable */
12543 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12544 {
12545 unconst(mSerialPorts[slot]).createObject();
12546 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12547 }
12548 /* create a list of parallel ports that will be mutable */
12549 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12550 {
12551 unconst(mParallelPorts[slot]).createObject();
12552 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12553 }
12554
12555 /* create another USB device filters object that will be mutable */
12556 unconst(mUSBDeviceFilters).createObject();
12557 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12558
12559 /* create a list of network adapters that will be mutable */
12560 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12561 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12562 {
12563 unconst(mNetworkAdapters[slot]).createObject();
12564 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12565 }
12566
12567 /* create another bandwidth control object that will be mutable */
12568 unconst(mBandwidthControl).createObject();
12569 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12570
12571 /* default is to delete saved state on Saved -> PoweredOff transition */
12572 mRemoveSavedState = true;
12573
12574 /* Confirm a successful initialization when it's the case */
12575 autoInitSpan.setSucceeded();
12576
12577 miNATNetworksStarted = 0;
12578
12579 LogFlowThisFuncLeave();
12580 return rc;
12581}
12582
12583/**
12584 * Uninitializes this session object. If the reason is other than
12585 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12586 * or the client watcher code.
12587 *
12588 * @param aReason uninitialization reason
12589 *
12590 * @note Locks mParent + this object for writing.
12591 */
12592void SessionMachine::uninit(Uninit::Reason aReason)
12593{
12594 LogFlowThisFuncEnter();
12595 LogFlowThisFunc(("reason=%d\n", aReason));
12596
12597 /*
12598 * Strongly reference ourselves to prevent this object deletion after
12599 * mData->mSession.mMachine.setNull() below (which can release the last
12600 * reference and call the destructor). Important: this must be done before
12601 * accessing any members (and before AutoUninitSpan that does it as well).
12602 * This self reference will be released as the very last step on return.
12603 */
12604 ComObjPtr<SessionMachine> selfRef;
12605 if (aReason != Uninit::Unexpected)
12606 selfRef = this;
12607
12608 /* Enclose the state transition Ready->InUninit->NotReady */
12609 AutoUninitSpan autoUninitSpan(this);
12610 if (autoUninitSpan.uninitDone())
12611 {
12612 LogFlowThisFunc(("Already uninitialized\n"));
12613 LogFlowThisFuncLeave();
12614 return;
12615 }
12616
12617 if (autoUninitSpan.initFailed())
12618 {
12619 /* We've been called by init() because it's failed. It's not really
12620 * necessary (nor it's safe) to perform the regular uninit sequence
12621 * below, the following is enough.
12622 */
12623 LogFlowThisFunc(("Initialization failed.\n"));
12624 /* destroy the machine client token */
12625 if (mClientToken)
12626 {
12627 delete mClientToken;
12628 mClientToken = NULL;
12629 }
12630 uninitDataAndChildObjects();
12631 mData.free();
12632 unconst(mParent) = NULL;
12633 unconst(mPeer) = NULL;
12634 LogFlowThisFuncLeave();
12635 return;
12636 }
12637
12638 MachineState_T lastState;
12639 {
12640 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12641 lastState = mData->mMachineState;
12642 }
12643 NOREF(lastState);
12644
12645#ifdef VBOX_WITH_USB
12646 // release all captured USB devices, but do this before requesting the locks below
12647 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12648 {
12649 /* Console::captureUSBDevices() is called in the VM process only after
12650 * setting the machine state to Starting or Restoring.
12651 * Console::detachAllUSBDevices() will be called upon successful
12652 * termination. So, we need to release USB devices only if there was
12653 * an abnormal termination of a running VM.
12654 *
12655 * This is identical to SessionMachine::DetachAllUSBDevices except
12656 * for the aAbnormal argument. */
12657 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12658 AssertComRC(rc);
12659 NOREF(rc);
12660
12661 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12662 if (service)
12663 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12664 }
12665#endif /* VBOX_WITH_USB */
12666
12667 // we need to lock this object in uninit() because the lock is shared
12668 // with mPeer (as well as data we modify below). mParent lock is needed
12669 // by several calls to it.
12670 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12671
12672#ifdef VBOX_WITH_RESOURCE_USAGE_API
12673 /*
12674 * It is safe to call Machine::i_unregisterMetrics() here because
12675 * PerformanceCollector::samplerCallback no longer accesses guest methods
12676 * holding the lock.
12677 */
12678 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12679 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12680 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12681 if (mCollectorGuest)
12682 {
12683 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12684 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12685 mCollectorGuest = NULL;
12686 }
12687#endif
12688
12689 if (aReason == Uninit::Abnormal)
12690 {
12691 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12692
12693 /*
12694 * Move the VM to the 'Aborted' machine state unless we are restoring a
12695 * VM that was in the 'Saved' machine state. In that case, if the VM
12696 * fails before reaching either the 'Restoring' machine state or the
12697 * 'Running' machine state then we set the machine state to
12698 * 'AbortedSaved' in order to preserve the saved state file so that the
12699 * VM can be restored in the future.
12700 */
12701 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12702 i_setMachineState(MachineState_AbortedSaved);
12703 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12704 i_setMachineState(MachineState_Aborted);
12705 }
12706
12707 // any machine settings modified?
12708 if (mData->flModifications)
12709 {
12710 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12711 i_rollback(false /* aNotify */);
12712 }
12713
12714 mData->mSession.mPID = NIL_RTPROCESS;
12715
12716 if (aReason == Uninit::Unexpected)
12717 {
12718 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12719 * client watcher thread to update the set of machines that have open
12720 * sessions. */
12721 mParent->i_updateClientWatcher();
12722 }
12723
12724 /* uninitialize all remote controls */
12725 if (mData->mSession.mRemoteControls.size())
12726 {
12727 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12728 mData->mSession.mRemoteControls.size()));
12729
12730 /* Always restart a the beginning, since the iterator is invalidated
12731 * by using erase(). */
12732 for (Data::Session::RemoteControlList::iterator
12733 it = mData->mSession.mRemoteControls.begin();
12734 it != mData->mSession.mRemoteControls.end();
12735 it = mData->mSession.mRemoteControls.begin())
12736 {
12737 ComPtr<IInternalSessionControl> pControl = *it;
12738 mData->mSession.mRemoteControls.erase(it);
12739 multilock.release();
12740 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12741 HRESULT rc = pControl->Uninitialize();
12742 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12743 if (FAILED(rc))
12744 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12745 multilock.acquire();
12746 }
12747 mData->mSession.mRemoteControls.clear();
12748 }
12749
12750 /* Remove all references to the NAT network service. The service will stop
12751 * if all references (also from other VMs) are removed. */
12752 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12753 {
12754 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12755 {
12756 BOOL enabled;
12757 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12758 if ( FAILED(hrc)
12759 || !enabled)
12760 continue;
12761
12762 NetworkAttachmentType_T type;
12763 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12764 if ( SUCCEEDED(hrc)
12765 && type == NetworkAttachmentType_NATNetwork)
12766 {
12767 Bstr name;
12768 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12769 if (SUCCEEDED(hrc))
12770 {
12771 multilock.release();
12772 Utf8Str strName(name);
12773 LogRel(("VM '%s' stops using NAT network '%s'\n",
12774 mUserData->s.strName.c_str(), strName.c_str()));
12775 mParent->i_natNetworkRefDec(strName);
12776 multilock.acquire();
12777 }
12778 }
12779 }
12780 }
12781
12782 /*
12783 * An expected uninitialization can come only from #i_checkForDeath().
12784 * Otherwise it means that something's gone really wrong (for example,
12785 * the Session implementation has released the VirtualBox reference
12786 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12787 * etc). However, it's also possible, that the client releases the IPC
12788 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12789 * but the VirtualBox release event comes first to the server process.
12790 * This case is practically possible, so we should not assert on an
12791 * unexpected uninit, just log a warning.
12792 */
12793
12794 if (aReason == Uninit::Unexpected)
12795 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12796
12797 if (aReason != Uninit::Normal)
12798 {
12799 mData->mSession.mDirectControl.setNull();
12800 }
12801 else
12802 {
12803 /* this must be null here (see #OnSessionEnd()) */
12804 Assert(mData->mSession.mDirectControl.isNull());
12805 Assert(mData->mSession.mState == SessionState_Unlocking);
12806 Assert(!mData->mSession.mProgress.isNull());
12807 }
12808 if (mData->mSession.mProgress)
12809 {
12810 if (aReason == Uninit::Normal)
12811 mData->mSession.mProgress->i_notifyComplete(S_OK);
12812 else
12813 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12814 COM_IIDOF(ISession),
12815 getComponentName(),
12816 tr("The VM session was aborted"));
12817 mData->mSession.mProgress.setNull();
12818 }
12819
12820 if (mConsoleTaskData.mProgress)
12821 {
12822 Assert(aReason == Uninit::Abnormal);
12823 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12824 COM_IIDOF(ISession),
12825 getComponentName(),
12826 tr("The VM session was aborted"));
12827 mConsoleTaskData.mProgress.setNull();
12828 }
12829
12830 /* remove the association between the peer machine and this session machine */
12831 Assert( (SessionMachine*)mData->mSession.mMachine == this
12832 || aReason == Uninit::Unexpected);
12833
12834 /* reset the rest of session data */
12835 mData->mSession.mLockType = LockType_Null;
12836 mData->mSession.mMachine.setNull();
12837 mData->mSession.mState = SessionState_Unlocked;
12838 mData->mSession.mName.setNull();
12839
12840 /* destroy the machine client token before leaving the exclusive lock */
12841 if (mClientToken)
12842 {
12843 delete mClientToken;
12844 mClientToken = NULL;
12845 }
12846
12847 /* fire an event */
12848 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12849
12850 uninitDataAndChildObjects();
12851
12852 /* free the essential data structure last */
12853 mData.free();
12854
12855 /* release the exclusive lock before setting the below two to NULL */
12856 multilock.release();
12857
12858 unconst(mParent) = NULL;
12859 unconst(mPeer) = NULL;
12860
12861 AuthLibUnload(&mAuthLibCtx);
12862
12863 LogFlowThisFuncLeave();
12864}
12865
12866// util::Lockable interface
12867////////////////////////////////////////////////////////////////////////////////
12868
12869/**
12870 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12871 * with the primary Machine instance (mPeer).
12872 */
12873RWLockHandle *SessionMachine::lockHandle() const
12874{
12875 AssertReturn(mPeer != NULL, NULL);
12876 return mPeer->lockHandle();
12877}
12878
12879// IInternalMachineControl methods
12880////////////////////////////////////////////////////////////////////////////////
12881
12882/**
12883 * Passes collected guest statistics to performance collector object
12884 */
12885HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12886 ULONG aCpuKernel, ULONG aCpuIdle,
12887 ULONG aMemTotal, ULONG aMemFree,
12888 ULONG aMemBalloon, ULONG aMemShared,
12889 ULONG aMemCache, ULONG aPageTotal,
12890 ULONG aAllocVMM, ULONG aFreeVMM,
12891 ULONG aBalloonedVMM, ULONG aSharedVMM,
12892 ULONG aVmNetRx, ULONG aVmNetTx)
12893{
12894#ifdef VBOX_WITH_RESOURCE_USAGE_API
12895 if (mCollectorGuest)
12896 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12897 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12898 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12899 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12900
12901 return S_OK;
12902#else
12903 NOREF(aValidStats);
12904 NOREF(aCpuUser);
12905 NOREF(aCpuKernel);
12906 NOREF(aCpuIdle);
12907 NOREF(aMemTotal);
12908 NOREF(aMemFree);
12909 NOREF(aMemBalloon);
12910 NOREF(aMemShared);
12911 NOREF(aMemCache);
12912 NOREF(aPageTotal);
12913 NOREF(aAllocVMM);
12914 NOREF(aFreeVMM);
12915 NOREF(aBalloonedVMM);
12916 NOREF(aSharedVMM);
12917 NOREF(aVmNetRx);
12918 NOREF(aVmNetTx);
12919 return E_NOTIMPL;
12920#endif
12921}
12922
12923////////////////////////////////////////////////////////////////////////////////
12924//
12925// SessionMachine task records
12926//
12927////////////////////////////////////////////////////////////////////////////////
12928
12929/**
12930 * Task record for saving the machine state.
12931 */
12932class SessionMachine::SaveStateTask
12933 : public Machine::Task
12934{
12935public:
12936 SaveStateTask(SessionMachine *m,
12937 Progress *p,
12938 const Utf8Str &t,
12939 Reason_T enmReason,
12940 const Utf8Str &strStateFilePath)
12941 : Task(m, p, t),
12942 m_enmReason(enmReason),
12943 m_strStateFilePath(strStateFilePath)
12944 {}
12945
12946private:
12947 void handler()
12948 {
12949 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12950 }
12951
12952 Reason_T m_enmReason;
12953 Utf8Str m_strStateFilePath;
12954
12955 friend class SessionMachine;
12956};
12957
12958/**
12959 * Task thread implementation for SessionMachine::SaveState(), called from
12960 * SessionMachine::taskHandler().
12961 *
12962 * @note Locks this object for writing.
12963 *
12964 * @param task
12965 * @return
12966 */
12967void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12968{
12969 LogFlowThisFuncEnter();
12970
12971 AutoCaller autoCaller(this);
12972 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12973 if (FAILED(autoCaller.rc()))
12974 {
12975 /* we might have been uninitialized because the session was accidentally
12976 * closed by the client, so don't assert */
12977 HRESULT rc = setError(E_FAIL,
12978 tr("The session has been accidentally closed"));
12979 task.m_pProgress->i_notifyComplete(rc);
12980 LogFlowThisFuncLeave();
12981 return;
12982 }
12983
12984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12985
12986 HRESULT rc = S_OK;
12987
12988 try
12989 {
12990 ComPtr<IInternalSessionControl> directControl;
12991 if (mData->mSession.mLockType == LockType_VM)
12992 directControl = mData->mSession.mDirectControl;
12993 if (directControl.isNull())
12994 throw setError(VBOX_E_INVALID_VM_STATE,
12995 tr("Trying to save state without a running VM"));
12996 alock.release();
12997 BOOL fSuspendedBySave;
12998 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12999 Assert(!fSuspendedBySave);
13000 alock.acquire();
13001
13002 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13003 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13004 throw E_FAIL);
13005
13006 if (SUCCEEDED(rc))
13007 {
13008 mSSData->strStateFilePath = task.m_strStateFilePath;
13009
13010 /* save all VM settings */
13011 rc = i_saveSettings(NULL, alock);
13012 // no need to check whether VirtualBox.xml needs saving also since
13013 // we can't have a name change pending at this point
13014 }
13015 else
13016 {
13017 // On failure, set the state to the state we had at the beginning.
13018 i_setMachineState(task.m_machineStateBackup);
13019 i_updateMachineStateOnClient();
13020
13021 // Delete the saved state file (might have been already created).
13022 // No need to check whether this is shared with a snapshot here
13023 // because we certainly created a fresh saved state file here.
13024 RTFileDelete(task.m_strStateFilePath.c_str());
13025 }
13026 }
13027 catch (HRESULT aRC) { rc = aRC; }
13028
13029 task.m_pProgress->i_notifyComplete(rc);
13030
13031 LogFlowThisFuncLeave();
13032}
13033
13034/**
13035 * @note Locks this object for writing.
13036 */
13037HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13038{
13039 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13040}
13041
13042HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13043{
13044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13045
13046 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13047 if (FAILED(rc)) return rc;
13048
13049 if ( mData->mMachineState != MachineState_Running
13050 && mData->mMachineState != MachineState_Paused
13051 )
13052 return setError(VBOX_E_INVALID_VM_STATE,
13053 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13054 Global::stringifyMachineState(mData->mMachineState));
13055
13056 ComObjPtr<Progress> pProgress;
13057 pProgress.createObject();
13058 rc = pProgress->init(i_getVirtualBox(),
13059 static_cast<IMachine *>(this) /* aInitiator */,
13060 tr("Saving the execution state of the virtual machine"),
13061 FALSE /* aCancelable */);
13062 if (FAILED(rc))
13063 return rc;
13064
13065 Utf8Str strStateFilePath;
13066 i_composeSavedStateFilename(strStateFilePath);
13067
13068 /* create and start the task on a separate thread (note that it will not
13069 * start working until we release alock) */
13070 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13071 rc = pTask->createThread();
13072 if (FAILED(rc))
13073 return rc;
13074
13075 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13076 i_setMachineState(MachineState_Saving);
13077 i_updateMachineStateOnClient();
13078
13079 pProgress.queryInterfaceTo(aProgress.asOutParam());
13080
13081 return S_OK;
13082}
13083
13084/**
13085 * @note Locks this object for writing.
13086 */
13087HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13088{
13089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13090
13091 HRESULT rc = i_checkStateDependency(MutableStateDep);
13092 if (FAILED(rc)) return rc;
13093
13094 if ( mData->mMachineState != MachineState_PoweredOff
13095 && mData->mMachineState != MachineState_Teleported
13096 && mData->mMachineState != MachineState_Aborted
13097 )
13098 return setError(VBOX_E_INVALID_VM_STATE,
13099 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13100 Global::stringifyMachineState(mData->mMachineState));
13101
13102 com::Utf8Str stateFilePathFull;
13103 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13104 if (RT_FAILURE(vrc))
13105 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13106 tr("Invalid saved state file path '%s' (%Rrc)"),
13107 aSavedStateFile.c_str(),
13108 vrc);
13109
13110 mSSData->strStateFilePath = stateFilePathFull;
13111
13112 /* The below i_setMachineState() will detect the state transition and will
13113 * update the settings file */
13114
13115 return i_setMachineState(MachineState_Saved);
13116}
13117
13118/**
13119 * @note Locks this object for writing.
13120 */
13121HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13122{
13123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13124
13125 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13126 if (FAILED(rc)) return rc;
13127
13128 if ( mData->mMachineState != MachineState_Saved
13129 && mData->mMachineState != MachineState_AbortedSaved)
13130 return setError(VBOX_E_INVALID_VM_STATE,
13131 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13132 Global::stringifyMachineState(mData->mMachineState));
13133
13134 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13135
13136 /*
13137 * Saved -> PoweredOff transition will be detected in the SessionMachine
13138 * and properly handled.
13139 */
13140 rc = i_setMachineState(MachineState_PoweredOff);
13141 return rc;
13142}
13143
13144
13145/**
13146 * @note Locks the same as #i_setMachineState() does.
13147 */
13148HRESULT SessionMachine::updateState(MachineState_T aState)
13149{
13150 return i_setMachineState(aState);
13151}
13152
13153/**
13154 * @note Locks this object for writing.
13155 */
13156HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13157{
13158 IProgress *pProgress(aProgress);
13159
13160 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13161
13162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13163
13164 if (mData->mSession.mState != SessionState_Locked)
13165 return VBOX_E_INVALID_OBJECT_STATE;
13166
13167 if (!mData->mSession.mProgress.isNull())
13168 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13169
13170 /* If we didn't reference the NAT network service yet, add a reference to
13171 * force a start */
13172 if (miNATNetworksStarted < 1)
13173 {
13174 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13175 {
13176 BOOL enabled;
13177 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13178 if ( FAILED(hrc)
13179 || !enabled)
13180 continue;
13181
13182 NetworkAttachmentType_T type;
13183 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13184 if ( SUCCEEDED(hrc)
13185 && type == NetworkAttachmentType_NATNetwork)
13186 {
13187 Bstr name;
13188 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13189 if (SUCCEEDED(hrc))
13190 {
13191 Utf8Str strName(name);
13192 LogRel(("VM '%s' starts using NAT network '%s'\n",
13193 mUserData->s.strName.c_str(), strName.c_str()));
13194 mPeer->lockHandle()->unlockWrite();
13195 mParent->i_natNetworkRefInc(strName);
13196#ifdef RT_LOCK_STRICT
13197 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13198#else
13199 mPeer->lockHandle()->lockWrite();
13200#endif
13201 }
13202 }
13203 }
13204 miNATNetworksStarted++;
13205 }
13206
13207 LogFlowThisFunc(("returns S_OK.\n"));
13208 return S_OK;
13209}
13210
13211/**
13212 * @note Locks this object for writing.
13213 */
13214HRESULT SessionMachine::endPowerUp(LONG aResult)
13215{
13216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13217
13218 if (mData->mSession.mState != SessionState_Locked)
13219 return VBOX_E_INVALID_OBJECT_STATE;
13220
13221 /* Finalize the LaunchVMProcess progress object. */
13222 if (mData->mSession.mProgress)
13223 {
13224 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13225 mData->mSession.mProgress.setNull();
13226 }
13227
13228 if (SUCCEEDED((HRESULT)aResult))
13229 {
13230#ifdef VBOX_WITH_RESOURCE_USAGE_API
13231 /* The VM has been powered up successfully, so it makes sense
13232 * now to offer the performance metrics for a running machine
13233 * object. Doing it earlier wouldn't be safe. */
13234 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13235 mData->mSession.mPID);
13236#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13237 }
13238
13239 return S_OK;
13240}
13241
13242/**
13243 * @note Locks this object for writing.
13244 */
13245HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13246{
13247 LogFlowThisFuncEnter();
13248
13249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13250
13251 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13252 E_FAIL);
13253
13254 /* create a progress object to track operation completion */
13255 ComObjPtr<Progress> pProgress;
13256 pProgress.createObject();
13257 pProgress->init(i_getVirtualBox(),
13258 static_cast<IMachine *>(this) /* aInitiator */,
13259 tr("Stopping the virtual machine"),
13260 FALSE /* aCancelable */);
13261
13262 /* fill in the console task data */
13263 mConsoleTaskData.mLastState = mData->mMachineState;
13264 mConsoleTaskData.mProgress = pProgress;
13265
13266 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13267 i_setMachineState(MachineState_Stopping);
13268
13269 pProgress.queryInterfaceTo(aProgress.asOutParam());
13270
13271 return S_OK;
13272}
13273
13274/**
13275 * @note Locks this object for writing.
13276 */
13277HRESULT SessionMachine::endPoweringDown(LONG aResult,
13278 const com::Utf8Str &aErrMsg)
13279{
13280 HRESULT const hrcResult = (HRESULT)aResult;
13281 LogFlowThisFuncEnter();
13282
13283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13284
13285 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13286 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13287 && mConsoleTaskData.mLastState != MachineState_Null,
13288 E_FAIL);
13289
13290 /*
13291 * On failure, set the state to the state we had when BeginPoweringDown()
13292 * was called (this is expected by Console::PowerDown() and the associated
13293 * task). On success the VM process already changed the state to
13294 * MachineState_PoweredOff, so no need to do anything.
13295 */
13296 if (FAILED(hrcResult))
13297 i_setMachineState(mConsoleTaskData.mLastState);
13298
13299 /* notify the progress object about operation completion */
13300 Assert(mConsoleTaskData.mProgress);
13301 if (SUCCEEDED(hrcResult))
13302 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13303 else
13304 {
13305 if (aErrMsg.length())
13306 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13307 COM_IIDOF(ISession),
13308 getComponentName(),
13309 aErrMsg.c_str());
13310 else
13311 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13312 }
13313
13314 /* clear out the temporary saved state data */
13315 mConsoleTaskData.mLastState = MachineState_Null;
13316 mConsoleTaskData.mProgress.setNull();
13317
13318 LogFlowThisFuncLeave();
13319 return S_OK;
13320}
13321
13322
13323/**
13324 * Goes through the USB filters of the given machine to see if the given
13325 * device matches any filter or not.
13326 *
13327 * @note Locks the same as USBController::hasMatchingFilter() does.
13328 */
13329HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13330 BOOL *aMatched,
13331 ULONG *aMaskedInterfaces)
13332{
13333 LogFlowThisFunc(("\n"));
13334
13335#ifdef VBOX_WITH_USB
13336 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13337#else
13338 NOREF(aDevice);
13339 NOREF(aMaskedInterfaces);
13340 *aMatched = FALSE;
13341#endif
13342
13343 return S_OK;
13344}
13345
13346/**
13347 * @note Locks the same as Host::captureUSBDevice() does.
13348 */
13349HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13350{
13351 LogFlowThisFunc(("\n"));
13352
13353#ifdef VBOX_WITH_USB
13354 /* if captureDeviceForVM() fails, it must have set extended error info */
13355 clearError();
13356 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13357 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13358 return rc;
13359
13360 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13361 AssertReturn(service, E_FAIL);
13362 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13363#else
13364 RT_NOREF(aId, aCaptureFilename);
13365 return E_NOTIMPL;
13366#endif
13367}
13368
13369/**
13370 * @note Locks the same as Host::detachUSBDevice() does.
13371 */
13372HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13373 BOOL aDone)
13374{
13375 LogFlowThisFunc(("\n"));
13376
13377#ifdef VBOX_WITH_USB
13378 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13379 AssertReturn(service, E_FAIL);
13380 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13381#else
13382 NOREF(aId);
13383 NOREF(aDone);
13384 return E_NOTIMPL;
13385#endif
13386}
13387
13388/**
13389 * Inserts all machine filters to the USB proxy service and then calls
13390 * Host::autoCaptureUSBDevices().
13391 *
13392 * Called by Console from the VM process upon VM startup.
13393 *
13394 * @note Locks what called methods lock.
13395 */
13396HRESULT SessionMachine::autoCaptureUSBDevices()
13397{
13398 LogFlowThisFunc(("\n"));
13399
13400#ifdef VBOX_WITH_USB
13401 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13402 AssertComRC(rc);
13403 NOREF(rc);
13404
13405 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13406 AssertReturn(service, E_FAIL);
13407 return service->autoCaptureDevicesForVM(this);
13408#else
13409 return S_OK;
13410#endif
13411}
13412
13413/**
13414 * Removes all machine filters from the USB proxy service and then calls
13415 * Host::detachAllUSBDevices().
13416 *
13417 * Called by Console from the VM process upon normal VM termination or by
13418 * SessionMachine::uninit() upon abnormal VM termination (from under the
13419 * Machine/SessionMachine lock).
13420 *
13421 * @note Locks what called methods lock.
13422 */
13423HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13424{
13425 LogFlowThisFunc(("\n"));
13426
13427#ifdef VBOX_WITH_USB
13428 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13429 AssertComRC(rc);
13430 NOREF(rc);
13431
13432 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13433 AssertReturn(service, E_FAIL);
13434 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13435#else
13436 NOREF(aDone);
13437 return S_OK;
13438#endif
13439}
13440
13441/**
13442 * @note Locks this object for writing.
13443 */
13444HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13445 ComPtr<IProgress> &aProgress)
13446{
13447 LogFlowThisFuncEnter();
13448
13449 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13450 /*
13451 * We don't assert below because it might happen that a non-direct session
13452 * informs us it is closed right after we've been uninitialized -- it's ok.
13453 */
13454
13455 /* get IInternalSessionControl interface */
13456 ComPtr<IInternalSessionControl> control(aSession);
13457
13458 ComAssertRet(!control.isNull(), E_INVALIDARG);
13459
13460 /* Creating a Progress object requires the VirtualBox lock, and
13461 * thus locking it here is required by the lock order rules. */
13462 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13463
13464 if (control == mData->mSession.mDirectControl)
13465 {
13466 /* The direct session is being normally closed by the client process
13467 * ----------------------------------------------------------------- */
13468
13469 /* go to the closing state (essential for all open*Session() calls and
13470 * for #i_checkForDeath()) */
13471 Assert(mData->mSession.mState == SessionState_Locked);
13472 mData->mSession.mState = SessionState_Unlocking;
13473
13474 /* set direct control to NULL to release the remote instance */
13475 mData->mSession.mDirectControl.setNull();
13476 LogFlowThisFunc(("Direct control is set to NULL\n"));
13477
13478 if (mData->mSession.mProgress)
13479 {
13480 /* finalize the progress, someone might wait if a frontend
13481 * closes the session before powering on the VM. */
13482 mData->mSession.mProgress->notifyComplete(E_FAIL,
13483 COM_IIDOF(ISession),
13484 getComponentName(),
13485 tr("The VM session was closed before any attempt to power it on"));
13486 mData->mSession.mProgress.setNull();
13487 }
13488
13489 /* Create the progress object the client will use to wait until
13490 * #i_checkForDeath() is called to uninitialize this session object after
13491 * it releases the IPC semaphore.
13492 * Note! Because we're "reusing" mProgress here, this must be a proxy
13493 * object just like for LaunchVMProcess. */
13494 Assert(mData->mSession.mProgress.isNull());
13495 ComObjPtr<ProgressProxy> progress;
13496 progress.createObject();
13497 ComPtr<IUnknown> pPeer(mPeer);
13498 progress->init(mParent, pPeer,
13499 Bstr(tr("Closing session")).raw(),
13500 FALSE /* aCancelable */);
13501 progress.queryInterfaceTo(aProgress.asOutParam());
13502 mData->mSession.mProgress = progress;
13503 }
13504 else
13505 {
13506 /* the remote session is being normally closed */
13507 bool found = false;
13508 for (Data::Session::RemoteControlList::iterator
13509 it = mData->mSession.mRemoteControls.begin();
13510 it != mData->mSession.mRemoteControls.end();
13511 ++it)
13512 {
13513 if (control == *it)
13514 {
13515 found = true;
13516 // This MUST be erase(it), not remove(*it) as the latter
13517 // triggers a very nasty use after free due to the place where
13518 // the value "lives".
13519 mData->mSession.mRemoteControls.erase(it);
13520 break;
13521 }
13522 }
13523 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13524 E_INVALIDARG);
13525 }
13526
13527 /* signal the client watcher thread, because the client is going away */
13528 mParent->i_updateClientWatcher();
13529
13530 LogFlowThisFuncLeave();
13531 return S_OK;
13532}
13533
13534HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13535 std::vector<com::Utf8Str> &aValues,
13536 std::vector<LONG64> &aTimestamps,
13537 std::vector<com::Utf8Str> &aFlags)
13538{
13539 LogFlowThisFunc(("\n"));
13540
13541#ifdef VBOX_WITH_GUEST_PROPS
13542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13543
13544 size_t cEntries = mHWData->mGuestProperties.size();
13545 aNames.resize(cEntries);
13546 aValues.resize(cEntries);
13547 aTimestamps.resize(cEntries);
13548 aFlags.resize(cEntries);
13549
13550 size_t i = 0;
13551 for (HWData::GuestPropertyMap::const_iterator
13552 it = mHWData->mGuestProperties.begin();
13553 it != mHWData->mGuestProperties.end();
13554 ++it, ++i)
13555 {
13556 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13557 aNames[i] = it->first;
13558 aValues[i] = it->second.strValue;
13559 aTimestamps[i] = it->second.mTimestamp;
13560
13561 /* If it is NULL, keep it NULL. */
13562 if (it->second.mFlags)
13563 {
13564 GuestPropWriteFlags(it->second.mFlags, szFlags);
13565 aFlags[i] = szFlags;
13566 }
13567 else
13568 aFlags[i] = "";
13569 }
13570 return S_OK;
13571#else
13572 ReturnComNotImplemented();
13573#endif
13574}
13575
13576HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13577 const com::Utf8Str &aValue,
13578 LONG64 aTimestamp,
13579 const com::Utf8Str &aFlags)
13580{
13581 LogFlowThisFunc(("\n"));
13582
13583#ifdef VBOX_WITH_GUEST_PROPS
13584 try
13585 {
13586 /*
13587 * Convert input up front.
13588 */
13589 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13590 if (aFlags.length())
13591 {
13592 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13593 AssertRCReturn(vrc, E_INVALIDARG);
13594 }
13595
13596 /*
13597 * Now grab the object lock, validate the state and do the update.
13598 */
13599
13600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13601
13602 if (!Global::IsOnline(mData->mMachineState))
13603 {
13604 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13605 VBOX_E_INVALID_VM_STATE);
13606 }
13607
13608 i_setModified(IsModified_MachineData);
13609 mHWData.backup();
13610
13611 bool fDelete = !aValue.length();
13612 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13613 if (it != mHWData->mGuestProperties.end())
13614 {
13615 if (!fDelete)
13616 {
13617 it->second.strValue = aValue;
13618 it->second.mTimestamp = aTimestamp;
13619 it->second.mFlags = fFlags;
13620 }
13621 else
13622 mHWData->mGuestProperties.erase(it);
13623
13624 mData->mGuestPropertiesModified = TRUE;
13625 }
13626 else if (!fDelete)
13627 {
13628 HWData::GuestProperty prop;
13629 prop.strValue = aValue;
13630 prop.mTimestamp = aTimestamp;
13631 prop.mFlags = fFlags;
13632
13633 mHWData->mGuestProperties[aName] = prop;
13634 mData->mGuestPropertiesModified = TRUE;
13635 }
13636
13637 alock.release();
13638
13639 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13640 }
13641 catch (...)
13642 {
13643 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13644 }
13645 return S_OK;
13646#else
13647 ReturnComNotImplemented();
13648#endif
13649}
13650
13651
13652HRESULT SessionMachine::lockMedia()
13653{
13654 AutoMultiWriteLock2 alock(this->lockHandle(),
13655 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13656
13657 AssertReturn( mData->mMachineState == MachineState_Starting
13658 || mData->mMachineState == MachineState_Restoring
13659 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13660
13661 clearError();
13662 alock.release();
13663 return i_lockMedia();
13664}
13665
13666HRESULT SessionMachine::unlockMedia()
13667{
13668 HRESULT hrc = i_unlockMedia();
13669 return hrc;
13670}
13671
13672HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13673 ComPtr<IMediumAttachment> &aNewAttachment)
13674{
13675 // request the host lock first, since might be calling Host methods for getting host drives;
13676 // next, protect the media tree all the while we're in here, as well as our member variables
13677 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13678 this->lockHandle(),
13679 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13680
13681 IMediumAttachment *iAttach = aAttachment;
13682 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13683
13684 Utf8Str ctrlName;
13685 LONG lPort;
13686 LONG lDevice;
13687 bool fTempEject;
13688 {
13689 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13690
13691 /* Need to query the details first, as the IMediumAttachment reference
13692 * might be to the original settings, which we are going to change. */
13693 ctrlName = pAttach->i_getControllerName();
13694 lPort = pAttach->i_getPort();
13695 lDevice = pAttach->i_getDevice();
13696 fTempEject = pAttach->i_getTempEject();
13697 }
13698
13699 if (!fTempEject)
13700 {
13701 /* Remember previously mounted medium. The medium before taking the
13702 * backup is not necessarily the same thing. */
13703 ComObjPtr<Medium> oldmedium;
13704 oldmedium = pAttach->i_getMedium();
13705
13706 i_setModified(IsModified_Storage);
13707 mMediumAttachments.backup();
13708
13709 // The backup operation makes the pAttach reference point to the
13710 // old settings. Re-get the correct reference.
13711 pAttach = i_findAttachment(*mMediumAttachments.data(),
13712 ctrlName,
13713 lPort,
13714 lDevice);
13715
13716 {
13717 AutoCaller autoAttachCaller(this);
13718 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13719
13720 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13721 if (!oldmedium.isNull())
13722 oldmedium->i_removeBackReference(mData->mUuid);
13723
13724 pAttach->i_updateMedium(NULL);
13725 pAttach->i_updateEjected();
13726 }
13727
13728 i_setModified(IsModified_Storage);
13729 }
13730 else
13731 {
13732 {
13733 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13734 pAttach->i_updateEjected();
13735 }
13736 }
13737
13738 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13739
13740 return S_OK;
13741}
13742
13743HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13744 com::Utf8Str &aResult)
13745{
13746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13747
13748 HRESULT hr = S_OK;
13749
13750 if (!mAuthLibCtx.hAuthLibrary)
13751 {
13752 /* Load the external authentication library. */
13753 Bstr authLibrary;
13754 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13755
13756 Utf8Str filename = authLibrary;
13757
13758 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13759 if (RT_FAILURE(vrc))
13760 hr = setErrorBoth(E_FAIL, vrc,
13761 tr("Could not load the external authentication library '%s' (%Rrc)"),
13762 filename.c_str(), vrc);
13763 }
13764
13765 /* The auth library might need the machine lock. */
13766 alock.release();
13767
13768 if (FAILED(hr))
13769 return hr;
13770
13771 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13772 {
13773 enum VRDEAuthParams
13774 {
13775 parmUuid = 1,
13776 parmGuestJudgement,
13777 parmUser,
13778 parmPassword,
13779 parmDomain,
13780 parmClientId
13781 };
13782
13783 AuthResult result = AuthResultAccessDenied;
13784
13785 Guid uuid(aAuthParams[parmUuid]);
13786 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13787 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13788
13789 result = AuthLibAuthenticate(&mAuthLibCtx,
13790 uuid.raw(), guestJudgement,
13791 aAuthParams[parmUser].c_str(),
13792 aAuthParams[parmPassword].c_str(),
13793 aAuthParams[parmDomain].c_str(),
13794 u32ClientId);
13795
13796 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13797 size_t cbPassword = aAuthParams[parmPassword].length();
13798 if (cbPassword)
13799 {
13800 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13801 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13802 }
13803
13804 if (result == AuthResultAccessGranted)
13805 aResult = "granted";
13806 else
13807 aResult = "denied";
13808
13809 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13810 aAuthParams[parmUser].c_str(), aResult.c_str()));
13811 }
13812 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13813 {
13814 enum VRDEAuthDisconnectParams
13815 {
13816 parmUuid = 1,
13817 parmClientId
13818 };
13819
13820 Guid uuid(aAuthParams[parmUuid]);
13821 uint32_t u32ClientId = 0;
13822 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13823 }
13824 else
13825 {
13826 hr = E_INVALIDARG;
13827 }
13828
13829 return hr;
13830}
13831
13832// public methods only for internal purposes
13833/////////////////////////////////////////////////////////////////////////////
13834
13835#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13836/**
13837 * Called from the client watcher thread to check for expected or unexpected
13838 * death of the client process that has a direct session to this machine.
13839 *
13840 * On Win32 and on OS/2, this method is called only when we've got the
13841 * mutex (i.e. the client has either died or terminated normally) so it always
13842 * returns @c true (the client is terminated, the session machine is
13843 * uninitialized).
13844 *
13845 * On other platforms, the method returns @c true if the client process has
13846 * terminated normally or abnormally and the session machine was uninitialized,
13847 * and @c false if the client process is still alive.
13848 *
13849 * @note Locks this object for writing.
13850 */
13851bool SessionMachine::i_checkForDeath()
13852{
13853 Uninit::Reason reason;
13854 bool terminated = false;
13855
13856 /* Enclose autoCaller with a block because calling uninit() from under it
13857 * will deadlock. */
13858 {
13859 AutoCaller autoCaller(this);
13860 if (!autoCaller.isOk())
13861 {
13862 /* return true if not ready, to cause the client watcher to exclude
13863 * the corresponding session from watching */
13864 LogFlowThisFunc(("Already uninitialized!\n"));
13865 return true;
13866 }
13867
13868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13869
13870 /* Determine the reason of death: if the session state is Closing here,
13871 * everything is fine. Otherwise it means that the client did not call
13872 * OnSessionEnd() before it released the IPC semaphore. This may happen
13873 * either because the client process has abnormally terminated, or
13874 * because it simply forgot to call ISession::Close() before exiting. We
13875 * threat the latter also as an abnormal termination (see
13876 * Session::uninit() for details). */
13877 reason = mData->mSession.mState == SessionState_Unlocking ?
13878 Uninit::Normal :
13879 Uninit::Abnormal;
13880
13881 if (mClientToken)
13882 terminated = mClientToken->release();
13883 } /* AutoCaller block */
13884
13885 if (terminated)
13886 uninit(reason);
13887
13888 return terminated;
13889}
13890
13891void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13892{
13893 LogFlowThisFunc(("\n"));
13894
13895 strTokenId.setNull();
13896
13897 AutoCaller autoCaller(this);
13898 AssertComRCReturnVoid(autoCaller.rc());
13899
13900 Assert(mClientToken);
13901 if (mClientToken)
13902 mClientToken->getId(strTokenId);
13903}
13904#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13905IToken *SessionMachine::i_getToken()
13906{
13907 LogFlowThisFunc(("\n"));
13908
13909 AutoCaller autoCaller(this);
13910 AssertComRCReturn(autoCaller.rc(), NULL);
13911
13912 Assert(mClientToken);
13913 if (mClientToken)
13914 return mClientToken->getToken();
13915 else
13916 return NULL;
13917}
13918#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13919
13920Machine::ClientToken *SessionMachine::i_getClientToken()
13921{
13922 LogFlowThisFunc(("\n"));
13923
13924 AutoCaller autoCaller(this);
13925 AssertComRCReturn(autoCaller.rc(), NULL);
13926
13927 return mClientToken;
13928}
13929
13930
13931/**
13932 * @note Locks this object for reading.
13933 */
13934HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13935{
13936 LogFlowThisFunc(("\n"));
13937
13938 AutoCaller autoCaller(this);
13939 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13940
13941 ComPtr<IInternalSessionControl> directControl;
13942 {
13943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13944 if (mData->mSession.mLockType == LockType_VM)
13945 directControl = mData->mSession.mDirectControl;
13946 }
13947
13948 /* ignore notifications sent after #OnSessionEnd() is called */
13949 if (!directControl)
13950 return S_OK;
13951
13952 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13953}
13954
13955/**
13956 * @note Locks this object for reading.
13957 */
13958HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13959 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13960 const Utf8Str &aGuestIp, LONG aGuestPort)
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 * instead acting like callback we ask IVirtualBox deliver corresponding event
13979 */
13980
13981 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13982 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13983 return S_OK;
13984}
13985
13986/**
13987 * @note Locks this object for reading.
13988 */
13989HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13990{
13991 LogFlowThisFunc(("\n"));
13992
13993 AutoCaller autoCaller(this);
13994 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13995
13996 ComPtr<IInternalSessionControl> directControl;
13997 {
13998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13999 if (mData->mSession.mLockType == LockType_VM)
14000 directControl = mData->mSession.mDirectControl;
14001 }
14002
14003 /* ignore notifications sent after #OnSessionEnd() is called */
14004 if (!directControl)
14005 return S_OK;
14006
14007 return directControl->OnAudioAdapterChange(audioAdapter);
14008}
14009
14010/**
14011 * @note Locks this object for reading.
14012 */
14013HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14014{
14015 LogFlowThisFunc(("\n"));
14016
14017 AutoCaller autoCaller(this);
14018 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14019
14020 ComPtr<IInternalSessionControl> directControl;
14021 {
14022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14023 if (mData->mSession.mLockType == LockType_VM)
14024 directControl = mData->mSession.mDirectControl;
14025 }
14026
14027 /* ignore notifications sent after #OnSessionEnd() is called */
14028 if (!directControl)
14029 return S_OK;
14030
14031 return directControl->OnSerialPortChange(serialPort);
14032}
14033
14034/**
14035 * @note Locks this object for reading.
14036 */
14037HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14043
14044 ComPtr<IInternalSessionControl> directControl;
14045 {
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047 if (mData->mSession.mLockType == LockType_VM)
14048 directControl = mData->mSession.mDirectControl;
14049 }
14050
14051 /* ignore notifications sent after #OnSessionEnd() is called */
14052 if (!directControl)
14053 return S_OK;
14054
14055 return directControl->OnParallelPortChange(parallelPort);
14056}
14057
14058/**
14059 * @note Locks this object for reading.
14060 */
14061HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14062{
14063 LogFlowThisFunc(("\n"));
14064
14065 AutoCaller autoCaller(this);
14066 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14067
14068 ComPtr<IInternalSessionControl> directControl;
14069 {
14070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14071 if (mData->mSession.mLockType == LockType_VM)
14072 directControl = mData->mSession.mDirectControl;
14073 }
14074
14075 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14076
14077 /* ignore notifications sent after #OnSessionEnd() is called */
14078 if (!directControl)
14079 return S_OK;
14080
14081 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14082}
14083
14084/**
14085 * @note Locks this object for reading.
14086 */
14087HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
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_onMediumChanged(aAttachment);
14102
14103 /* ignore notifications sent after #OnSessionEnd() is called */
14104 if (!directControl)
14105 return S_OK;
14106
14107 return directControl->OnMediumChange(aAttachment, aForce);
14108}
14109
14110HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14111{
14112 LogFlowThisFunc(("\n"));
14113
14114 AutoCaller autoCaller(this);
14115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14116
14117 ComPtr<IInternalSessionControl> directControl;
14118 {
14119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14120 if (mData->mSession.mLockType == LockType_VM)
14121 directControl = mData->mSession.mDirectControl;
14122 }
14123
14124 /* ignore notifications sent after #OnSessionEnd() is called */
14125 if (!directControl)
14126 return S_OK;
14127
14128 return directControl->OnVMProcessPriorityChange(aPriority);
14129}
14130
14131/**
14132 * @note Locks this object for reading.
14133 */
14134HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14135{
14136 LogFlowThisFunc(("\n"));
14137
14138 AutoCaller autoCaller(this);
14139 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14140
14141 ComPtr<IInternalSessionControl> directControl;
14142 {
14143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14144 if (mData->mSession.mLockType == LockType_VM)
14145 directControl = mData->mSession.mDirectControl;
14146 }
14147
14148 /* ignore notifications sent after #OnSessionEnd() is called */
14149 if (!directControl)
14150 return S_OK;
14151
14152 return directControl->OnCPUChange(aCPU, aRemove);
14153}
14154
14155HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14156{
14157 LogFlowThisFunc(("\n"));
14158
14159 AutoCaller autoCaller(this);
14160 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14161
14162 ComPtr<IInternalSessionControl> directControl;
14163 {
14164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14165 if (mData->mSession.mLockType == LockType_VM)
14166 directControl = mData->mSession.mDirectControl;
14167 }
14168
14169 /* ignore notifications sent after #OnSessionEnd() is called */
14170 if (!directControl)
14171 return S_OK;
14172
14173 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14174}
14175
14176/**
14177 * @note Locks this object for reading.
14178 */
14179HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14180{
14181 LogFlowThisFunc(("\n"));
14182
14183 AutoCaller autoCaller(this);
14184 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14185
14186 ComPtr<IInternalSessionControl> directControl;
14187 {
14188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14189 if (mData->mSession.mLockType == LockType_VM)
14190 directControl = mData->mSession.mDirectControl;
14191 }
14192
14193 /* ignore notifications sent after #OnSessionEnd() is called */
14194 if (!directControl)
14195 return S_OK;
14196
14197 return directControl->OnVRDEServerChange(aRestart);
14198}
14199
14200/**
14201 * @note Locks this object for reading.
14202 */
14203HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14204{
14205 LogFlowThisFunc(("\n"));
14206
14207 AutoCaller autoCaller(this);
14208 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14209
14210 ComPtr<IInternalSessionControl> directControl;
14211 {
14212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14213 if (mData->mSession.mLockType == LockType_VM)
14214 directControl = mData->mSession.mDirectControl;
14215 }
14216
14217 /* ignore notifications sent after #OnSessionEnd() is called */
14218 if (!directControl)
14219 return S_OK;
14220
14221 return directControl->OnRecordingChange(aEnable);
14222}
14223
14224/**
14225 * @note Locks this object for reading.
14226 */
14227HRESULT SessionMachine::i_onUSBControllerChange()
14228{
14229 LogFlowThisFunc(("\n"));
14230
14231 AutoCaller autoCaller(this);
14232 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14233
14234 ComPtr<IInternalSessionControl> directControl;
14235 {
14236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14237 if (mData->mSession.mLockType == LockType_VM)
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 /* ignore notifications sent after #OnSessionEnd() is called */
14242 if (!directControl)
14243 return S_OK;
14244
14245 return directControl->OnUSBControllerChange();
14246}
14247
14248/**
14249 * @note Locks this object for reading.
14250 */
14251HRESULT SessionMachine::i_onSharedFolderChange()
14252{
14253 LogFlowThisFunc(("\n"));
14254
14255 AutoCaller autoCaller(this);
14256 AssertComRCReturnRC(autoCaller.rc());
14257
14258 ComPtr<IInternalSessionControl> directControl;
14259 {
14260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14261 if (mData->mSession.mLockType == LockType_VM)
14262 directControl = mData->mSession.mDirectControl;
14263 }
14264
14265 /* ignore notifications sent after #OnSessionEnd() is called */
14266 if (!directControl)
14267 return S_OK;
14268
14269 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14270}
14271
14272/**
14273 * @note Locks this object for reading.
14274 */
14275HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14276{
14277 LogFlowThisFunc(("\n"));
14278
14279 AutoCaller autoCaller(this);
14280 AssertComRCReturnRC(autoCaller.rc());
14281
14282 ComPtr<IInternalSessionControl> directControl;
14283 {
14284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14285 if (mData->mSession.mLockType == LockType_VM)
14286 directControl = mData->mSession.mDirectControl;
14287 }
14288
14289 /* ignore notifications sent after #OnSessionEnd() is called */
14290 if (!directControl)
14291 return S_OK;
14292
14293 return directControl->OnClipboardModeChange(aClipboardMode);
14294}
14295
14296/**
14297 * @note Locks this object for reading.
14298 */
14299HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14300{
14301 LogFlowThisFunc(("\n"));
14302
14303 AutoCaller autoCaller(this);
14304 AssertComRCReturnRC(autoCaller.rc());
14305
14306 ComPtr<IInternalSessionControl> directControl;
14307 {
14308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14309 if (mData->mSession.mLockType == LockType_VM)
14310 directControl = mData->mSession.mDirectControl;
14311 }
14312
14313 /* ignore notifications sent after #OnSessionEnd() is called */
14314 if (!directControl)
14315 return S_OK;
14316
14317 return directControl->OnClipboardFileTransferModeChange(aEnable);
14318}
14319
14320/**
14321 * @note Locks this object for reading.
14322 */
14323HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14324{
14325 LogFlowThisFunc(("\n"));
14326
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturnRC(autoCaller.rc());
14329
14330 ComPtr<IInternalSessionControl> directControl;
14331 {
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333 if (mData->mSession.mLockType == LockType_VM)
14334 directControl = mData->mSession.mDirectControl;
14335 }
14336
14337 /* ignore notifications sent after #OnSessionEnd() is called */
14338 if (!directControl)
14339 return S_OK;
14340
14341 return directControl->OnDnDModeChange(aDnDMode);
14342}
14343
14344/**
14345 * @note Locks this object for reading.
14346 */
14347HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14348{
14349 LogFlowThisFunc(("\n"));
14350
14351 AutoCaller autoCaller(this);
14352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14353
14354 ComPtr<IInternalSessionControl> directControl;
14355 {
14356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14357 if (mData->mSession.mLockType == LockType_VM)
14358 directControl = mData->mSession.mDirectControl;
14359 }
14360
14361 /* ignore notifications sent after #OnSessionEnd() is called */
14362 if (!directControl)
14363 return S_OK;
14364
14365 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14366}
14367
14368/**
14369 * @note Locks this object for reading.
14370 */
14371HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14372{
14373 LogFlowThisFunc(("\n"));
14374
14375 AutoCaller autoCaller(this);
14376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14377
14378 ComPtr<IInternalSessionControl> directControl;
14379 {
14380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14381 if (mData->mSession.mLockType == LockType_VM)
14382 directControl = mData->mSession.mDirectControl;
14383 }
14384
14385 /* ignore notifications sent after #OnSessionEnd() is called */
14386 if (!directControl)
14387 return S_OK;
14388
14389 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14390}
14391
14392/**
14393 * Returns @c true if this machine's USB controller reports it has a matching
14394 * filter for the given USB device and @c false otherwise.
14395 *
14396 * @note locks this object for reading.
14397 */
14398bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14399{
14400 AutoCaller autoCaller(this);
14401 /* silently return if not ready -- this method may be called after the
14402 * direct machine session has been called */
14403 if (!autoCaller.isOk())
14404 return false;
14405
14406#ifdef VBOX_WITH_USB
14407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14408
14409 switch (mData->mMachineState)
14410 {
14411 case MachineState_Starting:
14412 case MachineState_Restoring:
14413 case MachineState_TeleportingIn:
14414 case MachineState_Paused:
14415 case MachineState_Running:
14416 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14417 * elsewhere... */
14418 alock.release();
14419 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14420 default: break;
14421 }
14422#else
14423 NOREF(aDevice);
14424 NOREF(aMaskedIfs);
14425#endif
14426 return false;
14427}
14428
14429/**
14430 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14431 */
14432HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14433 IVirtualBoxErrorInfo *aError,
14434 ULONG aMaskedIfs,
14435 const com::Utf8Str &aCaptureFilename)
14436{
14437 LogFlowThisFunc(("\n"));
14438
14439 AutoCaller autoCaller(this);
14440
14441 /* This notification may happen after the machine object has been
14442 * uninitialized (the session was closed), so don't assert. */
14443 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14444
14445 ComPtr<IInternalSessionControl> directControl;
14446 {
14447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14448 if (mData->mSession.mLockType == LockType_VM)
14449 directControl = mData->mSession.mDirectControl;
14450 }
14451
14452 /* fail on notifications sent after #OnSessionEnd() is called, it is
14453 * expected by the caller */
14454 if (!directControl)
14455 return E_FAIL;
14456
14457 /* No locks should be held at this point. */
14458 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14459 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14460
14461 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14462}
14463
14464/**
14465 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14466 */
14467HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14468 IVirtualBoxErrorInfo *aError)
14469{
14470 LogFlowThisFunc(("\n"));
14471
14472 AutoCaller autoCaller(this);
14473
14474 /* This notification may happen after the machine object has been
14475 * uninitialized (the session was closed), so don't assert. */
14476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14477
14478 ComPtr<IInternalSessionControl> directControl;
14479 {
14480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14481 if (mData->mSession.mLockType == LockType_VM)
14482 directControl = mData->mSession.mDirectControl;
14483 }
14484
14485 /* fail on notifications sent after #OnSessionEnd() is called, it is
14486 * expected by the caller */
14487 if (!directControl)
14488 return E_FAIL;
14489
14490 /* No locks should be held at this point. */
14491 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14492 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14493
14494 return directControl->OnUSBDeviceDetach(aId, aError);
14495}
14496
14497// protected methods
14498/////////////////////////////////////////////////////////////////////////////
14499
14500/**
14501 * Deletes the given file if it is no longer in use by either the current machine state
14502 * (if the machine is "saved") or any of the machine's snapshots.
14503 *
14504 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14505 * but is different for each SnapshotMachine. When calling this, the order of calling this
14506 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14507 * is therefore critical. I know, it's all rather messy.
14508 *
14509 * @param strStateFile
14510 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14511 * the test for whether the saved state file is in use.
14512 */
14513void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14514 Snapshot *pSnapshotToIgnore)
14515{
14516 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14517 if ( (strStateFile.isNotEmpty())
14518 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14519 )
14520 // ... and it must also not be shared with other snapshots
14521 if ( !mData->mFirstSnapshot
14522 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14523 // this checks the SnapshotMachine's state file paths
14524 )
14525 RTFileDelete(strStateFile.c_str());
14526}
14527
14528/**
14529 * Locks the attached media.
14530 *
14531 * All attached hard disks are locked for writing and DVD/floppy are locked for
14532 * reading. Parents of attached hard disks (if any) are locked for reading.
14533 *
14534 * This method also performs accessibility check of all media it locks: if some
14535 * media is inaccessible, the method will return a failure and a bunch of
14536 * extended error info objects per each inaccessible medium.
14537 *
14538 * Note that this method is atomic: if it returns a success, all media are
14539 * locked as described above; on failure no media is locked at all (all
14540 * succeeded individual locks will be undone).
14541 *
14542 * The caller is responsible for doing the necessary state sanity checks.
14543 *
14544 * The locks made by this method must be undone by calling #unlockMedia() when
14545 * no more needed.
14546 */
14547HRESULT SessionMachine::i_lockMedia()
14548{
14549 AutoCaller autoCaller(this);
14550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14551
14552 AutoMultiWriteLock2 alock(this->lockHandle(),
14553 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14554
14555 /* bail out if trying to lock things with already set up locking */
14556 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14557
14558 MultiResult mrc(S_OK);
14559
14560 /* Collect locking information for all medium objects attached to the VM. */
14561 for (MediumAttachmentList::const_iterator
14562 it = mMediumAttachments->begin();
14563 it != mMediumAttachments->end();
14564 ++it)
14565 {
14566 MediumAttachment *pAtt = *it;
14567 DeviceType_T devType = pAtt->i_getType();
14568 Medium *pMedium = pAtt->i_getMedium();
14569
14570 MediumLockList *pMediumLockList(new MediumLockList());
14571 // There can be attachments without a medium (floppy/dvd), and thus
14572 // it's impossible to create a medium lock list. It still makes sense
14573 // to have the empty medium lock list in the map in case a medium is
14574 // attached later.
14575 if (pMedium != NULL)
14576 {
14577 MediumType_T mediumType = pMedium->i_getType();
14578 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14579 || mediumType == MediumType_Shareable;
14580 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14581
14582 alock.release();
14583 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14584 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14585 false /* fMediumLockWriteAll */,
14586 NULL,
14587 *pMediumLockList);
14588 alock.acquire();
14589 if (FAILED(mrc))
14590 {
14591 delete pMediumLockList;
14592 mData->mSession.mLockedMedia.Clear();
14593 break;
14594 }
14595 }
14596
14597 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14598 if (FAILED(rc))
14599 {
14600 mData->mSession.mLockedMedia.Clear();
14601 mrc = setError(rc,
14602 tr("Collecting locking information for all attached media failed"));
14603 break;
14604 }
14605 }
14606
14607 if (SUCCEEDED(mrc))
14608 {
14609 /* Now lock all media. If this fails, nothing is locked. */
14610 alock.release();
14611 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14612 alock.acquire();
14613 if (FAILED(rc))
14614 {
14615 mrc = setError(rc,
14616 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14617 }
14618 }
14619
14620 return mrc;
14621}
14622
14623/**
14624 * Undoes the locks made by by #lockMedia().
14625 */
14626HRESULT SessionMachine::i_unlockMedia()
14627{
14628 AutoCaller autoCaller(this);
14629 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14630
14631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14632
14633 /* we may be holding important error info on the current thread;
14634 * preserve it */
14635 ErrorInfoKeeper eik;
14636
14637 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14638 AssertComRC(rc);
14639 return rc;
14640}
14641
14642/**
14643 * Helper to change the machine state (reimplementation).
14644 *
14645 * @note Locks this object for writing.
14646 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14647 * it can cause crashes in random places due to unexpectedly committing
14648 * the current settings. The caller is responsible for that. The call
14649 * to saveStateSettings is fine, because this method does not commit.
14650 */
14651HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14652{
14653 LogFlowThisFuncEnter();
14654
14655 AutoCaller autoCaller(this);
14656 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14657
14658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14659
14660 MachineState_T oldMachineState = mData->mMachineState;
14661
14662 AssertMsgReturn(oldMachineState != aMachineState,
14663 ("oldMachineState=%s, aMachineState=%s\n",
14664 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14665 E_FAIL);
14666
14667 HRESULT rc = S_OK;
14668
14669 int stsFlags = 0;
14670 bool deleteSavedState = false;
14671
14672 /* detect some state transitions */
14673
14674 if ( ( ( oldMachineState == MachineState_Saved
14675 || oldMachineState == MachineState_AbortedSaved
14676 )
14677 && aMachineState == MachineState_Restoring
14678 )
14679 || ( ( oldMachineState == MachineState_PoweredOff
14680 || oldMachineState == MachineState_Teleported
14681 || oldMachineState == MachineState_Aborted
14682 )
14683 && ( aMachineState == MachineState_TeleportingIn
14684 || aMachineState == MachineState_Starting
14685 )
14686 )
14687 )
14688 {
14689 /* The EMT thread is about to start */
14690
14691 /* Nothing to do here for now... */
14692
14693 /// @todo NEWMEDIA don't let mDVDDrive and other children
14694 /// change anything when in the Starting/Restoring state
14695 }
14696 else if ( ( oldMachineState == MachineState_Running
14697 || oldMachineState == MachineState_Paused
14698 || oldMachineState == MachineState_Teleporting
14699 || oldMachineState == MachineState_OnlineSnapshotting
14700 || oldMachineState == MachineState_LiveSnapshotting
14701 || oldMachineState == MachineState_Stuck
14702 || oldMachineState == MachineState_Starting
14703 || oldMachineState == MachineState_Stopping
14704 || oldMachineState == MachineState_Saving
14705 || oldMachineState == MachineState_Restoring
14706 || oldMachineState == MachineState_TeleportingPausedVM
14707 || oldMachineState == MachineState_TeleportingIn
14708 )
14709 && ( aMachineState == MachineState_PoweredOff
14710 || aMachineState == MachineState_Saved
14711 || aMachineState == MachineState_Teleported
14712 || aMachineState == MachineState_Aborted
14713 || aMachineState == MachineState_AbortedSaved
14714 )
14715 )
14716 {
14717 /* The EMT thread has just stopped, unlock attached media. Note that as
14718 * opposed to locking that is done from Console, we do unlocking here
14719 * because the VM process may have aborted before having a chance to
14720 * properly unlock all media it locked. */
14721
14722 unlockMedia();
14723 }
14724
14725 if (oldMachineState == MachineState_Restoring)
14726 {
14727 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14728 {
14729 /*
14730 * delete the saved state file once the machine has finished
14731 * restoring from it (note that Console sets the state from
14732 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14733 * to give the user an ability to fix an error and retry --
14734 * we keep the saved state file in this case)
14735 */
14736 deleteSavedState = true;
14737 }
14738 }
14739 else if ( oldMachineState == MachineState_Saved
14740 && ( aMachineState == MachineState_PoweredOff
14741 || aMachineState == MachineState_Teleported
14742 )
14743 )
14744 {
14745 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14746 deleteSavedState = true;
14747 mData->mCurrentStateModified = TRUE;
14748 stsFlags |= SaveSTS_CurStateModified;
14749 }
14750 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14751 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14752
14753 if ( aMachineState == MachineState_Starting
14754 || aMachineState == MachineState_Restoring
14755 || aMachineState == MachineState_TeleportingIn
14756 )
14757 {
14758 /* set the current state modified flag to indicate that the current
14759 * state is no more identical to the state in the
14760 * current snapshot */
14761 if (!mData->mCurrentSnapshot.isNull())
14762 {
14763 mData->mCurrentStateModified = TRUE;
14764 stsFlags |= SaveSTS_CurStateModified;
14765 }
14766 }
14767
14768 if (deleteSavedState)
14769 {
14770 if (mRemoveSavedState)
14771 {
14772 Assert(!mSSData->strStateFilePath.isEmpty());
14773
14774 // it is safe to delete the saved state file if ...
14775 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14776 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14777 // ... none of the snapshots share the saved state file
14778 )
14779 RTFileDelete(mSSData->strStateFilePath.c_str());
14780 }
14781
14782 mSSData->strStateFilePath.setNull();
14783 stsFlags |= SaveSTS_StateFilePath;
14784 }
14785
14786 /* redirect to the underlying peer machine */
14787 mPeer->i_setMachineState(aMachineState);
14788
14789 if ( oldMachineState != MachineState_RestoringSnapshot
14790 && ( aMachineState == MachineState_PoweredOff
14791 || aMachineState == MachineState_Teleported
14792 || aMachineState == MachineState_Aborted
14793 || aMachineState == MachineState_AbortedSaved
14794 || aMachineState == MachineState_Saved))
14795 {
14796 /* the machine has stopped execution
14797 * (or the saved state file was adopted) */
14798 stsFlags |= SaveSTS_StateTimeStamp;
14799 }
14800
14801 if ( ( oldMachineState == MachineState_PoweredOff
14802 || oldMachineState == MachineState_Aborted
14803 || oldMachineState == MachineState_Teleported
14804 )
14805 && aMachineState == MachineState_Saved)
14806 {
14807 /* the saved state file was adopted */
14808 Assert(!mSSData->strStateFilePath.isEmpty());
14809 stsFlags |= SaveSTS_StateFilePath;
14810 }
14811
14812#ifdef VBOX_WITH_GUEST_PROPS
14813 if ( aMachineState == MachineState_PoweredOff
14814 || aMachineState == MachineState_Aborted
14815 || aMachineState == MachineState_Teleported)
14816 {
14817 /* Make sure any transient guest properties get removed from the
14818 * property store on shutdown. */
14819 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14820
14821 /* remove it from the settings representation */
14822 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14823 for (settings::GuestPropertiesList::iterator
14824 it = llGuestProperties.begin();
14825 it != llGuestProperties.end();
14826 /*nothing*/)
14827 {
14828 const settings::GuestProperty &prop = *it;
14829 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14830 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14831 {
14832 it = llGuestProperties.erase(it);
14833 fNeedsSaving = true;
14834 }
14835 else
14836 {
14837 ++it;
14838 }
14839 }
14840
14841 /* Additionally remove it from the HWData representation. Required to
14842 * keep everything in sync, as this is what the API keeps using. */
14843 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14844 for (HWData::GuestPropertyMap::iterator
14845 it = llHWGuestProperties.begin();
14846 it != llHWGuestProperties.end();
14847 /*nothing*/)
14848 {
14849 uint32_t fFlags = it->second.mFlags;
14850 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14851 {
14852 /* iterator where we need to continue after the erase call
14853 * (C++03 is a fact still, and it doesn't return the iterator
14854 * which would allow continuing) */
14855 HWData::GuestPropertyMap::iterator it2 = it;
14856 ++it2;
14857 llHWGuestProperties.erase(it);
14858 it = it2;
14859 fNeedsSaving = true;
14860 }
14861 else
14862 {
14863 ++it;
14864 }
14865 }
14866
14867 if (fNeedsSaving)
14868 {
14869 mData->mCurrentStateModified = TRUE;
14870 stsFlags |= SaveSTS_CurStateModified;
14871 }
14872 }
14873#endif /* VBOX_WITH_GUEST_PROPS */
14874
14875 rc = i_saveStateSettings(stsFlags);
14876
14877 if ( ( oldMachineState != MachineState_PoweredOff
14878 && oldMachineState != MachineState_Aborted
14879 && oldMachineState != MachineState_Teleported
14880 )
14881 && ( aMachineState == MachineState_PoweredOff
14882 || aMachineState == MachineState_Aborted
14883 || aMachineState == MachineState_Teleported
14884 )
14885 )
14886 {
14887 /* we've been shut down for any reason */
14888 /* no special action so far */
14889 }
14890
14891 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14892 LogFlowThisFuncLeave();
14893 return rc;
14894}
14895
14896/**
14897 * Sends the current machine state value to the VM process.
14898 *
14899 * @note Locks this object for reading, then calls a client process.
14900 */
14901HRESULT SessionMachine::i_updateMachineStateOnClient()
14902{
14903 AutoCaller autoCaller(this);
14904 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14905
14906 ComPtr<IInternalSessionControl> directControl;
14907 {
14908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14909 AssertReturn(!!mData, E_FAIL);
14910 if (mData->mSession.mLockType == LockType_VM)
14911 directControl = mData->mSession.mDirectControl;
14912
14913 /* directControl may be already set to NULL here in #OnSessionEnd()
14914 * called too early by the direct session process while there is still
14915 * some operation (like deleting the snapshot) in progress. The client
14916 * process in this case is waiting inside Session::close() for the
14917 * "end session" process object to complete, while #uninit() called by
14918 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14919 * operation to complete. For now, we accept this inconsistent behavior
14920 * and simply do nothing here. */
14921
14922 if (mData->mSession.mState == SessionState_Unlocking)
14923 return S_OK;
14924 }
14925
14926 /* ignore notifications sent after #OnSessionEnd() is called */
14927 if (!directControl)
14928 return S_OK;
14929
14930 return directControl->UpdateMachineState(mData->mMachineState);
14931}
14932
14933
14934/*static*/
14935HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14936{
14937 va_list args;
14938 va_start(args, pcszMsg);
14939 HRESULT rc = setErrorInternalV(aResultCode,
14940 getStaticClassIID(),
14941 getStaticComponentName(),
14942 pcszMsg, args,
14943 false /* aWarning */,
14944 true /* aLogIt */);
14945 va_end(args);
14946 return rc;
14947}
14948
14949
14950HRESULT Machine::updateState(MachineState_T aState)
14951{
14952 NOREF(aState);
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14957{
14958 NOREF(aProgress);
14959 ReturnComNotImplemented();
14960}
14961
14962HRESULT Machine::endPowerUp(LONG aResult)
14963{
14964 NOREF(aResult);
14965 ReturnComNotImplemented();
14966}
14967
14968HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14969{
14970 NOREF(aProgress);
14971 ReturnComNotImplemented();
14972}
14973
14974HRESULT Machine::endPoweringDown(LONG aResult,
14975 const com::Utf8Str &aErrMsg)
14976{
14977 NOREF(aResult);
14978 NOREF(aErrMsg);
14979 ReturnComNotImplemented();
14980}
14981
14982HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14983 BOOL *aMatched,
14984 ULONG *aMaskedInterfaces)
14985{
14986 NOREF(aDevice);
14987 NOREF(aMatched);
14988 NOREF(aMaskedInterfaces);
14989 ReturnComNotImplemented();
14990
14991}
14992
14993HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14994{
14995 NOREF(aId); NOREF(aCaptureFilename);
14996 ReturnComNotImplemented();
14997}
14998
14999HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15000 BOOL aDone)
15001{
15002 NOREF(aId);
15003 NOREF(aDone);
15004 ReturnComNotImplemented();
15005}
15006
15007HRESULT Machine::autoCaptureUSBDevices()
15008{
15009 ReturnComNotImplemented();
15010}
15011
15012HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15013{
15014 NOREF(aDone);
15015 ReturnComNotImplemented();
15016}
15017
15018HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15019 ComPtr<IProgress> &aProgress)
15020{
15021 NOREF(aSession);
15022 NOREF(aProgress);
15023 ReturnComNotImplemented();
15024}
15025
15026HRESULT Machine::finishOnlineMergeMedium()
15027{
15028 ReturnComNotImplemented();
15029}
15030
15031HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15032 std::vector<com::Utf8Str> &aValues,
15033 std::vector<LONG64> &aTimestamps,
15034 std::vector<com::Utf8Str> &aFlags)
15035{
15036 NOREF(aNames);
15037 NOREF(aValues);
15038 NOREF(aTimestamps);
15039 NOREF(aFlags);
15040 ReturnComNotImplemented();
15041}
15042
15043HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15044 const com::Utf8Str &aValue,
15045 LONG64 aTimestamp,
15046 const com::Utf8Str &aFlags)
15047{
15048 NOREF(aName);
15049 NOREF(aValue);
15050 NOREF(aTimestamp);
15051 NOREF(aFlags);
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::lockMedia()
15056{
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::unlockMedia()
15061{
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15066 ComPtr<IMediumAttachment> &aNewAttachment)
15067{
15068 NOREF(aAttachment);
15069 NOREF(aNewAttachment);
15070 ReturnComNotImplemented();
15071}
15072
15073HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15074 ULONG aCpuUser,
15075 ULONG aCpuKernel,
15076 ULONG aCpuIdle,
15077 ULONG aMemTotal,
15078 ULONG aMemFree,
15079 ULONG aMemBalloon,
15080 ULONG aMemShared,
15081 ULONG aMemCache,
15082 ULONG aPagedTotal,
15083 ULONG aMemAllocTotal,
15084 ULONG aMemFreeTotal,
15085 ULONG aMemBalloonTotal,
15086 ULONG aMemSharedTotal,
15087 ULONG aVmNetRx,
15088 ULONG aVmNetTx)
15089{
15090 NOREF(aValidStats);
15091 NOREF(aCpuUser);
15092 NOREF(aCpuKernel);
15093 NOREF(aCpuIdle);
15094 NOREF(aMemTotal);
15095 NOREF(aMemFree);
15096 NOREF(aMemBalloon);
15097 NOREF(aMemShared);
15098 NOREF(aMemCache);
15099 NOREF(aPagedTotal);
15100 NOREF(aMemAllocTotal);
15101 NOREF(aMemFreeTotal);
15102 NOREF(aMemBalloonTotal);
15103 NOREF(aMemSharedTotal);
15104 NOREF(aVmNetRx);
15105 NOREF(aVmNetTx);
15106 ReturnComNotImplemented();
15107}
15108
15109HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15110 com::Utf8Str &aResult)
15111{
15112 NOREF(aAuthParams);
15113 NOREF(aResult);
15114 ReturnComNotImplemented();
15115}
15116
15117com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15118{
15119 com::Utf8Str strControllerName = "Unknown";
15120 switch (aBusType)
15121 {
15122 case StorageBus_IDE:
15123 {
15124 strControllerName = "IDE";
15125 break;
15126 }
15127 case StorageBus_SATA:
15128 {
15129 strControllerName = "SATA";
15130 break;
15131 }
15132 case StorageBus_SCSI:
15133 {
15134 strControllerName = "SCSI";
15135 break;
15136 }
15137 case StorageBus_Floppy:
15138 {
15139 strControllerName = "Floppy";
15140 break;
15141 }
15142 case StorageBus_SAS:
15143 {
15144 strControllerName = "SAS";
15145 break;
15146 }
15147 case StorageBus_USB:
15148 {
15149 strControllerName = "USB";
15150 break;
15151 }
15152 default:
15153 break;
15154 }
15155 return strControllerName;
15156}
15157
15158HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15159{
15160 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15161
15162 AutoCaller autoCaller(this);
15163 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15164
15165 HRESULT rc = S_OK;
15166
15167 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15168 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15169 rc = getUSBDeviceFilters(usbDeviceFilters);
15170 if (FAILED(rc)) return rc;
15171
15172 NOREF(aFlags);
15173 com::Utf8Str osTypeId;
15174 ComObjPtr<GuestOSType> osType = NULL;
15175
15176 /* Get the guest os type as a string from the VB. */
15177 rc = getOSTypeId(osTypeId);
15178 if (FAILED(rc)) return rc;
15179
15180 /* Get the os type obj that coresponds, can be used to get
15181 * the defaults for this guest OS. */
15182 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15183 if (FAILED(rc)) return rc;
15184
15185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15186
15187 /* Let the OS type select 64-bit ness. */
15188 mHWData->mLongMode = osType->i_is64Bit()
15189 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15190
15191 /* Let the OS type enable the X2APIC */
15192 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15193
15194 /* This one covers IOAPICEnabled. */
15195 mBIOSSettings->i_applyDefaults(osType);
15196
15197 /* Initialize default record settings. */
15198 mRecordingSettings->i_applyDefaults();
15199
15200 /* Initialize default BIOS settings here */
15201 /* Hardware virtualization must be ON by default */
15202 mHWData->mAPIC = true;
15203 mHWData->mHWVirtExEnabled = true;
15204
15205 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15206 if (FAILED(rc)) return rc;
15207
15208 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15209 if (FAILED(rc)) return rc;
15210
15211 /* Graphics stuff. */
15212 GraphicsControllerType_T graphicsController;
15213 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15214 if (FAILED(rc)) return rc;
15215
15216 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15217 if (FAILED(rc)) return rc;
15218
15219 ULONG vramSize;
15220 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15221 if (FAILED(rc)) return rc;
15222
15223 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15224 if (FAILED(rc)) return rc;
15225
15226 BOOL fAccelerate2DVideoEnabled;
15227 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15228 if (FAILED(rc)) return rc;
15229
15230 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15231 if (FAILED(rc)) return rc;
15232
15233 BOOL fAccelerate3DEnabled;
15234 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15235 if (FAILED(rc)) return rc;
15236
15237 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15238 if (FAILED(rc)) return rc;
15239
15240 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15241 if (FAILED(rc)) return rc;
15242
15243 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15244 if (FAILED(rc)) return rc;
15245
15246 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15247 if (FAILED(rc)) return rc;
15248
15249 BOOL mRTCUseUTC;
15250 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15251 if (FAILED(rc)) return rc;
15252
15253 setRTCUseUTC(mRTCUseUTC);
15254 if (FAILED(rc)) return rc;
15255
15256 /* the setter does more than just the assignment, so use it */
15257 ChipsetType_T enmChipsetType;
15258 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15259 if (FAILED(rc)) return rc;
15260
15261 rc = COMSETTER(ChipsetType)(enmChipsetType);
15262 if (FAILED(rc)) return rc;
15263
15264 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15265 if (FAILED(rc)) return rc;
15266
15267 /* Apply IOMMU defaults. */
15268 IommuType_T enmIommuType;
15269 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15270 if (FAILED(rc)) return rc;
15271
15272 rc = COMSETTER(IommuType)(enmIommuType);
15273 if (FAILED(rc)) return rc;
15274
15275 /* Apply network adapters defaults */
15276 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15277 mNetworkAdapters[slot]->i_applyDefaults(osType);
15278
15279 /* Apply serial port defaults */
15280 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15281 mSerialPorts[slot]->i_applyDefaults(osType);
15282
15283 /* Apply parallel port defaults - not OS dependent*/
15284 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15285 mParallelPorts[slot]->i_applyDefaults();
15286
15287 /* This one covers the TPM type. */
15288 mTrustedPlatformModule->i_applyDefaults(osType);
15289
15290 /* This one covers secure boot. */
15291 rc = mNvramStore->i_applyDefaults(osType);
15292 if (FAILED(rc)) return rc;
15293
15294 /* Audio stuff. */
15295 AudioControllerType_T audioController;
15296 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15297 if (FAILED(rc)) return rc;
15298
15299 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15300 if (FAILED(rc)) return rc;
15301
15302 AudioCodecType_T audioCodec;
15303 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15304 if (FAILED(rc)) return rc;
15305
15306 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15307 if (FAILED(rc)) return rc;
15308
15309 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15310 if (FAILED(rc)) return rc;
15311
15312 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15313 if (FAILED(rc)) return rc;
15314
15315 /* Storage Controllers */
15316 StorageControllerType_T hdStorageControllerType;
15317 StorageBus_T hdStorageBusType;
15318 StorageControllerType_T dvdStorageControllerType;
15319 StorageBus_T dvdStorageBusType;
15320 BOOL recommendedFloppy;
15321 ComPtr<IStorageController> floppyController;
15322 ComPtr<IStorageController> hdController;
15323 ComPtr<IStorageController> dvdController;
15324 Utf8Str strFloppyName, strDVDName, strHDName;
15325
15326 /* GUI auto generates controller names using bus type. Do the same*/
15327 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15328
15329 /* Floppy recommended? add one. */
15330 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15331 if (FAILED(rc)) return rc;
15332 if (recommendedFloppy)
15333 {
15334 rc = addStorageController(strFloppyName,
15335 StorageBus_Floppy,
15336 floppyController);
15337 if (FAILED(rc)) return rc;
15338 }
15339
15340 /* Setup one DVD storage controller. */
15341 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15342 if (FAILED(rc)) return rc;
15343
15344 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15345 if (FAILED(rc)) return rc;
15346
15347 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15348
15349 rc = addStorageController(strDVDName,
15350 dvdStorageBusType,
15351 dvdController);
15352 if (FAILED(rc)) return rc;
15353
15354 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15355 if (FAILED(rc)) return rc;
15356
15357 /* Setup one HDD storage controller. */
15358 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15359 if (FAILED(rc)) return rc;
15360
15361 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15362 if (FAILED(rc)) return rc;
15363
15364 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15365
15366 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15367 {
15368 rc = addStorageController(strHDName,
15369 hdStorageBusType,
15370 hdController);
15371 if (FAILED(rc)) return rc;
15372
15373 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15374 if (FAILED(rc)) return rc;
15375 }
15376 else
15377 {
15378 /* The HD controller is the same as DVD: */
15379 hdController = dvdController;
15380 }
15381
15382 /* Limit the AHCI port count if it's used because windows has trouble with
15383 * too many ports and other guest (OS X in particular) may take extra long
15384 * boot: */
15385
15386 // pParent = static_cast<Medium*>(aP)
15387 IStorageController *temp = hdController;
15388 ComObjPtr<StorageController> storageController;
15389 storageController = static_cast<StorageController *>(temp);
15390
15391 // tempHDController = aHDController;
15392 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15393 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15394 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15395 storageController->COMSETTER(PortCount)(1);
15396
15397 /* USB stuff */
15398
15399 bool ohciEnabled = false;
15400
15401 ComPtr<IUSBController> usbController;
15402 BOOL recommendedUSB3;
15403 BOOL recommendedUSB;
15404 BOOL usbProxyAvailable;
15405
15406 getUSBProxyAvailable(&usbProxyAvailable);
15407 if (FAILED(rc)) return rc;
15408
15409 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15410 if (FAILED(rc)) return rc;
15411 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15412 if (FAILED(rc)) return rc;
15413
15414 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15415 {
15416#ifdef VBOX_WITH_EXTPACK
15417 /* USB 3.0 is only available if the proper ExtPack is installed. */
15418 ExtPackManager *aManager = mParent->i_getExtPackManager();
15419 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15420 {
15421 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15422 if (FAILED(rc)) return rc;
15423
15424 /* xHci includes OHCI */
15425 ohciEnabled = true;
15426 }
15427#endif
15428 }
15429 if ( !ohciEnabled
15430 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15431 {
15432 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15433 if (FAILED(rc)) return rc;
15434 ohciEnabled = true;
15435
15436#ifdef VBOX_WITH_EXTPACK
15437 /* USB 2.0 is only available if the proper ExtPack is installed.
15438 * Note. Configuring EHCI here and providing messages about
15439 * the missing extpack isn't exactly clean, but it is a
15440 * necessary evil to patch over legacy compatability issues
15441 * introduced by the new distribution model. */
15442 ExtPackManager *manager = mParent->i_getExtPackManager();
15443 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15444 {
15445 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15446 if (FAILED(rc)) return rc;
15447 }
15448#endif
15449 }
15450
15451 /* Set recommended human interface device types: */
15452 BOOL recommendedUSBHID;
15453 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15454 if (FAILED(rc)) return rc;
15455
15456 if (recommendedUSBHID)
15457 {
15458 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15459 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15460 if (!ohciEnabled && !usbDeviceFilters.isNull())
15461 {
15462 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15463 if (FAILED(rc)) return rc;
15464 }
15465 }
15466
15467 BOOL recommendedUSBTablet;
15468 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15469 if (FAILED(rc)) return rc;
15470
15471 if (recommendedUSBTablet)
15472 {
15473 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15474 if (!ohciEnabled && !usbDeviceFilters.isNull())
15475 {
15476 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15477 if (FAILED(rc)) return rc;
15478 }
15479 }
15480
15481 /* Enable the VMMDev testing feature for bootsector VMs: */
15482 if (osTypeId == "VBoxBS_64")
15483 {
15484 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15485 if (FAILED(rc))
15486 return rc;
15487 }
15488
15489 return S_OK;
15490}
15491
15492/* This isn't handled entirely by the wrapper generator yet. */
15493#ifdef VBOX_WITH_XPCOM
15494NS_DECL_CLASSINFO(SessionMachine)
15495NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15496
15497NS_DECL_CLASSINFO(SnapshotMachine)
15498NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15499#endif
Note: See TracBrowser for help on using the repository browser.

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