VirtualBox

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

Last change on this file since 91614 was 91614, checked in by vboxsync, 4 years ago

Main: Initialize the TPM type properly based on the guest OS type and also initialize the UEFI variable store based on the guest OS type secure boot setting, bugref:9580 and bugref:10075

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 540.4 KB
Line 
1/* $Id: MachineImpl.cpp 91614 2021-10-07 10:12:16Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
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#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185 mHWVirtExVirtVmsaveVmload = true;
186#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
187 mPAEEnabled = true;
188#else
189 mPAEEnabled = false;
190#endif
191 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
192 mTripleFaultReset = false;
193 mAPIC = true;
194 mX2APIC = false;
195 mIBPBOnVMExit = false;
196 mIBPBOnVMEntry = false;
197 mSpecCtrl = false;
198 mSpecCtrlByHost = false;
199 mL1DFlushOnSched = true;
200 mL1DFlushOnVMEntry = false;
201 mMDSClearOnSched = true;
202 mMDSClearOnVMEntry = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mClipboardFileTransfersEnabled = FALSE;
218
219 mDnDMode = DnDMode_Disabled;
220
221 mFirmwareType = FirmwareType_BIOS;
222 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
223 mPointingHIDType = PointingHIDType_PS2Mouse;
224 mChipsetType = ChipsetType_PIIX3;
225 mIommuType = IommuType_None;
226 mParavirtProvider = ParavirtProvider_Default;
227 mEmulatedUSBCardReaderEnabled = FALSE;
228
229 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
230 mCPUAttached[i] = false;
231
232 mIOCacheEnabled = true;
233 mIOCacheSize = 5; /* 5MB */
234}
235
236Machine::HWData::~HWData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine() :
248#ifdef VBOX_WITH_RESOURCE_USAGE_API
249 mCollectorGuest(NULL),
250#endif
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param strOsType OS Type string (stored as is if aOsType is NULL).
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
288 * scheme (includes the UUID).
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 const Utf8Str &strOsType,
297 GuestOSType *aOsType,
298 const Guid &aId,
299 bool fForceOverwrite,
300 bool fDirectoryIncludesUUID)
301{
302 LogFlowThisFuncEnter();
303 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
304
305 /* Enclose the state transition NotReady->InInit->Ready */
306 AutoInitSpan autoInitSpan(this);
307 AssertReturn(autoInitSpan.isOk(), E_FAIL);
308
309 HRESULT rc = initImpl(aParent, strConfigFile);
310 if (FAILED(rc)) return rc;
311
312 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
313 if (FAILED(rc)) return rc;
314
315 if (SUCCEEDED(rc))
316 {
317 // create an empty machine config
318 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
319
320 rc = initDataAndChildObjects();
321 }
322
323 if (SUCCEEDED(rc))
324 {
325 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
326 mData->mAccessible = TRUE;
327
328 unconst(mData->mUuid) = aId;
329
330 mUserData->s.strName = strName;
331
332 if (llGroups.size())
333 mUserData->s.llGroups = llGroups;
334
335 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
336 // the "name sync" flag determines whether the machine directory gets renamed along
337 // with the machine file; say so if the settings file name is the same as the
338 // settings file parent directory (machine directory)
339 mUserData->s.fNameSync = i_isInOwnDir();
340
341 // initialize the default snapshots folder
342 rc = COMSETTER(SnapshotFolder)(NULL);
343 AssertComRC(rc);
344
345 if (aOsType)
346 {
347 /* Store OS type */
348 mUserData->s.strOsType = aOsType->i_id();
349
350 /* Let the OS type select 64-bit ness. */
351 mHWData->mLongMode = aOsType->i_is64Bit()
352 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
353
354 /* Let the OS type enable the X2APIC */
355 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
356 }
357 else if (!strOsType.isEmpty())
358 {
359 /* Store OS type */
360 mUserData->s.strOsType = strOsType;
361
362 /* No guest OS type object. Pick some plausible defaults which the
363 * host can handle. There's no way to know or validate anything. */
364 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365 mHWData->mX2APIC = false;
366 }
367
368 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
369 AssertComRC(rc);
370
371 /* Apply BIOS defaults. */
372 mBIOSSettings->i_applyDefaults(aOsType);
373
374 /* Apply TPM defaults. */
375 mTrustedPlatformModule->i_applyDefaults(aOsType);
376
377 /* Apply record defaults. */
378 mRecordingSettings->i_applyDefaults();
379
380 /* Apply network adapters defaults */
381 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
382 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
383
384 /* Apply serial port defaults */
385 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
386 mSerialPorts[slot]->i_applyDefaults(aOsType);
387
388 /* Apply parallel port defaults */
389 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
390 mParallelPorts[slot]->i_applyDefaults();
391
392 /* At this point the changing of the current state modification
393 * flag is allowed. */
394 i_allowStateModification();
395
396 /* commit all changes made during the initialization */
397 i_commit();
398 }
399
400 /* Confirm a successful initialization when it's the case */
401 if (SUCCEEDED(rc))
402 {
403 if (mData->mAccessible)
404 autoInitSpan.setSucceeded();
405 else
406 autoInitSpan.setLimited();
407 }
408
409 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
410 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
411 mData->mRegistered,
412 mData->mAccessible,
413 rc));
414
415 LogFlowThisFuncLeave();
416
417 return rc;
418}
419
420/**
421 * Initializes a new instance with data from machine XML (formerly Init_Registered).
422 * Gets called in two modes:
423 *
424 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
425 * UUID is specified and we mark the machine as "registered";
426 *
427 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
428 * and the machine remains unregistered until RegisterMachine() is called.
429 *
430 * @param aParent Associated parent object
431 * @param strConfigFile Local file system path to the VM settings file (can
432 * be relative to the VirtualBox config directory).
433 * @param aId UUID of the machine or NULL (see above).
434 *
435 * @return Success indicator. if not S_OK, the machine object is invalid
436 */
437HRESULT Machine::initFromSettings(VirtualBox *aParent,
438 const Utf8Str &strConfigFile,
439 const Guid *aId)
440{
441 LogFlowThisFuncEnter();
442 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
443
444 /* Enclose the state transition NotReady->InInit->Ready */
445 AutoInitSpan autoInitSpan(this);
446 AssertReturn(autoInitSpan.isOk(), E_FAIL);
447
448 HRESULT rc = initImpl(aParent, strConfigFile);
449 if (FAILED(rc)) return rc;
450
451 if (aId)
452 {
453 // loading a registered VM:
454 unconst(mData->mUuid) = *aId;
455 mData->mRegistered = TRUE;
456 // now load the settings from XML:
457 rc = i_registeredInit();
458 // this calls initDataAndChildObjects() and loadSettings()
459 }
460 else
461 {
462 // opening an unregistered VM (VirtualBox::OpenMachine()):
463 rc = initDataAndChildObjects();
464
465 if (SUCCEEDED(rc))
466 {
467 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
468 mData->mAccessible = TRUE;
469
470 try
471 {
472 // load and parse machine XML; this will throw on XML or logic errors
473 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
474
475 // reject VM UUID duplicates, they can happen if someone
476 // tries to register an already known VM config again
477 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
478 true /* fPermitInaccessible */,
479 false /* aDoSetError */,
480 NULL) != VBOX_E_OBJECT_NOT_FOUND)
481 {
482 throw setError(E_FAIL,
483 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
484 mData->m_strConfigFile.c_str());
485 }
486
487 // use UUID from machine config
488 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
489
490 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
491 NULL /* puuidRegistry */);
492 if (FAILED(rc)) throw rc;
493
494 /* At this point the changing of the current state modification
495 * flag is allowed. */
496 i_allowStateModification();
497
498 i_commit();
499 }
500 catch (HRESULT err)
501 {
502 /* we assume that error info is set by the thrower */
503 rc = err;
504 }
505 catch (...)
506 {
507 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
508 }
509 }
510 }
511
512 /* Confirm a successful initialization when it's the case */
513 if (SUCCEEDED(rc))
514 {
515 if (mData->mAccessible)
516 autoInitSpan.setSucceeded();
517 else
518 {
519 autoInitSpan.setLimited();
520
521 // uninit media from this machine's media registry, or else
522 // reloading the settings will fail
523 mParent->i_unregisterMachineMedia(i_getId());
524 }
525 }
526
527 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
528 "rc=%08X\n",
529 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
530 mData->mRegistered, mData->mAccessible, rc));
531
532 LogFlowThisFuncLeave();
533
534 return rc;
535}
536
537/**
538 * Initializes a new instance from a machine config that is already in memory
539 * (import OVF case). Since we are importing, the UUID in the machine
540 * config is ignored and we always generate a fresh one.
541 *
542 * @param aParent Associated parent object.
543 * @param strName Name for the new machine; this overrides what is specified in config.
544 * @param strSettingsFilename File name of .vbox file.
545 * @param config Machine configuration loaded and parsed from XML.
546 *
547 * @return Success indicator. if not S_OK, the machine object is invalid
548 */
549HRESULT Machine::init(VirtualBox *aParent,
550 const Utf8Str &strName,
551 const Utf8Str &strSettingsFilename,
552 const settings::MachineConfigFile &config)
553{
554 LogFlowThisFuncEnter();
555
556 /* Enclose the state transition NotReady->InInit->Ready */
557 AutoInitSpan autoInitSpan(this);
558 AssertReturn(autoInitSpan.isOk(), E_FAIL);
559
560 HRESULT rc = initImpl(aParent, strSettingsFilename);
561 if (FAILED(rc)) return rc;
562
563 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
564 if (FAILED(rc)) return rc;
565
566 rc = initDataAndChildObjects();
567
568 if (SUCCEEDED(rc))
569 {
570 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
571 mData->mAccessible = TRUE;
572
573 // create empty machine config for instance data
574 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
575
576 // generate fresh UUID, ignore machine config
577 unconst(mData->mUuid).create();
578
579 rc = i_loadMachineDataFromSettings(config,
580 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
581
582 // override VM name as well, it may be different
583 mUserData->s.strName = strName;
584
585 if (SUCCEEDED(rc))
586 {
587 /* At this point the changing of the current state modification
588 * flag is allowed. */
589 i_allowStateModification();
590
591 /* commit all changes made during the initialization */
592 i_commit();
593 }
594 }
595
596 /* Confirm a successful initialization when it's the case */
597 if (SUCCEEDED(rc))
598 {
599 if (mData->mAccessible)
600 autoInitSpan.setSucceeded();
601 else
602 {
603 /* Ignore all errors from unregistering, they would destroy
604- * the more interesting error information we already have,
605- * pinpointing the issue with the VM config. */
606 ErrorInfoKeeper eik;
607
608 autoInitSpan.setLimited();
609
610 // uninit media from this machine's media registry, or else
611 // reloading the settings will fail
612 mParent->i_unregisterMachineMedia(i_getId());
613 }
614 }
615
616 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
617 "rc=%08X\n",
618 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
619 mData->mRegistered, mData->mAccessible, rc));
620
621 LogFlowThisFuncLeave();
622
623 return rc;
624}
625
626/**
627 * Shared code between the various init() implementations.
628 * @param aParent The VirtualBox object.
629 * @param strConfigFile Settings file.
630 * @return
631 */
632HRESULT Machine::initImpl(VirtualBox *aParent,
633 const Utf8Str &strConfigFile)
634{
635 LogFlowThisFuncEnter();
636
637 AssertReturn(aParent, E_INVALIDARG);
638 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
639
640 HRESULT rc = S_OK;
641
642 /* share the parent weakly */
643 unconst(mParent) = aParent;
644
645 /* allocate the essential machine data structure (the rest will be
646 * allocated later by initDataAndChildObjects() */
647 mData.allocate();
648
649 /* memorize the config file name (as provided) */
650 mData->m_strConfigFile = strConfigFile;
651
652 /* get the full file name */
653 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
654 if (RT_FAILURE(vrc1))
655 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
656 tr("Invalid machine settings file name '%s' (%Rrc)"),
657 strConfigFile.c_str(),
658 vrc1);
659
660 LogFlowThisFuncLeave();
661
662 return rc;
663}
664
665/**
666 * Tries to create a machine settings file in the path stored in the machine
667 * instance data. Used when a new machine is created to fail gracefully if
668 * the settings file could not be written (e.g. because machine dir is read-only).
669 * @return
670 */
671HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
672{
673 HRESULT rc = S_OK;
674
675 // when we create a new machine, we must be able to create the settings file
676 RTFILE f = NIL_RTFILE;
677 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
678 if ( RT_SUCCESS(vrc)
679 || vrc == VERR_SHARING_VIOLATION
680 )
681 {
682 if (RT_SUCCESS(vrc))
683 RTFileClose(f);
684 if (!fForceOverwrite)
685 rc = setError(VBOX_E_FILE_ERROR,
686 tr("Machine settings file '%s' already exists"),
687 mData->m_strConfigFileFull.c_str());
688 else
689 {
690 /* try to delete the config file, as otherwise the creation
691 * of a new settings file will fail. */
692 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
693 if (RT_FAILURE(vrc2))
694 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
695 tr("Could not delete the existing settings file '%s' (%Rrc)"),
696 mData->m_strConfigFileFull.c_str(), vrc2);
697 }
698 }
699 else if ( vrc != VERR_FILE_NOT_FOUND
700 && vrc != VERR_PATH_NOT_FOUND
701 )
702 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
703 tr("Invalid machine settings file name '%s' (%Rrc)"),
704 mData->m_strConfigFileFull.c_str(),
705 vrc);
706 return rc;
707}
708
709/**
710 * Initializes the registered machine by loading the settings file.
711 * This method is separated from #init() in order to make it possible to
712 * retry the operation after VirtualBox startup instead of refusing to
713 * startup the whole VirtualBox server in case if the settings file of some
714 * registered VM is invalid or inaccessible.
715 *
716 * @note Must be always called from this object's write lock
717 * (unless called from #init() that doesn't need any locking).
718 * @note Locks the mUSBController method for writing.
719 * @note Subclasses must not call this method.
720 */
721HRESULT Machine::i_registeredInit()
722{
723 AssertReturn(!i_isSessionMachine(), E_FAIL);
724 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
725 AssertReturn(mData->mUuid.isValid(), E_FAIL);
726 AssertReturn(!mData->mAccessible, E_FAIL);
727
728 HRESULT rc = initDataAndChildObjects();
729
730 if (SUCCEEDED(rc))
731 {
732 /* Temporarily reset the registered flag in order to let setters
733 * potentially called from loadSettings() succeed (isMutable() used in
734 * all setters will return FALSE for a Machine instance if mRegistered
735 * is TRUE). */
736 mData->mRegistered = FALSE;
737
738 try
739 {
740 // load and parse machine XML; this will throw on XML or logic errors
741 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
742
743 if (mData->mUuid != mData->pMachineConfigFile->uuid)
744 throw setError(E_FAIL,
745 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
746 mData->pMachineConfigFile->uuid.raw(),
747 mData->m_strConfigFileFull.c_str(),
748 mData->mUuid.toString().c_str(),
749 mParent->i_settingsFilePath().c_str());
750
751 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
752 NULL /* const Guid *puuidRegistry */);
753 if (FAILED(rc)) throw rc;
754 }
755 catch (HRESULT err)
756 {
757 /* we assume that error info is set by the thrower */
758 rc = err;
759 }
760 catch (...)
761 {
762 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
763 }
764
765 /* Restore the registered flag (even on failure) */
766 mData->mRegistered = TRUE;
767 }
768
769 if (SUCCEEDED(rc))
770 {
771 /* Set mAccessible to TRUE only if we successfully locked and loaded
772 * the settings file */
773 mData->mAccessible = TRUE;
774
775 /* commit all changes made during loading the settings file */
776 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
777 /// @todo r=klaus for some reason the settings loading logic backs up
778 // the settings, and therefore a commit is needed. Should probably be changed.
779 }
780 else
781 {
782 /* If the machine is registered, then, instead of returning a
783 * failure, we mark it as inaccessible and set the result to
784 * success to give it a try later */
785
786 /* fetch the current error info */
787 mData->mAccessError = com::ErrorInfo();
788 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
789
790 /* rollback all changes */
791 i_rollback(false /* aNotify */);
792
793 // uninit media from this machine's media registry, or else
794 // reloading the settings will fail
795 mParent->i_unregisterMachineMedia(i_getId());
796
797 /* uninitialize the common part to make sure all data is reset to
798 * default (null) values */
799 uninitDataAndChildObjects();
800
801 rc = S_OK;
802 }
803
804 return rc;
805}
806
807/**
808 * Uninitializes the instance.
809 * Called either from FinalRelease() or by the parent when it gets destroyed.
810 *
811 * @note The caller of this method must make sure that this object
812 * a) doesn't have active callers on the current thread and b) is not locked
813 * by the current thread; otherwise uninit() will hang either a) due to
814 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
815 * a dead-lock caused by this thread waiting for all callers on the other
816 * threads are done but preventing them from doing so by holding a lock.
817 */
818void Machine::uninit()
819{
820 LogFlowThisFuncEnter();
821
822 Assert(!isWriteLockOnCurrentThread());
823
824 Assert(!uRegistryNeedsSaving);
825 if (uRegistryNeedsSaving)
826 {
827 AutoCaller autoCaller(this);
828 if (SUCCEEDED(autoCaller.rc()))
829 {
830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
831 i_saveSettings(NULL, Machine::SaveS_Force);
832 }
833 }
834
835 /* Enclose the state transition Ready->InUninit->NotReady */
836 AutoUninitSpan autoUninitSpan(this);
837 if (autoUninitSpan.uninitDone())
838 return;
839
840 Assert(!i_isSnapshotMachine());
841 Assert(!i_isSessionMachine());
842 Assert(!!mData);
843
844 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
845 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
846
847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
848
849 if (!mData->mSession.mMachine.isNull())
850 {
851 /* Theoretically, this can only happen if the VirtualBox server has been
852 * terminated while there were clients running that owned open direct
853 * sessions. Since in this case we are definitely called by
854 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
855 * won't happen on the client watcher thread (because it has a
856 * VirtualBox caller for the duration of the
857 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
858 * cannot happen until the VirtualBox caller is released). This is
859 * important, because SessionMachine::uninit() cannot correctly operate
860 * after we return from this method (it expects the Machine instance is
861 * still valid). We'll call it ourselves below.
862 */
863 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
864 (SessionMachine*)mData->mSession.mMachine));
865
866 if (Global::IsOnlineOrTransient(mData->mMachineState))
867 {
868 Log1WarningThisFunc(("Setting state to Aborted!\n"));
869 /* set machine state using SessionMachine reimplementation */
870 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
871 }
872
873 /*
874 * Uninitialize SessionMachine using public uninit() to indicate
875 * an unexpected uninitialization.
876 */
877 mData->mSession.mMachine->uninit();
878 /* SessionMachine::uninit() must set mSession.mMachine to null */
879 Assert(mData->mSession.mMachine.isNull());
880 }
881
882 // uninit media from this machine's media registry, if they're still there
883 Guid uuidMachine(i_getId());
884
885 /* the lock is no more necessary (SessionMachine is uninitialized) */
886 alock.release();
887
888 /* XXX This will fail with
889 * "cannot be closed because it is still attached to 1 virtual machines"
890 * because at this point we did not call uninitDataAndChildObjects() yet
891 * and therefore also removeBackReference() for all these mediums was not called! */
892
893 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
894 mParent->i_unregisterMachineMedia(uuidMachine);
895
896 // has machine been modified?
897 if (mData->flModifications)
898 {
899 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
900 i_rollback(false /* aNotify */);
901 }
902
903 if (mData->mAccessible)
904 uninitDataAndChildObjects();
905
906 /* free the essential data structure last */
907 mData.free();
908
909 LogFlowThisFuncLeave();
910}
911
912// Wrapped IMachine properties
913/////////////////////////////////////////////////////////////////////////////
914HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
915{
916 /* mParent is constant during life time, no need to lock */
917 ComObjPtr<VirtualBox> pVirtualBox(mParent);
918 aParent = pVirtualBox;
919
920 return S_OK;
921}
922
923
924HRESULT Machine::getAccessible(BOOL *aAccessible)
925{
926 /* In some cases (medium registry related), it is necessary to be able to
927 * go through the list of all machines. Happens when an inaccessible VM
928 * has a sensible medium registry. */
929 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
931
932 HRESULT rc = S_OK;
933
934 if (!mData->mAccessible)
935 {
936 /* try to initialize the VM once more if not accessible */
937
938 AutoReinitSpan autoReinitSpan(this);
939 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
940
941#ifdef DEBUG
942 LogFlowThisFunc(("Dumping media backreferences\n"));
943 mParent->i_dumpAllBackRefs();
944#endif
945
946 if (mData->pMachineConfigFile)
947 {
948 // reset the XML file to force loadSettings() (called from i_registeredInit())
949 // to parse it again; the file might have changed
950 delete mData->pMachineConfigFile;
951 mData->pMachineConfigFile = NULL;
952 }
953
954 rc = i_registeredInit();
955
956 if (SUCCEEDED(rc) && mData->mAccessible)
957 {
958 autoReinitSpan.setSucceeded();
959
960 /* make sure interesting parties will notice the accessibility
961 * state change */
962 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
963 mParent->i_onMachineDataChanged(mData->mUuid);
964 }
965 }
966
967 if (SUCCEEDED(rc))
968 *aAccessible = mData->mAccessible;
969
970 LogFlowThisFuncLeave();
971
972 return rc;
973}
974
975HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
976{
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
980 {
981 /* return shortly */
982 aAccessError = NULL;
983 return S_OK;
984 }
985
986 HRESULT rc = S_OK;
987
988 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
989 rc = errorInfo.createObject();
990 if (SUCCEEDED(rc))
991 {
992 errorInfo->init(mData->mAccessError.getResultCode(),
993 mData->mAccessError.getInterfaceID().ref(),
994 Utf8Str(mData->mAccessError.getComponent()).c_str(),
995 Utf8Str(mData->mAccessError.getText()));
996 aAccessError = errorInfo;
997 }
998
999 return rc;
1000}
1001
1002HRESULT Machine::getName(com::Utf8Str &aName)
1003{
1004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1005
1006 aName = mUserData->s.strName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::setName(const com::Utf8Str &aName)
1012{
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 i_setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1033{
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 aDescription = mUserData->s.strDescription;
1037
1038 return S_OK;
1039}
1040
1041HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1042{
1043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 // this can be done in principle in any state as it doesn't affect the VM
1046 // significantly, but play safe by not messing around while complex
1047 // activities are going on
1048 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1049 if (FAILED(rc)) return rc;
1050
1051 i_setModified(IsModified_MachineData);
1052 mUserData.backup();
1053 mUserData->s.strDescription = aDescription;
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::getId(com::Guid &aId)
1059{
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 aId = mData->mUuid;
1063
1064 return S_OK;
1065}
1066
1067HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1068{
1069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1070 aGroups.resize(mUserData->s.llGroups.size());
1071 size_t i = 0;
1072 for (StringsList::const_iterator
1073 it = mUserData->s.llGroups.begin();
1074 it != mUserData->s.llGroups.end();
1075 ++it, ++i)
1076 aGroups[i] = (*it);
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1082{
1083 StringsList llGroups;
1084 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1085 if (FAILED(rc))
1086 return rc;
1087
1088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1089
1090 rc = i_checkStateDependency(MutableOrSavedStateDep);
1091 if (FAILED(rc)) return rc;
1092
1093 i_setModified(IsModified_MachineData);
1094 mUserData.backup();
1095 mUserData->s.llGroups = llGroups;
1096
1097 return S_OK;
1098}
1099
1100HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1101{
1102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 aOSTypeId = mUserData->s.strOsType;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1110{
1111 /* look up the object by Id to check it is valid */
1112 ComObjPtr<GuestOSType> pGuestOSType;
1113 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1114
1115 /* when setting, always use the "etalon" value for consistency -- lookup
1116 * by ID is case-insensitive and the input value may have different case */
1117 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1118
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mUserData.backup();
1126 mUserData->s.strOsType = osTypeId;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aFirmwareType = mHWData->mFirmwareType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mFirmwareType = aFirmwareType;
1150 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1151 alock.release();
1152
1153 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aPointingHIDType = mHWData->mPointingHIDType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 i_setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mPointingHIDType = aPointingHIDType;
1200
1201 return S_OK;
1202}
1203
1204HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1205{
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *aChipsetType = mHWData->mChipsetType;
1209
1210 return S_OK;
1211}
1212
1213HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1214{
1215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1216
1217 HRESULT rc = i_checkStateDependency(MutableStateDep);
1218 if (FAILED(rc)) return rc;
1219
1220 if (aChipsetType != mHWData->mChipsetType)
1221 {
1222 i_setModified(IsModified_MachineData);
1223 mHWData.backup();
1224 mHWData->mChipsetType = aChipsetType;
1225
1226 // Resize network adapter array, to be finalized on commit/rollback.
1227 // We must not throw away entries yet, otherwise settings are lost
1228 // without a way to roll back.
1229 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1230 size_t oldCount = mNetworkAdapters.size();
1231 if (newCount > oldCount)
1232 {
1233 mNetworkAdapters.resize(newCount);
1234 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1235 {
1236 unconst(mNetworkAdapters[slot]).createObject();
1237 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1238 }
1239 }
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aIommuType = mHWData->mIommuType;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setIommuType(IommuType_T aIommuType)
1255{
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = i_checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aIommuType != mHWData->mIommuType)
1262 {
1263 if (aIommuType == IommuType_Intel)
1264 {
1265#ifndef VBOX_WITH_IOMMU_INTEL
1266 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1267 return E_UNEXPECTED;
1268#endif
1269 }
1270
1271 i_setModified(IsModified_MachineData);
1272 mHWData.backup();
1273 mHWData->mIommuType = aIommuType;
1274 }
1275
1276 return S_OK;
1277}
1278
1279HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1280{
1281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1282
1283 aParavirtDebug = mHWData->mParavirtDebug;
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1288{
1289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1290
1291 HRESULT rc = i_checkStateDependency(MutableStateDep);
1292 if (FAILED(rc)) return rc;
1293
1294 /** @todo Parse/validate options? */
1295 if (aParavirtDebug != mHWData->mParavirtDebug)
1296 {
1297 i_setModified(IsModified_MachineData);
1298 mHWData.backup();
1299 mHWData->mParavirtDebug = aParavirtDebug;
1300 }
1301
1302 return S_OK;
1303}
1304
1305HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1306{
1307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 *aParavirtProvider = mHWData->mParavirtProvider;
1310
1311 return S_OK;
1312}
1313
1314HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1315{
1316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1317
1318 HRESULT rc = i_checkStateDependency(MutableStateDep);
1319 if (FAILED(rc)) return rc;
1320
1321 if (aParavirtProvider != mHWData->mParavirtProvider)
1322 {
1323 i_setModified(IsModified_MachineData);
1324 mHWData.backup();
1325 mHWData->mParavirtProvider = aParavirtProvider;
1326 }
1327
1328 return S_OK;
1329}
1330
1331HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1332{
1333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 *aParavirtProvider = mHWData->mParavirtProvider;
1336 switch (mHWData->mParavirtProvider)
1337 {
1338 case ParavirtProvider_None:
1339 case ParavirtProvider_HyperV:
1340 case ParavirtProvider_KVM:
1341 case ParavirtProvider_Minimal:
1342 break;
1343
1344 /* Resolve dynamic provider types to the effective types. */
1345 default:
1346 {
1347 ComObjPtr<GuestOSType> pGuestOSType;
1348 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1349 pGuestOSType);
1350 if (FAILED(hrc2) || pGuestOSType.isNull())
1351 {
1352 *aParavirtProvider = ParavirtProvider_None;
1353 break;
1354 }
1355
1356 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1357 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1358
1359 switch (mHWData->mParavirtProvider)
1360 {
1361 case ParavirtProvider_Legacy:
1362 {
1363 if (fOsXGuest)
1364 *aParavirtProvider = ParavirtProvider_Minimal;
1365 else
1366 *aParavirtProvider = ParavirtProvider_None;
1367 break;
1368 }
1369
1370 case ParavirtProvider_Default:
1371 {
1372 if (fOsXGuest)
1373 *aParavirtProvider = ParavirtProvider_Minimal;
1374 else if ( mUserData->s.strOsType == "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);
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#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2273 *aValue = FALSE;
2274#endif
2275 break;
2276
2277 case HWVirtExPropertyType_Force:
2278 *aValue = mHWData->mHWVirtExForceEnabled;
2279 break;
2280
2281 case HWVirtExPropertyType_UseNativeApi:
2282 *aValue = mHWData->mHWVirtExUseNativeApi;
2283 break;
2284
2285 case HWVirtExPropertyType_VirtVmsaveVmload:
2286 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2287 break;
2288
2289 default:
2290 return E_INVALIDARG;
2291 }
2292 return S_OK;
2293}
2294
2295HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2296{
2297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2298
2299 HRESULT rc = i_checkStateDependency(MutableStateDep);
2300 if (FAILED(rc)) return rc;
2301
2302 switch (aProperty)
2303 {
2304 case HWVirtExPropertyType_Enabled:
2305 i_setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mHWVirtExEnabled = !!aValue;
2308 break;
2309
2310 case HWVirtExPropertyType_VPID:
2311 i_setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2314 break;
2315
2316 case HWVirtExPropertyType_NestedPaging:
2317 i_setModified(IsModified_MachineData);
2318 mHWData.backup();
2319 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2320 break;
2321
2322 case HWVirtExPropertyType_UnrestrictedExecution:
2323 i_setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mHWVirtExUXEnabled = !!aValue;
2326 break;
2327
2328 case HWVirtExPropertyType_LargePages:
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2332 break;
2333
2334 case HWVirtExPropertyType_Force:
2335 i_setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mHWVirtExForceEnabled = !!aValue;
2338 break;
2339
2340 case HWVirtExPropertyType_UseNativeApi:
2341 i_setModified(IsModified_MachineData);
2342 mHWData.backup();
2343 mHWData->mHWVirtExUseNativeApi = !!aValue;
2344 break;
2345
2346 case HWVirtExPropertyType_VirtVmsaveVmload:
2347 i_setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2350 break;
2351
2352 default:
2353 return E_INVALIDARG;
2354 }
2355
2356 return S_OK;
2357}
2358
2359HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2360{
2361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2364
2365 return S_OK;
2366}
2367
2368HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2369{
2370 /** @todo (r=dmik):
2371 * 1. Allow to change the name of the snapshot folder containing snapshots
2372 * 2. Rename the folder on disk instead of just changing the property
2373 * value (to be smart and not to leave garbage). Note that it cannot be
2374 * done here because the change may be rolled back. Thus, the right
2375 * place is #saveSettings().
2376 */
2377
2378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2379
2380 HRESULT rc = i_checkStateDependency(MutableStateDep);
2381 if (FAILED(rc)) return rc;
2382
2383 if (!mData->mCurrentSnapshot.isNull())
2384 return setError(E_FAIL,
2385 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2386
2387 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2388
2389 if (strSnapshotFolder.isEmpty())
2390 strSnapshotFolder = "Snapshots";
2391 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2392 if (RT_FAILURE(vrc))
2393 return setErrorBoth(E_FAIL, vrc,
2394 tr("Invalid snapshot folder '%s' (%Rrc)"),
2395 strSnapshotFolder.c_str(), vrc);
2396
2397 i_setModified(IsModified_MachineData);
2398 mUserData.backup();
2399
2400 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2401
2402 return S_OK;
2403}
2404
2405HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2406{
2407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 aMediumAttachments.resize(mMediumAttachments->size());
2410 size_t i = 0;
2411 for (MediumAttachmentList::const_iterator
2412 it = mMediumAttachments->begin();
2413 it != mMediumAttachments->end();
2414 ++it, ++i)
2415 aMediumAttachments[i] = *it;
2416
2417 return S_OK;
2418}
2419
2420HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2421{
2422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2423
2424 Assert(!!mVRDEServer);
2425
2426 aVRDEServer = mVRDEServer;
2427
2428 return S_OK;
2429}
2430
2431HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2432{
2433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2434
2435 aAudioAdapter = mAudioAdapter;
2436
2437 return S_OK;
2438}
2439
2440HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2441{
2442#ifdef VBOX_WITH_VUSB
2443 clearError();
2444 MultiResult rc(S_OK);
2445
2446# ifdef VBOX_WITH_USB
2447 rc = mParent->i_host()->i_checkUSBProxyService();
2448 if (FAILED(rc)) return rc;
2449# endif
2450
2451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2452
2453 aUSBControllers.resize(mUSBControllers->size());
2454 size_t i = 0;
2455 for (USBControllerList::const_iterator
2456 it = mUSBControllers->begin();
2457 it != mUSBControllers->end();
2458 ++it, ++i)
2459 aUSBControllers[i] = *it;
2460
2461 return S_OK;
2462#else
2463 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2464 * extended error info to indicate that USB is simply not available
2465 * (w/o treating it as a failure), for example, as in OSE */
2466 NOREF(aUSBControllers);
2467 ReturnComNotImplemented();
2468#endif /* VBOX_WITH_VUSB */
2469}
2470
2471HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2472{
2473#ifdef VBOX_WITH_VUSB
2474 clearError();
2475 MultiResult rc(S_OK);
2476
2477# ifdef VBOX_WITH_USB
2478 rc = mParent->i_host()->i_checkUSBProxyService();
2479 if (FAILED(rc)) return rc;
2480# endif
2481
2482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 aUSBDeviceFilters = mUSBDeviceFilters;
2485 return rc;
2486#else
2487 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2488 * extended error info to indicate that USB is simply not available
2489 * (w/o treating it as a failure), for example, as in OSE */
2490 NOREF(aUSBDeviceFilters);
2491 ReturnComNotImplemented();
2492#endif /* VBOX_WITH_VUSB */
2493}
2494
2495HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2496{
2497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 aSettingsFilePath = mData->m_strConfigFileFull;
2500
2501 return S_OK;
2502}
2503
2504HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2505{
2506 RT_NOREF(aSettingsFilePath);
2507 ReturnComNotImplemented();
2508}
2509
2510HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2511{
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2515 if (FAILED(rc)) return rc;
2516
2517 if (!mData->pMachineConfigFile->fileExists())
2518 // this is a new machine, and no config file exists yet:
2519 *aSettingsModified = TRUE;
2520 else
2521 *aSettingsModified = (mData->flModifications != 0);
2522
2523 return S_OK;
2524}
2525
2526HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2527{
2528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2529
2530 *aSessionState = mData->mSession.mState;
2531
2532 return S_OK;
2533}
2534
2535HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2536{
2537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 aSessionName = mData->mSession.mName;
2540
2541 return S_OK;
2542}
2543
2544HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2545{
2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 *aSessionPID = mData->mSession.mPID;
2549
2550 return S_OK;
2551}
2552
2553HRESULT Machine::getState(MachineState_T *aState)
2554{
2555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2556
2557 *aState = mData->mMachineState;
2558 Assert(mData->mMachineState != MachineState_Null);
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2564{
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2568
2569 return S_OK;
2570}
2571
2572HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2573{
2574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2575
2576 aStateFilePath = mSSData->strStateFilePath;
2577
2578 return S_OK;
2579}
2580
2581HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2582{
2583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2584
2585 i_getLogFolder(aLogFolder);
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 aCurrentSnapshot = mData->mCurrentSnapshot;
2595
2596 return S_OK;
2597}
2598
2599HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2600{
2601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2602
2603 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2604 ? 0
2605 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2611{
2612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2613
2614 /* Note: for machines with no snapshots, we always return FALSE
2615 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2616 * reasons :) */
2617
2618 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2619 ? FALSE
2620 : mData->mCurrentStateModified;
2621
2622 return S_OK;
2623}
2624
2625HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2626{
2627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 aSharedFolders.resize(mHWData->mSharedFolders.size());
2630 size_t i = 0;
2631 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2632 it = mHWData->mSharedFolders.begin();
2633 it != mHWData->mSharedFolders.end();
2634 ++it, ++i)
2635 aSharedFolders[i] = *it;
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2641{
2642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2643
2644 *aClipboardMode = mHWData->mClipboardMode;
2645
2646 return S_OK;
2647}
2648
2649HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2650{
2651 HRESULT rc = S_OK;
2652
2653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2654
2655 alock.release();
2656 rc = i_onClipboardModeChange(aClipboardMode);
2657 alock.acquire();
2658 if (FAILED(rc)) return rc;
2659
2660 i_setModified(IsModified_MachineData);
2661 mHWData.backup();
2662 mHWData->mClipboardMode = aClipboardMode;
2663
2664 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2665 if (Global::IsOnline(mData->mMachineState))
2666 i_saveSettings(NULL);
2667
2668 return S_OK;
2669}
2670
2671HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2672{
2673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2674
2675 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2676
2677 return S_OK;
2678}
2679
2680HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2681{
2682 HRESULT rc = S_OK;
2683
2684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 alock.release();
2687 rc = i_onClipboardFileTransferModeChange(aEnabled);
2688 alock.acquire();
2689 if (FAILED(rc)) return rc;
2690
2691 i_setModified(IsModified_MachineData);
2692 mHWData.backup();
2693 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2694
2695 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2696 if (Global::IsOnline(mData->mMachineState))
2697 i_saveSettings(NULL);
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aDnDMode = mHWData->mDnDMode;
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2712{
2713 HRESULT rc = S_OK;
2714
2715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 alock.release();
2718 rc = i_onDnDModeChange(aDnDMode);
2719
2720 alock.acquire();
2721 if (FAILED(rc)) return rc;
2722
2723 i_setModified(IsModified_MachineData);
2724 mHWData.backup();
2725 mHWData->mDnDMode = aDnDMode;
2726
2727 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2728 if (Global::IsOnline(mData->mMachineState))
2729 i_saveSettings(NULL);
2730
2731 return S_OK;
2732}
2733
2734HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2735{
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 aStorageControllers.resize(mStorageControllers->size());
2739 size_t i = 0;
2740 for (StorageControllerList::const_iterator
2741 it = mStorageControllers->begin();
2742 it != mStorageControllers->end();
2743 ++it, ++i)
2744 aStorageControllers[i] = *it;
2745
2746 return S_OK;
2747}
2748
2749HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2750{
2751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2752
2753 *aEnabled = mUserData->s.fTeleporterEnabled;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2759{
2760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 /* Only allow it to be set to true when PoweredOff or Aborted.
2763 (Clearing it is always permitted.) */
2764 if ( aTeleporterEnabled
2765 && mData->mRegistered
2766 && ( !i_isSessionMachine()
2767 || ( mData->mMachineState != MachineState_PoweredOff
2768 && mData->mMachineState != MachineState_Teleported
2769 && mData->mMachineState != MachineState_Aborted
2770 )
2771 )
2772 )
2773 return setError(VBOX_E_INVALID_VM_STATE,
2774 tr("The machine is not powered off (state is %s)"),
2775 Global::stringifyMachineState(mData->mMachineState));
2776
2777 i_setModified(IsModified_MachineData);
2778 mUserData.backup();
2779 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2794{
2795 if (aTeleporterPort >= _64K)
2796 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2797
2798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2801 if (FAILED(rc)) return rc;
2802
2803 i_setModified(IsModified_MachineData);
2804 mUserData.backup();
2805 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2806
2807 return S_OK;
2808}
2809
2810HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2820{
2821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2824 if (FAILED(rc)) return rc;
2825
2826 i_setModified(IsModified_MachineData);
2827 mUserData.backup();
2828 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2829
2830 return S_OK;
2831}
2832
2833HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2834{
2835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2836 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2837
2838 return S_OK;
2839}
2840
2841HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2842{
2843 /*
2844 * Hash the password first.
2845 */
2846 com::Utf8Str aT = aTeleporterPassword;
2847
2848 if (!aT.isEmpty())
2849 {
2850 if (VBoxIsPasswordHashed(&aT))
2851 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2852 VBoxHashPassword(&aT);
2853 }
2854
2855 /*
2856 * Do the update.
2857 */
2858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2859 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2860 if (SUCCEEDED(hrc))
2861 {
2862 i_setModified(IsModified_MachineData);
2863 mUserData.backup();
2864 mUserData->s.strTeleporterPassword = aT;
2865 }
2866
2867 return hrc;
2868}
2869
2870HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2871{
2872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2873
2874 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2880{
2881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 /* Only allow it to be set to true when PoweredOff or Aborted.
2884 (Clearing it is always permitted.) */
2885 if ( aRTCUseUTC
2886 && mData->mRegistered
2887 && ( !i_isSessionMachine()
2888 || ( mData->mMachineState != MachineState_PoweredOff
2889 && mData->mMachineState != MachineState_Teleported
2890 && mData->mMachineState != MachineState_Aborted
2891 )
2892 )
2893 )
2894 return setError(VBOX_E_INVALID_VM_STATE,
2895 tr("The machine is not powered off (state is %s)"),
2896 Global::stringifyMachineState(mData->mMachineState));
2897
2898 i_setModified(IsModified_MachineData);
2899 mUserData.backup();
2900 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2901
2902 return S_OK;
2903}
2904
2905HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2906{
2907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2915{
2916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 HRESULT rc = i_checkStateDependency(MutableStateDep);
2919 if (FAILED(rc)) return rc;
2920
2921 i_setModified(IsModified_MachineData);
2922 mHWData.backup();
2923 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2924
2925 return S_OK;
2926}
2927
2928HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2929{
2930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2931
2932 *aIOCacheSize = mHWData->mIOCacheSize;
2933
2934 return S_OK;
2935}
2936
2937HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2938{
2939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 HRESULT rc = i_checkStateDependency(MutableStateDep);
2942 if (FAILED(rc)) return rc;
2943
2944 i_setModified(IsModified_MachineData);
2945 mHWData.backup();
2946 mHWData->mIOCacheSize = aIOCacheSize;
2947
2948 return S_OK;
2949}
2950
2951
2952/**
2953 * @note Locks objects!
2954 */
2955HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2956 LockType_T aLockType)
2957{
2958 /* check the session state */
2959 SessionState_T state;
2960 HRESULT rc = aSession->COMGETTER(State)(&state);
2961 if (FAILED(rc)) return rc;
2962
2963 if (state != SessionState_Unlocked)
2964 return setError(VBOX_E_INVALID_OBJECT_STATE,
2965 tr("The given session is busy"));
2966
2967 // get the client's IInternalSessionControl interface
2968 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2969 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2970 E_INVALIDARG);
2971
2972 // session name (only used in some code paths)
2973 Utf8Str strSessionName;
2974
2975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 if (!mData->mRegistered)
2978 return setError(E_UNEXPECTED,
2979 tr("The machine '%s' is not registered"),
2980 mUserData->s.strName.c_str());
2981
2982 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2983
2984 SessionState_T oldState = mData->mSession.mState;
2985 /* Hack: in case the session is closing and there is a progress object
2986 * which allows waiting for the session to be closed, take the opportunity
2987 * and do a limited wait (max. 1 second). This helps a lot when the system
2988 * is busy and thus session closing can take a little while. */
2989 if ( mData->mSession.mState == SessionState_Unlocking
2990 && mData->mSession.mProgress)
2991 {
2992 alock.release();
2993 mData->mSession.mProgress->WaitForCompletion(1000);
2994 alock.acquire();
2995 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2996 }
2997
2998 // try again now
2999 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3000 // (i.e. session machine exists)
3001 && (aLockType == LockType_Shared) // caller wants a shared link to the
3002 // existing session that holds the write lock:
3003 )
3004 {
3005 // OK, share the session... we are now dealing with three processes:
3006 // 1) VBoxSVC (where this code runs);
3007 // 2) process C: the caller's client process (who wants a shared session);
3008 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3009
3010 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3011 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3012 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3013 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3014 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3015
3016 /*
3017 * Release the lock before calling the client process. It's safe here
3018 * since the only thing to do after we get the lock again is to add
3019 * the remote control to the list (which doesn't directly influence
3020 * anything).
3021 */
3022 alock.release();
3023
3024 // get the console of the session holding the write lock (this is a remote call)
3025 ComPtr<IConsole> pConsoleW;
3026 if (mData->mSession.mLockType == LockType_VM)
3027 {
3028 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3029 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3030 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3031 if (FAILED(rc))
3032 // the failure may occur w/o any error info (from RPC), so provide one
3033 return setError(VBOX_E_VM_ERROR,
3034 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3035 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3036 }
3037
3038 // share the session machine and W's console with the caller's session
3039 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3040 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3041 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3042
3043 if (FAILED(rc))
3044 // the failure may occur w/o any error info (from RPC), so provide one
3045 return setError(VBOX_E_VM_ERROR,
3046 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3047 alock.acquire();
3048
3049 // need to revalidate the state after acquiring the lock again
3050 if (mData->mSession.mState != SessionState_Locked)
3051 {
3052 pSessionControl->Uninitialize();
3053 return setError(VBOX_E_INVALID_SESSION_STATE,
3054 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3055 mUserData->s.strName.c_str());
3056 }
3057
3058 // add the caller's session to the list
3059 mData->mSession.mRemoteControls.push_back(pSessionControl);
3060 }
3061 else if ( mData->mSession.mState == SessionState_Locked
3062 || mData->mSession.mState == SessionState_Unlocking
3063 )
3064 {
3065 // sharing not permitted, or machine still unlocking:
3066 return setError(VBOX_E_INVALID_OBJECT_STATE,
3067 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3068 mUserData->s.strName.c_str());
3069 }
3070 else
3071 {
3072 // machine is not locked: then write-lock the machine (create the session machine)
3073
3074 // must not be busy
3075 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3076
3077 // get the caller's session PID
3078 RTPROCESS pid = NIL_RTPROCESS;
3079 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3080 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3081 Assert(pid != NIL_RTPROCESS);
3082
3083 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3084
3085 if (fLaunchingVMProcess)
3086 {
3087 if (mData->mSession.mPID == NIL_RTPROCESS)
3088 {
3089 // two or more clients racing for a lock, the one which set the
3090 // session state to Spawning will win, the others will get an
3091 // error as we can't decide here if waiting a little would help
3092 // (only for shared locks this would avoid an error)
3093 return setError(VBOX_E_INVALID_OBJECT_STATE,
3094 tr("The machine '%s' already has a lock request pending"),
3095 mUserData->s.strName.c_str());
3096 }
3097
3098 // this machine is awaiting for a spawning session to be opened:
3099 // then the calling process must be the one that got started by
3100 // LaunchVMProcess()
3101
3102 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3103 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3104
3105#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3106 /* Hardened windows builds spawns three processes when a VM is
3107 launched, the 3rd one is the one that will end up here. */
3108 RTPROCESS pidParent;
3109 int vrc = RTProcQueryParent(pid, &pidParent);
3110 if (RT_SUCCESS(vrc))
3111 vrc = RTProcQueryParent(pidParent, &pidParent);
3112 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3113 || vrc == VERR_ACCESS_DENIED)
3114 {
3115 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3116 mData->mSession.mPID = pid;
3117 }
3118#endif
3119
3120 if (mData->mSession.mPID != pid)
3121 return setError(E_ACCESSDENIED,
3122 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3123 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3124 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3125 }
3126
3127 // create the mutable SessionMachine from the current machine
3128 ComObjPtr<SessionMachine> sessionMachine;
3129 sessionMachine.createObject();
3130 rc = sessionMachine->init(this);
3131 AssertComRC(rc);
3132
3133 /* NOTE: doing return from this function after this point but
3134 * before the end is forbidden since it may call SessionMachine::uninit()
3135 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3136 * lock while still holding the Machine lock in alock so that a deadlock
3137 * is possible due to the wrong lock order. */
3138
3139 if (SUCCEEDED(rc))
3140 {
3141 /*
3142 * Set the session state to Spawning to protect against subsequent
3143 * attempts to open a session and to unregister the machine after
3144 * we release the lock.
3145 */
3146 SessionState_T origState = mData->mSession.mState;
3147 mData->mSession.mState = SessionState_Spawning;
3148
3149#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3150 /* Get the client token ID to be passed to the client process */
3151 Utf8Str strTokenId;
3152 sessionMachine->i_getTokenId(strTokenId);
3153 Assert(!strTokenId.isEmpty());
3154#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3155 /* Get the client token to be passed to the client process */
3156 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3157 /* The token is now "owned" by pToken, fix refcount */
3158 if (!pToken.isNull())
3159 pToken->Release();
3160#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3161
3162 /*
3163 * Release the lock before calling the client process -- it will call
3164 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3165 * because the state is Spawning, so that LaunchVMProcess() and
3166 * LockMachine() calls will fail. This method, called before we
3167 * acquire the lock again, will fail because of the wrong PID.
3168 *
3169 * Note that mData->mSession.mRemoteControls accessed outside
3170 * the lock may not be modified when state is Spawning, so it's safe.
3171 */
3172 alock.release();
3173
3174 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3175#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3176 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3177#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3178 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3179 /* Now the token is owned by the client process. */
3180 pToken.setNull();
3181#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3182 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3183
3184 /* The failure may occur w/o any error info (from RPC), so provide one */
3185 if (FAILED(rc))
3186 setError(VBOX_E_VM_ERROR,
3187 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3188
3189 // get session name, either to remember or to compare against
3190 // the already known session name.
3191 {
3192 Bstr bstrSessionName;
3193 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3194 if (SUCCEEDED(rc2))
3195 strSessionName = bstrSessionName;
3196 }
3197
3198 if ( SUCCEEDED(rc)
3199 && fLaunchingVMProcess
3200 )
3201 {
3202 /* complete the remote session initialization */
3203
3204 /* get the console from the direct session */
3205 ComPtr<IConsole> console;
3206 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3207 ComAssertComRC(rc);
3208
3209 if (SUCCEEDED(rc) && !console)
3210 {
3211 ComAssert(!!console);
3212 rc = E_FAIL;
3213 }
3214
3215 /* assign machine & console to the remote session */
3216 if (SUCCEEDED(rc))
3217 {
3218 /*
3219 * after LaunchVMProcess(), the first and the only
3220 * entry in remoteControls is that remote session
3221 */
3222 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3223 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3224 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3225
3226 /* The failure may occur w/o any error info (from RPC), so provide one */
3227 if (FAILED(rc))
3228 setError(VBOX_E_VM_ERROR,
3229 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3230 }
3231
3232 if (FAILED(rc))
3233 pSessionControl->Uninitialize();
3234 }
3235
3236 /* acquire the lock again */
3237 alock.acquire();
3238
3239 /* Restore the session state */
3240 mData->mSession.mState = origState;
3241 }
3242
3243 // finalize spawning anyway (this is why we don't return on errors above)
3244 if (fLaunchingVMProcess)
3245 {
3246 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3247 /* Note that the progress object is finalized later */
3248 /** @todo Consider checking mData->mSession.mProgress for cancellation
3249 * around here. */
3250
3251 /* We don't reset mSession.mPID here because it is necessary for
3252 * SessionMachine::uninit() to reap the child process later. */
3253
3254 if (FAILED(rc))
3255 {
3256 /* Close the remote session, remove the remote control from the list
3257 * and reset session state to Closed (@note keep the code in sync
3258 * with the relevant part in checkForSpawnFailure()). */
3259
3260 Assert(mData->mSession.mRemoteControls.size() == 1);
3261 if (mData->mSession.mRemoteControls.size() == 1)
3262 {
3263 ErrorInfoKeeper eik;
3264 mData->mSession.mRemoteControls.front()->Uninitialize();
3265 }
3266
3267 mData->mSession.mRemoteControls.clear();
3268 mData->mSession.mState = SessionState_Unlocked;
3269 }
3270 }
3271 else
3272 {
3273 /* memorize PID of the directly opened session */
3274 if (SUCCEEDED(rc))
3275 mData->mSession.mPID = pid;
3276 }
3277
3278 if (SUCCEEDED(rc))
3279 {
3280 mData->mSession.mLockType = aLockType;
3281 /* memorize the direct session control and cache IUnknown for it */
3282 mData->mSession.mDirectControl = pSessionControl;
3283 mData->mSession.mState = SessionState_Locked;
3284 if (!fLaunchingVMProcess)
3285 mData->mSession.mName = strSessionName;
3286 /* associate the SessionMachine with this Machine */
3287 mData->mSession.mMachine = sessionMachine;
3288
3289 /* request an IUnknown pointer early from the remote party for later
3290 * identity checks (it will be internally cached within mDirectControl
3291 * at least on XPCOM) */
3292 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3293 NOREF(unk);
3294 }
3295
3296 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3297 * would break the lock order */
3298 alock.release();
3299
3300 /* uninitialize the created session machine on failure */
3301 if (FAILED(rc))
3302 sessionMachine->uninit();
3303 }
3304
3305 if (SUCCEEDED(rc))
3306 {
3307 /*
3308 * tell the client watcher thread to update the set of
3309 * machines that have open sessions
3310 */
3311 mParent->i_updateClientWatcher();
3312
3313 if (oldState != SessionState_Locked)
3314 /* fire an event */
3315 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3316 }
3317
3318 return rc;
3319}
3320
3321/**
3322 * @note Locks objects!
3323 */
3324HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3325 const com::Utf8Str &aName,
3326 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3327 ComPtr<IProgress> &aProgress)
3328{
3329 Utf8Str strFrontend(aName);
3330 /* "emergencystop" doesn't need the session, so skip the checks/interface
3331 * retrieval. This code doesn't quite fit in here, but introducing a
3332 * special API method would be even more effort, and would require explicit
3333 * support by every API client. It's better to hide the feature a bit. */
3334 if (strFrontend != "emergencystop")
3335 CheckComArgNotNull(aSession);
3336
3337 HRESULT rc = S_OK;
3338 if (strFrontend.isEmpty())
3339 {
3340 Bstr bstrFrontend;
3341 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3342 if (FAILED(rc))
3343 return rc;
3344 strFrontend = bstrFrontend;
3345 if (strFrontend.isEmpty())
3346 {
3347 ComPtr<ISystemProperties> systemProperties;
3348 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3349 if (FAILED(rc))
3350 return rc;
3351 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3352 if (FAILED(rc))
3353 return rc;
3354 strFrontend = bstrFrontend;
3355 }
3356 /* paranoia - emergencystop is not a valid default */
3357 if (strFrontend == "emergencystop")
3358 strFrontend = Utf8Str::Empty;
3359 }
3360 /* default frontend: Qt GUI */
3361 if (strFrontend.isEmpty())
3362 strFrontend = "GUI/Qt";
3363
3364 if (strFrontend != "emergencystop")
3365 {
3366 /* check the session state */
3367 SessionState_T state;
3368 rc = aSession->COMGETTER(State)(&state);
3369 if (FAILED(rc))
3370 return rc;
3371
3372 if (state != SessionState_Unlocked)
3373 return setError(VBOX_E_INVALID_OBJECT_STATE,
3374 tr("The given session is busy"));
3375
3376 /* get the IInternalSessionControl interface */
3377 ComPtr<IInternalSessionControl> control(aSession);
3378 ComAssertMsgRet(!control.isNull(),
3379 ("No IInternalSessionControl interface"),
3380 E_INVALIDARG);
3381
3382 /* get the teleporter enable state for the progress object init. */
3383 BOOL fTeleporterEnabled;
3384 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3385 if (FAILED(rc))
3386 return rc;
3387
3388 /* create a progress object */
3389 ComObjPtr<ProgressProxy> progress;
3390 progress.createObject();
3391 rc = progress->init(mParent,
3392 static_cast<IMachine*>(this),
3393 Bstr(tr("Starting VM")).raw(),
3394 TRUE /* aCancelable */,
3395 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3396 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3397 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3398 2 /* uFirstOperationWeight */,
3399 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3400
3401 if (SUCCEEDED(rc))
3402 {
3403 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3404 if (SUCCEEDED(rc))
3405 {
3406 aProgress = progress;
3407
3408 /* signal the client watcher thread */
3409 mParent->i_updateClientWatcher();
3410
3411 /* fire an event */
3412 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3413 }
3414 }
3415 }
3416 else
3417 {
3418 /* no progress object - either instant success or failure */
3419 aProgress = NULL;
3420
3421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3422
3423 if (mData->mSession.mState != SessionState_Locked)
3424 return setError(VBOX_E_INVALID_OBJECT_STATE,
3425 tr("The machine '%s' is not locked by a session"),
3426 mUserData->s.strName.c_str());
3427
3428 /* must have a VM process associated - do not kill normal API clients
3429 * with an open session */
3430 if (!Global::IsOnline(mData->mMachineState))
3431 return setError(VBOX_E_INVALID_OBJECT_STATE,
3432 tr("The machine '%s' does not have a VM process"),
3433 mUserData->s.strName.c_str());
3434
3435 /* forcibly terminate the VM process */
3436 if (mData->mSession.mPID != NIL_RTPROCESS)
3437 RTProcTerminate(mData->mSession.mPID);
3438
3439 /* signal the client watcher thread, as most likely the client has
3440 * been terminated */
3441 mParent->i_updateClientWatcher();
3442 }
3443
3444 return rc;
3445}
3446
3447HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3448{
3449 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3450 return setError(E_INVALIDARG,
3451 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3452 aPosition, SchemaDefs::MaxBootPosition);
3453
3454 if (aDevice == DeviceType_USB)
3455 return setError(E_NOTIMPL,
3456 tr("Booting from USB device is currently not supported"));
3457
3458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3459
3460 HRESULT rc = i_checkStateDependency(MutableStateDep);
3461 if (FAILED(rc)) return rc;
3462
3463 i_setModified(IsModified_MachineData);
3464 mHWData.backup();
3465 mHWData->mBootOrder[aPosition - 1] = aDevice;
3466
3467 return S_OK;
3468}
3469
3470HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3471{
3472 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3473 return setError(E_INVALIDARG,
3474 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3475 aPosition, SchemaDefs::MaxBootPosition);
3476
3477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3478
3479 *aDevice = mHWData->mBootOrder[aPosition - 1];
3480
3481 return S_OK;
3482}
3483
3484HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3485 LONG aControllerPort,
3486 LONG aDevice,
3487 DeviceType_T aType,
3488 const ComPtr<IMedium> &aMedium)
3489{
3490 IMedium *aM = aMedium;
3491 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3492 aName.c_str(), aControllerPort, aDevice, aType, aM));
3493
3494 // request the host lock first, since might be calling Host methods for getting host drives;
3495 // next, protect the media tree all the while we're in here, as well as our member variables
3496 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3497 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3498
3499 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3500 if (FAILED(rc)) return rc;
3501
3502 /// @todo NEWMEDIA implicit machine registration
3503 if (!mData->mRegistered)
3504 return setError(VBOX_E_INVALID_OBJECT_STATE,
3505 tr("Cannot attach storage devices to an unregistered machine"));
3506
3507 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3508
3509 /* Check for an existing controller. */
3510 ComObjPtr<StorageController> ctl;
3511 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3512 if (FAILED(rc)) return rc;
3513
3514 StorageControllerType_T ctrlType;
3515 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3516 if (FAILED(rc))
3517 return setError(E_FAIL,
3518 tr("Could not get type of controller '%s'"),
3519 aName.c_str());
3520
3521 bool fSilent = false;
3522 Utf8Str strReconfig;
3523
3524 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3525 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3526 if ( mData->mMachineState == MachineState_Paused
3527 && strReconfig == "1")
3528 fSilent = true;
3529
3530 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3531 bool fHotplug = false;
3532 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3533 fHotplug = true;
3534
3535 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3536 return setError(VBOX_E_INVALID_VM_STATE,
3537 tr("Controller '%s' does not support hotplugging"),
3538 aName.c_str());
3539
3540 // check that the port and device are not out of range
3541 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3542 if (FAILED(rc)) return rc;
3543
3544 /* check if the device slot is already busy */
3545 MediumAttachment *pAttachTemp;
3546 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3547 aName,
3548 aControllerPort,
3549 aDevice)))
3550 {
3551 Medium *pMedium = pAttachTemp->i_getMedium();
3552 if (pMedium)
3553 {
3554 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3555 return setError(VBOX_E_OBJECT_IN_USE,
3556 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3557 pMedium->i_getLocationFull().c_str(),
3558 aControllerPort,
3559 aDevice,
3560 aName.c_str());
3561 }
3562 else
3563 return setError(VBOX_E_OBJECT_IN_USE,
3564 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3565 aControllerPort, aDevice, aName.c_str());
3566 }
3567
3568 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3569 if (aMedium && medium.isNull())
3570 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3571
3572 AutoCaller mediumCaller(medium);
3573 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3574
3575 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3576
3577 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3578 && !medium.isNull()
3579 && ( medium->i_getType() != MediumType_Readonly
3580 || medium->i_getDeviceType() != DeviceType_DVD)
3581 )
3582 return setError(VBOX_E_OBJECT_IN_USE,
3583 tr("Medium '%s' is already attached to this virtual machine"),
3584 medium->i_getLocationFull().c_str());
3585
3586 if (!medium.isNull())
3587 {
3588 MediumType_T mtype = medium->i_getType();
3589 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3590 // For DVDs it's not written to the config file, so needs no global config
3591 // version bump. For floppies it's a new attribute "type", which is ignored
3592 // by older VirtualBox version, so needs no global config version bump either.
3593 // For hard disks this type is not accepted.
3594 if (mtype == MediumType_MultiAttach)
3595 {
3596 // This type is new with VirtualBox 4.0 and therefore requires settings
3597 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3598 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3599 // two reasons: The medium type is a property of the media registry tree, which
3600 // can reside in the global config file (for pre-4.0 media); we would therefore
3601 // possibly need to bump the global config version. We don't want to do that though
3602 // because that might make downgrading to pre-4.0 impossible.
3603 // As a result, we can only use these two new types if the medium is NOT in the
3604 // global registry:
3605 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3606 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3607 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3608 )
3609 return setError(VBOX_E_INVALID_OBJECT_STATE,
3610 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3611 "to machines that were created with VirtualBox 4.0 or later"),
3612 medium->i_getLocationFull().c_str());
3613 }
3614 }
3615
3616 bool fIndirect = false;
3617 if (!medium.isNull())
3618 fIndirect = medium->i_isReadOnly();
3619 bool associate = true;
3620
3621 do
3622 {
3623 if ( aType == DeviceType_HardDisk
3624 && mMediumAttachments.isBackedUp())
3625 {
3626 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3627
3628 /* check if the medium was attached to the VM before we started
3629 * changing attachments in which case the attachment just needs to
3630 * be restored */
3631 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3632 {
3633 AssertReturn(!fIndirect, E_FAIL);
3634
3635 /* see if it's the same bus/channel/device */
3636 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3637 {
3638 /* the simplest case: restore the whole attachment
3639 * and return, nothing else to do */
3640 mMediumAttachments->push_back(pAttachTemp);
3641
3642 /* Reattach the medium to the VM. */
3643 if (fHotplug || fSilent)
3644 {
3645 mediumLock.release();
3646 treeLock.release();
3647 alock.release();
3648
3649 MediumLockList *pMediumLockList(new MediumLockList());
3650
3651 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3652 medium /* pToLockWrite */,
3653 false /* fMediumLockWriteAll */,
3654 NULL,
3655 *pMediumLockList);
3656 alock.acquire();
3657 if (FAILED(rc))
3658 delete pMediumLockList;
3659 else
3660 {
3661 mData->mSession.mLockedMedia.Unlock();
3662 alock.release();
3663 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3664 mData->mSession.mLockedMedia.Lock();
3665 alock.acquire();
3666 }
3667 alock.release();
3668
3669 if (SUCCEEDED(rc))
3670 {
3671 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3672 /* Remove lock list in case of error. */
3673 if (FAILED(rc))
3674 {
3675 mData->mSession.mLockedMedia.Unlock();
3676 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3677 mData->mSession.mLockedMedia.Lock();
3678 }
3679 }
3680 }
3681
3682 return S_OK;
3683 }
3684
3685 /* bus/channel/device differ; we need a new attachment object,
3686 * but don't try to associate it again */
3687 associate = false;
3688 break;
3689 }
3690 }
3691
3692 /* go further only if the attachment is to be indirect */
3693 if (!fIndirect)
3694 break;
3695
3696 /* perform the so called smart attachment logic for indirect
3697 * attachments. Note that smart attachment is only applicable to base
3698 * hard disks. */
3699
3700 if (medium->i_getParent().isNull())
3701 {
3702 /* first, investigate the backup copy of the current hard disk
3703 * attachments to make it possible to re-attach existing diffs to
3704 * another device slot w/o losing their contents */
3705 if (mMediumAttachments.isBackedUp())
3706 {
3707 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3708
3709 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3710 uint32_t foundLevel = 0;
3711
3712 for (MediumAttachmentList::const_iterator
3713 it = oldAtts.begin();
3714 it != oldAtts.end();
3715 ++it)
3716 {
3717 uint32_t level = 0;
3718 MediumAttachment *pAttach = *it;
3719 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3720 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3721 if (pMedium.isNull())
3722 continue;
3723
3724 if (pMedium->i_getBase(&level) == medium)
3725 {
3726 /* skip the hard disk if its currently attached (we
3727 * cannot attach the same hard disk twice) */
3728 if (i_findAttachment(*mMediumAttachments.data(),
3729 pMedium))
3730 continue;
3731
3732 /* matched device, channel and bus (i.e. attached to the
3733 * same place) will win and immediately stop the search;
3734 * otherwise the attachment that has the youngest
3735 * descendant of medium will be used
3736 */
3737 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3738 {
3739 /* the simplest case: restore the whole attachment
3740 * and return, nothing else to do */
3741 mMediumAttachments->push_back(*it);
3742
3743 /* Reattach the medium to the VM. */
3744 if (fHotplug || fSilent)
3745 {
3746 mediumLock.release();
3747 treeLock.release();
3748 alock.release();
3749
3750 MediumLockList *pMediumLockList(new MediumLockList());
3751
3752 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3753 medium /* pToLockWrite */,
3754 false /* fMediumLockWriteAll */,
3755 NULL,
3756 *pMediumLockList);
3757 alock.acquire();
3758 if (FAILED(rc))
3759 delete pMediumLockList;
3760 else
3761 {
3762 mData->mSession.mLockedMedia.Unlock();
3763 alock.release();
3764 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3765 mData->mSession.mLockedMedia.Lock();
3766 alock.acquire();
3767 }
3768 alock.release();
3769
3770 if (SUCCEEDED(rc))
3771 {
3772 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3773 /* Remove lock list in case of error. */
3774 if (FAILED(rc))
3775 {
3776 mData->mSession.mLockedMedia.Unlock();
3777 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3778 mData->mSession.mLockedMedia.Lock();
3779 }
3780 }
3781 }
3782
3783 return S_OK;
3784 }
3785 else if ( foundIt == oldAtts.end()
3786 || level > foundLevel /* prefer younger */
3787 )
3788 {
3789 foundIt = it;
3790 foundLevel = level;
3791 }
3792 }
3793 }
3794
3795 if (foundIt != oldAtts.end())
3796 {
3797 /* use the previously attached hard disk */
3798 medium = (*foundIt)->i_getMedium();
3799 mediumCaller.attach(medium);
3800 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3801 mediumLock.attach(medium);
3802 /* not implicit, doesn't require association with this VM */
3803 fIndirect = false;
3804 associate = false;
3805 /* go right to the MediumAttachment creation */
3806 break;
3807 }
3808 }
3809
3810 /* must give up the medium lock and medium tree lock as below we
3811 * go over snapshots, which needs a lock with higher lock order. */
3812 mediumLock.release();
3813 treeLock.release();
3814
3815 /* then, search through snapshots for the best diff in the given
3816 * hard disk's chain to base the new diff on */
3817
3818 ComObjPtr<Medium> base;
3819 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3820 while (snap)
3821 {
3822 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3823
3824 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3825
3826 MediumAttachment *pAttachFound = NULL;
3827 uint32_t foundLevel = 0;
3828
3829 for (MediumAttachmentList::const_iterator
3830 it = snapAtts.begin();
3831 it != snapAtts.end();
3832 ++it)
3833 {
3834 MediumAttachment *pAttach = *it;
3835 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3836 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3837 if (pMedium.isNull())
3838 continue;
3839
3840 uint32_t level = 0;
3841 if (pMedium->i_getBase(&level) == medium)
3842 {
3843 /* matched device, channel and bus (i.e. attached to the
3844 * same place) will win and immediately stop the search;
3845 * otherwise the attachment that has the youngest
3846 * descendant of medium will be used
3847 */
3848 if ( pAttach->i_getDevice() == aDevice
3849 && pAttach->i_getPort() == aControllerPort
3850 && pAttach->i_getControllerName() == aName
3851 )
3852 {
3853 pAttachFound = pAttach;
3854 break;
3855 }
3856 else if ( !pAttachFound
3857 || level > foundLevel /* prefer younger */
3858 )
3859 {
3860 pAttachFound = pAttach;
3861 foundLevel = level;
3862 }
3863 }
3864 }
3865
3866 if (pAttachFound)
3867 {
3868 base = pAttachFound->i_getMedium();
3869 break;
3870 }
3871
3872 snap = snap->i_getParent();
3873 }
3874
3875 /* re-lock medium tree and the medium, as we need it below */
3876 treeLock.acquire();
3877 mediumLock.acquire();
3878
3879 /* found a suitable diff, use it as a base */
3880 if (!base.isNull())
3881 {
3882 medium = base;
3883 mediumCaller.attach(medium);
3884 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3885 mediumLock.attach(medium);
3886 }
3887 }
3888
3889 Utf8Str strFullSnapshotFolder;
3890 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3891
3892 ComObjPtr<Medium> diff;
3893 diff.createObject();
3894 // store this diff in the same registry as the parent
3895 Guid uuidRegistryParent;
3896 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3897 {
3898 // parent image has no registry: this can happen if we're attaching a new immutable
3899 // image that has not yet been attached (medium then points to the base and we're
3900 // creating the diff image for the immutable, and the parent is not yet registered);
3901 // put the parent in the machine registry then
3902 mediumLock.release();
3903 treeLock.release();
3904 alock.release();
3905 i_addMediumToRegistry(medium);
3906 alock.acquire();
3907 treeLock.acquire();
3908 mediumLock.acquire();
3909 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3910 }
3911 rc = diff->init(mParent,
3912 medium->i_getPreferredDiffFormat(),
3913 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3914 uuidRegistryParent,
3915 DeviceType_HardDisk);
3916 if (FAILED(rc)) return rc;
3917
3918 /* Apply the normal locking logic to the entire chain. */
3919 MediumLockList *pMediumLockList(new MediumLockList());
3920 mediumLock.release();
3921 treeLock.release();
3922 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3923 diff /* pToLockWrite */,
3924 false /* fMediumLockWriteAll */,
3925 medium,
3926 *pMediumLockList);
3927 treeLock.acquire();
3928 mediumLock.acquire();
3929 if (SUCCEEDED(rc))
3930 {
3931 mediumLock.release();
3932 treeLock.release();
3933 rc = pMediumLockList->Lock();
3934 treeLock.acquire();
3935 mediumLock.acquire();
3936 if (FAILED(rc))
3937 setError(rc,
3938 tr("Could not lock medium when creating diff '%s'"),
3939 diff->i_getLocationFull().c_str());
3940 else
3941 {
3942 /* will release the lock before the potentially lengthy
3943 * operation, so protect with the special state */
3944 MachineState_T oldState = mData->mMachineState;
3945 i_setMachineState(MachineState_SettingUp);
3946
3947 mediumLock.release();
3948 treeLock.release();
3949 alock.release();
3950
3951 rc = medium->i_createDiffStorage(diff,
3952 medium->i_getPreferredDiffVariant(),
3953 pMediumLockList,
3954 NULL /* aProgress */,
3955 true /* aWait */,
3956 false /* aNotify */);
3957
3958 alock.acquire();
3959 treeLock.acquire();
3960 mediumLock.acquire();
3961
3962 i_setMachineState(oldState);
3963 }
3964 }
3965
3966 /* Unlock the media and free the associated memory. */
3967 delete pMediumLockList;
3968
3969 if (FAILED(rc)) return rc;
3970
3971 /* use the created diff for the actual attachment */
3972 medium = diff;
3973 mediumCaller.attach(medium);
3974 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3975 mediumLock.attach(medium);
3976 }
3977 while (0);
3978
3979 ComObjPtr<MediumAttachment> attachment;
3980 attachment.createObject();
3981 rc = attachment->init(this,
3982 medium,
3983 aName,
3984 aControllerPort,
3985 aDevice,
3986 aType,
3987 fIndirect,
3988 false /* fPassthrough */,
3989 false /* fTempEject */,
3990 false /* fNonRotational */,
3991 false /* fDiscard */,
3992 fHotplug /* fHotPluggable */,
3993 Utf8Str::Empty);
3994 if (FAILED(rc)) return rc;
3995
3996 if (associate && !medium.isNull())
3997 {
3998 // as the last step, associate the medium to the VM
3999 rc = medium->i_addBackReference(mData->mUuid);
4000 // here we can fail because of Deleting, or being in process of creating a Diff
4001 if (FAILED(rc)) return rc;
4002
4003 mediumLock.release();
4004 treeLock.release();
4005 alock.release();
4006 i_addMediumToRegistry(medium);
4007 alock.acquire();
4008 treeLock.acquire();
4009 mediumLock.acquire();
4010 }
4011
4012 /* success: finally remember the attachment */
4013 i_setModified(IsModified_Storage);
4014 mMediumAttachments.backup();
4015 mMediumAttachments->push_back(attachment);
4016
4017 mediumLock.release();
4018 treeLock.release();
4019 alock.release();
4020
4021 if (fHotplug || fSilent)
4022 {
4023 if (!medium.isNull())
4024 {
4025 MediumLockList *pMediumLockList(new MediumLockList());
4026
4027 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4028 medium /* pToLockWrite */,
4029 false /* fMediumLockWriteAll */,
4030 NULL,
4031 *pMediumLockList);
4032 alock.acquire();
4033 if (FAILED(rc))
4034 delete pMediumLockList;
4035 else
4036 {
4037 mData->mSession.mLockedMedia.Unlock();
4038 alock.release();
4039 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4040 mData->mSession.mLockedMedia.Lock();
4041 alock.acquire();
4042 }
4043 alock.release();
4044 }
4045
4046 if (SUCCEEDED(rc))
4047 {
4048 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4049 /* Remove lock list in case of error. */
4050 if (FAILED(rc))
4051 {
4052 mData->mSession.mLockedMedia.Unlock();
4053 mData->mSession.mLockedMedia.Remove(attachment);
4054 mData->mSession.mLockedMedia.Lock();
4055 }
4056 }
4057 }
4058
4059 /* Save modified registries, but skip this machine as it's the caller's
4060 * job to save its settings like all other settings changes. */
4061 mParent->i_unmarkRegistryModified(i_getId());
4062 mParent->i_saveModifiedRegistries();
4063
4064 if (SUCCEEDED(rc))
4065 {
4066 if (fIndirect && medium != aM)
4067 mParent->i_onMediumConfigChanged(medium);
4068 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4069 }
4070
4071 return rc;
4072}
4073
4074HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4075 LONG aDevice)
4076{
4077 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4078 aName.c_str(), aControllerPort, aDevice));
4079
4080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4081
4082 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4083 if (FAILED(rc)) return rc;
4084
4085 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4086
4087 /* Check for an existing controller. */
4088 ComObjPtr<StorageController> ctl;
4089 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4090 if (FAILED(rc)) return rc;
4091
4092 StorageControllerType_T ctrlType;
4093 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4094 if (FAILED(rc))
4095 return setError(E_FAIL,
4096 tr("Could not get type of controller '%s'"),
4097 aName.c_str());
4098
4099 bool fSilent = false;
4100 Utf8Str strReconfig;
4101
4102 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4103 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4104 if ( mData->mMachineState == MachineState_Paused
4105 && strReconfig == "1")
4106 fSilent = true;
4107
4108 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4109 bool fHotplug = false;
4110 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4111 fHotplug = true;
4112
4113 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4114 return setError(VBOX_E_INVALID_VM_STATE,
4115 tr("Controller '%s' does not support hotplugging"),
4116 aName.c_str());
4117
4118 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4119 aName,
4120 aControllerPort,
4121 aDevice);
4122 if (!pAttach)
4123 return setError(VBOX_E_OBJECT_NOT_FOUND,
4124 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4125 aDevice, aControllerPort, aName.c_str());
4126
4127 if (fHotplug && !pAttach->i_getHotPluggable())
4128 return setError(VBOX_E_NOT_SUPPORTED,
4129 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4130 aDevice, aControllerPort, aName.c_str());
4131
4132 /*
4133 * The VM has to detach the device before we delete any implicit diffs.
4134 * If this fails we can roll back without loosing data.
4135 */
4136 if (fHotplug || fSilent)
4137 {
4138 alock.release();
4139 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4140 alock.acquire();
4141 }
4142 if (FAILED(rc)) return rc;
4143
4144 /* If we are here everything went well and we can delete the implicit now. */
4145 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4146
4147 alock.release();
4148
4149 /* Save modified registries, but skip this machine as it's the caller's
4150 * job to save its settings like all other settings changes. */
4151 mParent->i_unmarkRegistryModified(i_getId());
4152 mParent->i_saveModifiedRegistries();
4153
4154 if (SUCCEEDED(rc))
4155 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4156
4157 return rc;
4158}
4159
4160HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4161 LONG aDevice, BOOL aPassthrough)
4162{
4163 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4164 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4165
4166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4167
4168 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4169 if (FAILED(rc)) return rc;
4170
4171 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4172
4173 /* Check for an existing controller. */
4174 ComObjPtr<StorageController> ctl;
4175 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4176 if (FAILED(rc)) return rc;
4177
4178 StorageControllerType_T ctrlType;
4179 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4180 if (FAILED(rc))
4181 return setError(E_FAIL,
4182 tr("Could not get type of controller '%s'"),
4183 aName.c_str());
4184
4185 bool fSilent = false;
4186 Utf8Str strReconfig;
4187
4188 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4189 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4190 if ( mData->mMachineState == MachineState_Paused
4191 && strReconfig == "1")
4192 fSilent = true;
4193
4194 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4195 bool fHotplug = false;
4196 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4197 fHotplug = true;
4198
4199 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4200 return setError(VBOX_E_INVALID_VM_STATE,
4201 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4202 aName.c_str());
4203
4204 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4205 aName,
4206 aControllerPort,
4207 aDevice);
4208 if (!pAttach)
4209 return setError(VBOX_E_OBJECT_NOT_FOUND,
4210 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4211 aDevice, aControllerPort, aName.c_str());
4212
4213
4214 i_setModified(IsModified_Storage);
4215 mMediumAttachments.backup();
4216
4217 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4218
4219 if (pAttach->i_getType() != DeviceType_DVD)
4220 return setError(E_INVALIDARG,
4221 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4222 aDevice, aControllerPort, aName.c_str());
4223
4224 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4225
4226 pAttach->i_updatePassthrough(!!aPassthrough);
4227
4228 attLock.release();
4229 alock.release();
4230 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4231 if (SUCCEEDED(rc) && fValueChanged)
4232 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4233
4234 return rc;
4235}
4236
4237HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4238 LONG aDevice, BOOL aTemporaryEject)
4239{
4240
4241 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4242 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4243
4244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4245
4246 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4247 if (FAILED(rc)) return rc;
4248
4249 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4250 aName,
4251 aControllerPort,
4252 aDevice);
4253 if (!pAttach)
4254 return setError(VBOX_E_OBJECT_NOT_FOUND,
4255 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4256 aDevice, aControllerPort, aName.c_str());
4257
4258
4259 i_setModified(IsModified_Storage);
4260 mMediumAttachments.backup();
4261
4262 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4263
4264 if (pAttach->i_getType() != DeviceType_DVD)
4265 return setError(E_INVALIDARG,
4266 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4267 aDevice, aControllerPort, aName.c_str());
4268 pAttach->i_updateTempEject(!!aTemporaryEject);
4269
4270 return S_OK;
4271}
4272
4273HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4274 LONG aDevice, BOOL aNonRotational)
4275{
4276
4277 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4278 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4279
4280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4281
4282 HRESULT rc = i_checkStateDependency(MutableStateDep);
4283 if (FAILED(rc)) return rc;
4284
4285 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4286
4287 if (Global::IsOnlineOrTransient(mData->mMachineState))
4288 return setError(VBOX_E_INVALID_VM_STATE,
4289 tr("Invalid machine state: %s"),
4290 Global::stringifyMachineState(mData->mMachineState));
4291
4292 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4293 aName,
4294 aControllerPort,
4295 aDevice);
4296 if (!pAttach)
4297 return setError(VBOX_E_OBJECT_NOT_FOUND,
4298 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4299 aDevice, aControllerPort, aName.c_str());
4300
4301
4302 i_setModified(IsModified_Storage);
4303 mMediumAttachments.backup();
4304
4305 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4306
4307 if (pAttach->i_getType() != DeviceType_HardDisk)
4308 return setError(E_INVALIDARG,
4309 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"),
4310 aDevice, aControllerPort, aName.c_str());
4311 pAttach->i_updateNonRotational(!!aNonRotational);
4312
4313 return S_OK;
4314}
4315
4316HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4317 LONG aDevice, BOOL aDiscard)
4318{
4319
4320 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4321 aName.c_str(), aControllerPort, aDevice, aDiscard));
4322
4323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4324
4325 HRESULT rc = i_checkStateDependency(MutableStateDep);
4326 if (FAILED(rc)) return rc;
4327
4328 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4329
4330 if (Global::IsOnlineOrTransient(mData->mMachineState))
4331 return setError(VBOX_E_INVALID_VM_STATE,
4332 tr("Invalid machine state: %s"),
4333 Global::stringifyMachineState(mData->mMachineState));
4334
4335 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4336 aName,
4337 aControllerPort,
4338 aDevice);
4339 if (!pAttach)
4340 return setError(VBOX_E_OBJECT_NOT_FOUND,
4341 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4342 aDevice, aControllerPort, aName.c_str());
4343
4344
4345 i_setModified(IsModified_Storage);
4346 mMediumAttachments.backup();
4347
4348 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4349
4350 if (pAttach->i_getType() != DeviceType_HardDisk)
4351 return setError(E_INVALIDARG,
4352 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"),
4353 aDevice, aControllerPort, aName.c_str());
4354 pAttach->i_updateDiscard(!!aDiscard);
4355
4356 return S_OK;
4357}
4358
4359HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4360 LONG aDevice, BOOL aHotPluggable)
4361{
4362 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4363 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4364
4365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4366
4367 HRESULT rc = i_checkStateDependency(MutableStateDep);
4368 if (FAILED(rc)) return rc;
4369
4370 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4371
4372 if (Global::IsOnlineOrTransient(mData->mMachineState))
4373 return setError(VBOX_E_INVALID_VM_STATE,
4374 tr("Invalid machine state: %s"),
4375 Global::stringifyMachineState(mData->mMachineState));
4376
4377 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4378 aName,
4379 aControllerPort,
4380 aDevice);
4381 if (!pAttach)
4382 return setError(VBOX_E_OBJECT_NOT_FOUND,
4383 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4384 aDevice, aControllerPort, aName.c_str());
4385
4386 /* Check for an existing controller. */
4387 ComObjPtr<StorageController> ctl;
4388 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4389 if (FAILED(rc)) return rc;
4390
4391 StorageControllerType_T ctrlType;
4392 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4393 if (FAILED(rc))
4394 return setError(E_FAIL,
4395 tr("Could not get type of controller '%s'"),
4396 aName.c_str());
4397
4398 if (!i_isControllerHotplugCapable(ctrlType))
4399 return setError(VBOX_E_NOT_SUPPORTED,
4400 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4401 aName.c_str());
4402
4403 i_setModified(IsModified_Storage);
4404 mMediumAttachments.backup();
4405
4406 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4407
4408 if (pAttach->i_getType() == DeviceType_Floppy)
4409 return setError(E_INVALIDARG,
4410 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"),
4411 aDevice, aControllerPort, aName.c_str());
4412 pAttach->i_updateHotPluggable(!!aHotPluggable);
4413
4414 return S_OK;
4415}
4416
4417HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4418 LONG aDevice)
4419{
4420 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4421 aName.c_str(), aControllerPort, aDevice));
4422
4423 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4424}
4425
4426HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4427 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4428{
4429 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4430 aName.c_str(), aControllerPort, aDevice));
4431
4432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4433
4434 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4435 if (FAILED(rc)) return rc;
4436
4437 if (Global::IsOnlineOrTransient(mData->mMachineState))
4438 return setError(VBOX_E_INVALID_VM_STATE,
4439 tr("Invalid machine state: %s"),
4440 Global::stringifyMachineState(mData->mMachineState));
4441
4442 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4443 aName,
4444 aControllerPort,
4445 aDevice);
4446 if (!pAttach)
4447 return setError(VBOX_E_OBJECT_NOT_FOUND,
4448 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4449 aDevice, aControllerPort, aName.c_str());
4450
4451
4452 i_setModified(IsModified_Storage);
4453 mMediumAttachments.backup();
4454
4455 IBandwidthGroup *iB = aBandwidthGroup;
4456 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4457 if (aBandwidthGroup && group.isNull())
4458 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4459
4460 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4461
4462 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4463 if (strBandwidthGroupOld.isNotEmpty())
4464 {
4465 /* Get the bandwidth group object and release it - this must not fail. */
4466 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4467 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4468 Assert(SUCCEEDED(rc));
4469
4470 pBandwidthGroupOld->i_release();
4471 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4472 }
4473
4474 if (!group.isNull())
4475 {
4476 group->i_reference();
4477 pAttach->i_updateBandwidthGroup(group->i_getName());
4478 }
4479
4480 return S_OK;
4481}
4482
4483HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4484 LONG aControllerPort,
4485 LONG aDevice,
4486 DeviceType_T aType)
4487{
4488 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4489 aName.c_str(), aControllerPort, aDevice, aType));
4490
4491 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4492}
4493
4494
4495HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4496 LONG aControllerPort,
4497 LONG aDevice,
4498 BOOL aForce)
4499{
4500 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4501 aName.c_str(), aControllerPort, aForce));
4502
4503 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4504}
4505
4506HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4507 LONG aControllerPort,
4508 LONG aDevice,
4509 const ComPtr<IMedium> &aMedium,
4510 BOOL aForce)
4511{
4512 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4513 aName.c_str(), aControllerPort, aDevice, aForce));
4514
4515 // request the host lock first, since might be calling Host methods for getting host drives;
4516 // next, protect the media tree all the while we're in here, as well as our member variables
4517 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4518 this->lockHandle(),
4519 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4520
4521 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4522 aName,
4523 aControllerPort,
4524 aDevice);
4525 if (pAttach.isNull())
4526 return setError(VBOX_E_OBJECT_NOT_FOUND,
4527 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4528 aDevice, aControllerPort, aName.c_str());
4529
4530 /* Remember previously mounted medium. The medium before taking the
4531 * backup is not necessarily the same thing. */
4532 ComObjPtr<Medium> oldmedium;
4533 oldmedium = pAttach->i_getMedium();
4534
4535 IMedium *iM = aMedium;
4536 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4537 if (aMedium && pMedium.isNull())
4538 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4539
4540 AutoCaller mediumCaller(pMedium);
4541 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4542
4543 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4544 if (pMedium)
4545 {
4546 DeviceType_T mediumType = pAttach->i_getType();
4547 switch (mediumType)
4548 {
4549 case DeviceType_DVD:
4550 case DeviceType_Floppy:
4551 break;
4552
4553 default:
4554 return setError(VBOX_E_INVALID_OBJECT_STATE,
4555 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4556 aControllerPort,
4557 aDevice,
4558 aName.c_str());
4559 }
4560 }
4561
4562 i_setModified(IsModified_Storage);
4563 mMediumAttachments.backup();
4564
4565 {
4566 // The backup operation makes the pAttach reference point to the
4567 // old settings. Re-get the correct reference.
4568 pAttach = i_findAttachment(*mMediumAttachments.data(),
4569 aName,
4570 aControllerPort,
4571 aDevice);
4572 if (!oldmedium.isNull())
4573 oldmedium->i_removeBackReference(mData->mUuid);
4574 if (!pMedium.isNull())
4575 {
4576 pMedium->i_addBackReference(mData->mUuid);
4577
4578 mediumLock.release();
4579 multiLock.release();
4580 i_addMediumToRegistry(pMedium);
4581 multiLock.acquire();
4582 mediumLock.acquire();
4583 }
4584
4585 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4586 pAttach->i_updateMedium(pMedium);
4587 }
4588
4589 i_setModified(IsModified_Storage);
4590
4591 mediumLock.release();
4592 multiLock.release();
4593 HRESULT rc = i_onMediumChange(pAttach, aForce);
4594 multiLock.acquire();
4595 mediumLock.acquire();
4596
4597 /* On error roll back this change only. */
4598 if (FAILED(rc))
4599 {
4600 if (!pMedium.isNull())
4601 pMedium->i_removeBackReference(mData->mUuid);
4602 pAttach = i_findAttachment(*mMediumAttachments.data(),
4603 aName,
4604 aControllerPort,
4605 aDevice);
4606 /* If the attachment is gone in the meantime, bail out. */
4607 if (pAttach.isNull())
4608 return rc;
4609 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4610 if (!oldmedium.isNull())
4611 oldmedium->i_addBackReference(mData->mUuid);
4612 pAttach->i_updateMedium(oldmedium);
4613 }
4614
4615 mediumLock.release();
4616 multiLock.release();
4617
4618 /* Save modified registries, but skip this machine as it's the caller's
4619 * job to save its settings like all other settings changes. */
4620 mParent->i_unmarkRegistryModified(i_getId());
4621 mParent->i_saveModifiedRegistries();
4622
4623 return rc;
4624}
4625HRESULT Machine::getMedium(const com::Utf8Str &aName,
4626 LONG aControllerPort,
4627 LONG aDevice,
4628 ComPtr<IMedium> &aMedium)
4629{
4630 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4631 aName.c_str(), aControllerPort, aDevice));
4632
4633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4634
4635 aMedium = NULL;
4636
4637 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4638 aName,
4639 aControllerPort,
4640 aDevice);
4641 if (pAttach.isNull())
4642 return setError(VBOX_E_OBJECT_NOT_FOUND,
4643 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4644 aDevice, aControllerPort, aName.c_str());
4645
4646 aMedium = pAttach->i_getMedium();
4647
4648 return S_OK;
4649}
4650
4651HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4652{
4653 if (aSlot < RT_ELEMENTS(mSerialPorts))
4654 {
4655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4656 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4657 return S_OK;
4658 }
4659 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4660}
4661
4662HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4663{
4664 if (aSlot < RT_ELEMENTS(mParallelPorts))
4665 {
4666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4667 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4668 return S_OK;
4669 }
4670 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4671}
4672
4673
4674HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4675{
4676 /* Do not assert if slot is out of range, just return the advertised
4677 status. testdriver/vbox.py triggers this in logVmInfo. */
4678 if (aSlot >= mNetworkAdapters.size())
4679 return setError(E_INVALIDARG,
4680 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4681 aSlot, mNetworkAdapters.size());
4682
4683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4684
4685 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4686
4687 return S_OK;
4688}
4689
4690HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4691{
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4695 size_t i = 0;
4696 for (settings::StringsMap::const_iterator
4697 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4698 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4699 ++it, ++i)
4700 aKeys[i] = it->first;
4701
4702 return S_OK;
4703}
4704
4705 /**
4706 * @note Locks this object for reading.
4707 */
4708HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4709 com::Utf8Str &aValue)
4710{
4711 /* start with nothing found */
4712 aValue = "";
4713
4714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4715
4716 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4717 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4718 // found:
4719 aValue = it->second; // source is a Utf8Str
4720
4721 /* return the result to caller (may be empty) */
4722 return S_OK;
4723}
4724
4725 /**
4726 * @note Locks mParent for writing + this object for writing.
4727 */
4728HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4729{
4730 /* Because control characters in aKey have caused problems in the settings
4731 * they are rejected unless the key should be deleted. */
4732 if (!aValue.isEmpty())
4733 {
4734 for (size_t i = 0; i < aKey.length(); ++i)
4735 {
4736 char ch = aKey[i];
4737 if (RTLocCIsCntrl(ch))
4738 return E_INVALIDARG;
4739 }
4740 }
4741
4742 Utf8Str strOldValue; // empty
4743
4744 // locking note: we only hold the read lock briefly to look up the old value,
4745 // then release it and call the onExtraCanChange callbacks. There is a small
4746 // chance of a race insofar as the callback might be called twice if two callers
4747 // change the same key at the same time, but that's a much better solution
4748 // than the deadlock we had here before. The actual changing of the extradata
4749 // is then performed under the write lock and race-free.
4750
4751 // look up the old value first; if nothing has changed then we need not do anything
4752 {
4753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4754
4755 // For snapshots don't even think about allowing changes, extradata
4756 // is global for a machine, so there is nothing snapshot specific.
4757 if (i_isSnapshotMachine())
4758 return setError(VBOX_E_INVALID_VM_STATE,
4759 tr("Cannot set extradata for a snapshot"));
4760
4761 // check if the right IMachine instance is used
4762 if (mData->mRegistered && !i_isSessionMachine())
4763 return setError(VBOX_E_INVALID_VM_STATE,
4764 tr("Cannot set extradata for an immutable machine"));
4765
4766 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4767 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4768 strOldValue = it->second;
4769 }
4770
4771 bool fChanged;
4772 if ((fChanged = (strOldValue != aValue)))
4773 {
4774 // ask for permission from all listeners outside the locks;
4775 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4776 // lock to copy the list of callbacks to invoke
4777 Bstr bstrError;
4778 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4779 {
4780 const char *sep = bstrError.isEmpty() ? "" : ": ";
4781 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4782 return setError(E_ACCESSDENIED,
4783 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4784 aKey.c_str(),
4785 aValue.c_str(),
4786 sep,
4787 bstrError.raw());
4788 }
4789
4790 // data is changing and change not vetoed: then write it out under the lock
4791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4792
4793 if (aValue.isEmpty())
4794 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4795 else
4796 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4797 // creates a new key if needed
4798
4799 bool fNeedsGlobalSaveSettings = false;
4800 // This saving of settings is tricky: there is no "old state" for the
4801 // extradata items at all (unlike all other settings), so the old/new
4802 // settings comparison would give a wrong result!
4803 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4804
4805 if (fNeedsGlobalSaveSettings)
4806 {
4807 // save the global settings; for that we should hold only the VirtualBox lock
4808 alock.release();
4809 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4810 mParent->i_saveSettings();
4811 }
4812 }
4813
4814 // fire notification outside the lock
4815 if (fChanged)
4816 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4817
4818 return S_OK;
4819}
4820
4821HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4822{
4823 aProgress = NULL;
4824 NOREF(aSettingsFilePath);
4825 ReturnComNotImplemented();
4826}
4827
4828HRESULT Machine::saveSettings()
4829{
4830 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4831
4832 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4833 if (FAILED(rc)) return rc;
4834
4835 /* the settings file path may never be null */
4836 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4837
4838 /* save all VM data excluding snapshots */
4839 bool fNeedsGlobalSaveSettings = false;
4840 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4841 mlock.release();
4842
4843 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4844 {
4845 // save the global settings; for that we should hold only the VirtualBox lock
4846 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4847 rc = mParent->i_saveSettings();
4848 }
4849
4850 return rc;
4851}
4852
4853
4854HRESULT Machine::discardSettings()
4855{
4856 /*
4857 * We need to take the machine list lock here as well as the machine one
4858 * or we'll get into trouble should any media stuff require rolling back.
4859 *
4860 * Details:
4861 *
4862 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4863 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4864 * 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]
4865 * 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
4866 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4867 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4869 * 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
4870 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4872 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4873 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4874 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4875 * 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]
4876 * 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] (*)
4877 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4878 * 0:005> k
4879 * # Child-SP RetAddr Call Site
4880 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4881 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4882 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4883 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4884 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4885 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4886 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4887 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4888 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4889 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4890 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4891 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4892 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4893 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4894 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4895 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4896 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4897 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4898 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4899 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4900 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4901 *
4902 */
4903 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4905
4906 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4907 if (FAILED(rc)) return rc;
4908
4909 /*
4910 * during this rollback, the session will be notified if data has
4911 * been actually changed
4912 */
4913 i_rollback(true /* aNotify */);
4914
4915 return S_OK;
4916}
4917
4918/** @note Locks objects! */
4919HRESULT Machine::unregister(AutoCaller &autoCaller,
4920 CleanupMode_T aCleanupMode,
4921 std::vector<ComPtr<IMedium> > &aMedia)
4922{
4923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4924
4925 Guid id(i_getId());
4926
4927 if (mData->mSession.mState != SessionState_Unlocked)
4928 return setError(VBOX_E_INVALID_OBJECT_STATE,
4929 tr("Cannot unregister the machine '%s' while it is locked"),
4930 mUserData->s.strName.c_str());
4931
4932 // wait for state dependents to drop to zero
4933 i_ensureNoStateDependencies();
4934
4935 if (!mData->mAccessible)
4936 {
4937 // inaccessible machines can only be unregistered; uninitialize ourselves
4938 // here because currently there may be no unregistered that are inaccessible
4939 // (this state combination is not supported). Note releasing the caller and
4940 // leaving the lock before calling uninit()
4941 alock.release();
4942 autoCaller.release();
4943
4944 uninit();
4945
4946 mParent->i_unregisterMachine(this, id);
4947 // calls VirtualBox::i_saveSettings()
4948
4949 return S_OK;
4950 }
4951
4952 HRESULT rc = S_OK;
4953 mData->llFilesToDelete.clear();
4954
4955 if (!mSSData->strStateFilePath.isEmpty())
4956 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4957
4958 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4959 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4960 mData->llFilesToDelete.push_back(strNVRAMFile);
4961
4962 // This list collects the medium objects from all medium attachments
4963 // which we will detach from the machine and its snapshots, in a specific
4964 // order which allows for closing all media without getting "media in use"
4965 // errors, simply by going through the list from the front to the back:
4966 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4967 // and must be closed before the parent media from the snapshots, or closing the parents
4968 // will fail because they still have children);
4969 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4970 // the root ("first") snapshot of the machine.
4971 MediaList llMedia;
4972
4973 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4974 && mMediumAttachments->size()
4975 )
4976 {
4977 // we have media attachments: detach them all and add the Medium objects to our list
4978 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4979 }
4980
4981 if (mData->mFirstSnapshot)
4982 {
4983 // add the media from the medium attachments of the snapshots to llMedia
4984 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4985 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4986 // into the children first
4987
4988 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4989 MachineState_T oldState = mData->mMachineState;
4990 mData->mMachineState = MachineState_DeletingSnapshot;
4991
4992 // make a copy of the first snapshot reference so the refcount does not
4993 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4994 // (would hang due to the AutoCaller voodoo)
4995 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4996
4997 // GO!
4998 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4999
5000 mData->mMachineState = oldState;
5001 }
5002
5003 if (FAILED(rc))
5004 {
5005 i_rollbackMedia();
5006 return rc;
5007 }
5008
5009 // commit all the media changes made above
5010 i_commitMedia();
5011
5012 mData->mRegistered = false;
5013
5014 // machine lock no longer needed
5015 alock.release();
5016
5017 /* Make sure that the settings of the current VM are not saved, because
5018 * they are rather crippled at this point to meet the cleanup expectations
5019 * and there's no point destroying the VM config on disk just because. */
5020 mParent->i_unmarkRegistryModified(id);
5021
5022 // return media to caller
5023 aMedia.resize(llMedia.size());
5024 size_t i = 0;
5025 for (MediaList::const_iterator
5026 it = llMedia.begin();
5027 it != llMedia.end();
5028 ++it, ++i)
5029 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5030
5031 mParent->i_unregisterMachine(this, id);
5032 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5033
5034 return S_OK;
5035}
5036
5037/**
5038 * Task record for deleting a machine config.
5039 */
5040class Machine::DeleteConfigTask
5041 : public Machine::Task
5042{
5043public:
5044 DeleteConfigTask(Machine *m,
5045 Progress *p,
5046 const Utf8Str &t,
5047 const RTCList<ComPtr<IMedium> > &llMediums,
5048 const StringsList &llFilesToDelete)
5049 : Task(m, p, t),
5050 m_llMediums(llMediums),
5051 m_llFilesToDelete(llFilesToDelete)
5052 {}
5053
5054private:
5055 void handler()
5056 {
5057 try
5058 {
5059 m_pMachine->i_deleteConfigHandler(*this);
5060 }
5061 catch (...)
5062 {
5063 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5064 }
5065 }
5066
5067 RTCList<ComPtr<IMedium> > m_llMediums;
5068 StringsList m_llFilesToDelete;
5069
5070 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5071};
5072
5073/**
5074 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5075 * SessionMachine::taskHandler().
5076 *
5077 * @note Locks this object for writing.
5078 *
5079 * @param task
5080 * @return
5081 */
5082void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5083{
5084 LogFlowThisFuncEnter();
5085
5086 AutoCaller autoCaller(this);
5087 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5088 if (FAILED(autoCaller.rc()))
5089 {
5090 /* we might have been uninitialized because the session was accidentally
5091 * closed by the client, so don't assert */
5092 HRESULT rc = setError(E_FAIL,
5093 tr("The session has been accidentally closed"));
5094 task.m_pProgress->i_notifyComplete(rc);
5095 LogFlowThisFuncLeave();
5096 return;
5097 }
5098
5099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5100
5101 HRESULT rc = S_OK;
5102
5103 try
5104 {
5105 ULONG uLogHistoryCount = 3;
5106 ComPtr<ISystemProperties> systemProperties;
5107 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5108 if (FAILED(rc)) throw rc;
5109
5110 if (!systemProperties.isNull())
5111 {
5112 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5113 if (FAILED(rc)) throw rc;
5114 }
5115
5116 MachineState_T oldState = mData->mMachineState;
5117 i_setMachineState(MachineState_SettingUp);
5118 alock.release();
5119 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5120 {
5121 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5122 {
5123 AutoCaller mac(pMedium);
5124 if (FAILED(mac.rc())) throw mac.rc();
5125 Utf8Str strLocation = pMedium->i_getLocationFull();
5126 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5127 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5128 if (FAILED(rc)) throw rc;
5129 }
5130 if (pMedium->i_isMediumFormatFile())
5131 {
5132 ComPtr<IProgress> pProgress2;
5133 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5134 if (FAILED(rc)) throw rc;
5135 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5136 if (FAILED(rc)) throw rc;
5137 }
5138
5139 /* Close the medium, deliberately without checking the return
5140 * code, and without leaving any trace in the error info, as
5141 * a failure here is a very minor issue, which shouldn't happen
5142 * as above we even managed to delete the medium. */
5143 {
5144 ErrorInfoKeeper eik;
5145 pMedium->Close();
5146 }
5147 }
5148 i_setMachineState(oldState);
5149 alock.acquire();
5150
5151 // delete the files pushed on the task list by Machine::Delete()
5152 // (this includes saved states of the machine and snapshots and
5153 // medium storage files from the IMedium list passed in, and the
5154 // machine XML file)
5155 for (StringsList::const_iterator
5156 it = task.m_llFilesToDelete.begin();
5157 it != task.m_llFilesToDelete.end();
5158 ++it)
5159 {
5160 const Utf8Str &strFile = *it;
5161 LogFunc(("Deleting file %s\n", strFile.c_str()));
5162 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5163 if (FAILED(rc)) throw rc;
5164
5165 int vrc = RTFileDelete(strFile.c_str());
5166 if (RT_FAILURE(vrc))
5167 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5168 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5169 }
5170
5171 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5172 if (FAILED(rc)) throw rc;
5173
5174 /* delete the settings only when the file actually exists */
5175 if (mData->pMachineConfigFile->fileExists())
5176 {
5177 /* Delete any backup or uncommitted XML files. Ignore failures.
5178 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5179 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5180 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5181 RTFileDelete(otherXml.c_str());
5182 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5183 RTFileDelete(otherXml.c_str());
5184
5185 /* delete the Logs folder, nothing important should be left
5186 * there (we don't check for errors because the user might have
5187 * some private files there that we don't want to delete) */
5188 Utf8Str logFolder;
5189 getLogFolder(logFolder);
5190 Assert(logFolder.length());
5191 if (RTDirExists(logFolder.c_str()))
5192 {
5193 /* Delete all VBox.log[.N] files from the Logs folder
5194 * (this must be in sync with the rotation logic in
5195 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5196 * files that may have been created by the GUI. */
5197 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5198 RTFileDelete(log.c_str());
5199 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5200 RTFileDelete(log.c_str());
5201 for (ULONG i = uLogHistoryCount; i > 0; i--)
5202 {
5203 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5204 RTFileDelete(log.c_str());
5205 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5206 RTFileDelete(log.c_str());
5207 }
5208 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5209 RTFileDelete(log.c_str());
5210#if defined(RT_OS_WINDOWS)
5211 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5212 RTFileDelete(log.c_str());
5213 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5214 RTFileDelete(log.c_str());
5215#endif
5216
5217 RTDirRemove(logFolder.c_str());
5218 }
5219
5220 /* delete the Snapshots folder, nothing important should be left
5221 * there (we don't check for errors because the user might have
5222 * some private files there that we don't want to delete) */
5223 Utf8Str strFullSnapshotFolder;
5224 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5225 Assert(!strFullSnapshotFolder.isEmpty());
5226 if (RTDirExists(strFullSnapshotFolder.c_str()))
5227 RTDirRemove(strFullSnapshotFolder.c_str());
5228
5229 // delete the directory that contains the settings file, but only
5230 // if it matches the VM name
5231 Utf8Str settingsDir;
5232 if (i_isInOwnDir(&settingsDir))
5233 RTDirRemove(settingsDir.c_str());
5234 }
5235
5236 alock.release();
5237
5238 mParent->i_saveModifiedRegistries();
5239 }
5240 catch (HRESULT aRC) { rc = aRC; }
5241
5242 task.m_pProgress->i_notifyComplete(rc);
5243
5244 LogFlowThisFuncLeave();
5245}
5246
5247HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5248{
5249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5250
5251 HRESULT rc = i_checkStateDependency(MutableStateDep);
5252 if (FAILED(rc)) return rc;
5253
5254 if (mData->mRegistered)
5255 return setError(VBOX_E_INVALID_VM_STATE,
5256 tr("Cannot delete settings of a registered machine"));
5257
5258 // collect files to delete
5259 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5260 // machine config file
5261 if (mData->pMachineConfigFile->fileExists())
5262 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5263 // backup of machine config file
5264 Utf8Str strTmp(mData->m_strConfigFileFull);
5265 strTmp.append("-prev");
5266 if (RTFileExists(strTmp.c_str()))
5267 llFilesToDelete.push_back(strTmp);
5268
5269 RTCList<ComPtr<IMedium> > llMediums;
5270 for (size_t i = 0; i < aMedia.size(); ++i)
5271 {
5272 IMedium *pIMedium(aMedia[i]);
5273 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5274 if (pMedium.isNull())
5275 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5276 SafeArray<BSTR> ids;
5277 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5278 if (FAILED(rc)) return rc;
5279 /* At this point the medium should not have any back references
5280 * anymore. If it has it is attached to another VM and *must* not
5281 * deleted. */
5282 if (ids.size() < 1)
5283 llMediums.append(pMedium);
5284 }
5285
5286 ComObjPtr<Progress> pProgress;
5287 pProgress.createObject();
5288 rc = pProgress->init(i_getVirtualBox(),
5289 static_cast<IMachine*>(this) /* aInitiator */,
5290 tr("Deleting files"),
5291 true /* fCancellable */,
5292 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5293 tr("Collecting file inventory"));
5294 if (FAILED(rc))
5295 return rc;
5296
5297 /* create and start the task on a separate thread (note that it will not
5298 * start working until we release alock) */
5299 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5300 rc = pTask->createThread();
5301 pTask = NULL;
5302 if (FAILED(rc))
5303 return rc;
5304
5305 pProgress.queryInterfaceTo(aProgress.asOutParam());
5306
5307 LogFlowFuncLeave();
5308
5309 return S_OK;
5310}
5311
5312HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5313{
5314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5315
5316 ComObjPtr<Snapshot> pSnapshot;
5317 HRESULT rc;
5318
5319 if (aNameOrId.isEmpty())
5320 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5321 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5322 else
5323 {
5324 Guid uuid(aNameOrId);
5325 if (uuid.isValid())
5326 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5327 else
5328 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5329 }
5330 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5331
5332 return rc;
5333}
5334
5335HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5336 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5337{
5338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5339
5340 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5341 if (FAILED(rc)) return rc;
5342
5343 ComObjPtr<SharedFolder> sharedFolder;
5344 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5345 if (SUCCEEDED(rc))
5346 return setError(VBOX_E_OBJECT_IN_USE,
5347 tr("Shared folder named '%s' already exists"),
5348 aName.c_str());
5349
5350 sharedFolder.createObject();
5351 rc = sharedFolder->init(i_getMachine(),
5352 aName,
5353 aHostPath,
5354 !!aWritable,
5355 !!aAutomount,
5356 aAutoMountPoint,
5357 true /* fFailOnError */);
5358 if (FAILED(rc)) return rc;
5359
5360 i_setModified(IsModified_SharedFolders);
5361 mHWData.backup();
5362 mHWData->mSharedFolders.push_back(sharedFolder);
5363
5364 /* inform the direct session if any */
5365 alock.release();
5366 i_onSharedFolderChange();
5367
5368 return S_OK;
5369}
5370
5371HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5372{
5373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5374
5375 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5376 if (FAILED(rc)) return rc;
5377
5378 ComObjPtr<SharedFolder> sharedFolder;
5379 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5380 if (FAILED(rc)) return rc;
5381
5382 i_setModified(IsModified_SharedFolders);
5383 mHWData.backup();
5384 mHWData->mSharedFolders.remove(sharedFolder);
5385
5386 /* inform the direct session if any */
5387 alock.release();
5388 i_onSharedFolderChange();
5389
5390 return S_OK;
5391}
5392
5393HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5394{
5395 /* start with No */
5396 *aCanShow = FALSE;
5397
5398 ComPtr<IInternalSessionControl> directControl;
5399 {
5400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5401
5402 if (mData->mSession.mState != SessionState_Locked)
5403 return setError(VBOX_E_INVALID_VM_STATE,
5404 tr("Machine is not locked for session (session state: %s)"),
5405 Global::stringifySessionState(mData->mSession.mState));
5406
5407 if (mData->mSession.mLockType == LockType_VM)
5408 directControl = mData->mSession.mDirectControl;
5409 }
5410
5411 /* ignore calls made after #OnSessionEnd() is called */
5412 if (!directControl)
5413 return S_OK;
5414
5415 LONG64 dummy;
5416 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5417}
5418
5419HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5420{
5421 ComPtr<IInternalSessionControl> directControl;
5422 {
5423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5424
5425 if (mData->mSession.mState != SessionState_Locked)
5426 return setError(E_FAIL,
5427 tr("Machine is not locked for session (session state: %s)"),
5428 Global::stringifySessionState(mData->mSession.mState));
5429
5430 if (mData->mSession.mLockType == LockType_VM)
5431 directControl = mData->mSession.mDirectControl;
5432 }
5433
5434 /* ignore calls made after #OnSessionEnd() is called */
5435 if (!directControl)
5436 return S_OK;
5437
5438 BOOL dummy;
5439 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5440}
5441
5442#ifdef VBOX_WITH_GUEST_PROPS
5443/**
5444 * Look up a guest property in VBoxSVC's internal structures.
5445 */
5446HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5447 com::Utf8Str &aValue,
5448 LONG64 *aTimestamp,
5449 com::Utf8Str &aFlags) const
5450{
5451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5452
5453 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5454 if (it != mHWData->mGuestProperties.end())
5455 {
5456 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5457 aValue = it->second.strValue;
5458 *aTimestamp = it->second.mTimestamp;
5459 GuestPropWriteFlags(it->second.mFlags, szFlags);
5460 aFlags = Utf8Str(szFlags);
5461 }
5462
5463 return S_OK;
5464}
5465
5466/**
5467 * Query the VM that a guest property belongs to for the property.
5468 * @returns E_ACCESSDENIED if the VM process is not available or not
5469 * currently handling queries and the lookup should then be done in
5470 * VBoxSVC.
5471 */
5472HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5473 com::Utf8Str &aValue,
5474 LONG64 *aTimestamp,
5475 com::Utf8Str &aFlags) const
5476{
5477 HRESULT rc = S_OK;
5478 Bstr bstrValue;
5479 Bstr bstrFlags;
5480
5481 ComPtr<IInternalSessionControl> directControl;
5482 {
5483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5484 if (mData->mSession.mLockType == LockType_VM)
5485 directControl = mData->mSession.mDirectControl;
5486 }
5487
5488 /* ignore calls made after #OnSessionEnd() is called */
5489 if (!directControl)
5490 rc = E_ACCESSDENIED;
5491 else
5492 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5493 0 /* accessMode */,
5494 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5495
5496 aValue = bstrValue;
5497 aFlags = bstrFlags;
5498
5499 return rc;
5500}
5501#endif // VBOX_WITH_GUEST_PROPS
5502
5503HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5504 com::Utf8Str &aValue,
5505 LONG64 *aTimestamp,
5506 com::Utf8Str &aFlags)
5507{
5508#ifndef VBOX_WITH_GUEST_PROPS
5509 ReturnComNotImplemented();
5510#else // VBOX_WITH_GUEST_PROPS
5511
5512 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5513
5514 if (rc == E_ACCESSDENIED)
5515 /* The VM is not running or the service is not (yet) accessible */
5516 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5517 return rc;
5518#endif // VBOX_WITH_GUEST_PROPS
5519}
5520
5521HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5522{
5523 LONG64 dummyTimestamp;
5524 com::Utf8Str dummyFlags;
5525 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5526 return rc;
5527
5528}
5529HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5530{
5531 com::Utf8Str dummyFlags;
5532 com::Utf8Str dummyValue;
5533 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5534 return rc;
5535}
5536
5537#ifdef VBOX_WITH_GUEST_PROPS
5538/**
5539 * Set a guest property in VBoxSVC's internal structures.
5540 */
5541HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5542 const com::Utf8Str &aFlags, bool fDelete)
5543{
5544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5545 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5546 if (FAILED(rc)) return rc;
5547
5548 try
5549 {
5550 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5551 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5552 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5553
5554 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5555 if (it == mHWData->mGuestProperties.end())
5556 {
5557 if (!fDelete)
5558 {
5559 i_setModified(IsModified_MachineData);
5560 mHWData.backupEx();
5561
5562 RTTIMESPEC time;
5563 HWData::GuestProperty prop;
5564 prop.strValue = Bstr(aValue).raw();
5565 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5566 prop.mFlags = fFlags;
5567 mHWData->mGuestProperties[aName] = prop;
5568 }
5569 }
5570 else
5571 {
5572 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5573 {
5574 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5575 }
5576 else
5577 {
5578 i_setModified(IsModified_MachineData);
5579 mHWData.backupEx();
5580
5581 /* The backupEx() operation invalidates our iterator,
5582 * so get a new one. */
5583 it = mHWData->mGuestProperties.find(aName);
5584 Assert(it != mHWData->mGuestProperties.end());
5585
5586 if (!fDelete)
5587 {
5588 RTTIMESPEC time;
5589 it->second.strValue = aValue;
5590 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5591 it->second.mFlags = fFlags;
5592 }
5593 else
5594 mHWData->mGuestProperties.erase(it);
5595 }
5596 }
5597
5598 if (SUCCEEDED(rc))
5599 {
5600 alock.release();
5601
5602 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5603 }
5604 }
5605 catch (std::bad_alloc &)
5606 {
5607 rc = E_OUTOFMEMORY;
5608 }
5609
5610 return rc;
5611}
5612
5613/**
5614 * Set a property on the VM that that property belongs to.
5615 * @returns E_ACCESSDENIED if the VM process is not available or not
5616 * currently handling queries and the setting should then be done in
5617 * VBoxSVC.
5618 */
5619HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5620 const com::Utf8Str &aFlags, bool fDelete)
5621{
5622 HRESULT rc;
5623
5624 try
5625 {
5626 ComPtr<IInternalSessionControl> directControl;
5627 {
5628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5629 if (mData->mSession.mLockType == LockType_VM)
5630 directControl = mData->mSession.mDirectControl;
5631 }
5632
5633 Bstr dummy1; /* will not be changed (setter) */
5634 Bstr dummy2; /* will not be changed (setter) */
5635 LONG64 dummy64;
5636 if (!directControl)
5637 rc = E_ACCESSDENIED;
5638 else
5639 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5640 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5641 fDelete ? 2 : 1 /* accessMode */,
5642 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5643 }
5644 catch (std::bad_alloc &)
5645 {
5646 rc = E_OUTOFMEMORY;
5647 }
5648
5649 return rc;
5650}
5651#endif // VBOX_WITH_GUEST_PROPS
5652
5653HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5654 const com::Utf8Str &aFlags)
5655{
5656#ifndef VBOX_WITH_GUEST_PROPS
5657 ReturnComNotImplemented();
5658#else // VBOX_WITH_GUEST_PROPS
5659 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5660 if (rc == E_ACCESSDENIED)
5661 /* The VM is not running or the service is not (yet) accessible */
5662 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5663 return rc;
5664#endif // VBOX_WITH_GUEST_PROPS
5665}
5666
5667HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5668{
5669 return setGuestProperty(aProperty, aValue, "");
5670}
5671
5672HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5673{
5674#ifndef VBOX_WITH_GUEST_PROPS
5675 ReturnComNotImplemented();
5676#else // VBOX_WITH_GUEST_PROPS
5677 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5678 if (rc == E_ACCESSDENIED)
5679 /* The VM is not running or the service is not (yet) accessible */
5680 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5681 return rc;
5682#endif // VBOX_WITH_GUEST_PROPS
5683}
5684
5685#ifdef VBOX_WITH_GUEST_PROPS
5686/**
5687 * Enumerate the guest properties in VBoxSVC's internal structures.
5688 */
5689HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5690 std::vector<com::Utf8Str> &aNames,
5691 std::vector<com::Utf8Str> &aValues,
5692 std::vector<LONG64> &aTimestamps,
5693 std::vector<com::Utf8Str> &aFlags)
5694{
5695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5696 Utf8Str strPatterns(aPatterns);
5697
5698 /*
5699 * Look for matching patterns and build up a list.
5700 */
5701 HWData::GuestPropertyMap propMap;
5702 for (HWData::GuestPropertyMap::const_iterator
5703 it = mHWData->mGuestProperties.begin();
5704 it != mHWData->mGuestProperties.end();
5705 ++it)
5706 {
5707 if ( strPatterns.isEmpty()
5708 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5709 RTSTR_MAX,
5710 it->first.c_str(),
5711 RTSTR_MAX,
5712 NULL)
5713 )
5714 propMap.insert(*it);
5715 }
5716
5717 alock.release();
5718
5719 /*
5720 * And build up the arrays for returning the property information.
5721 */
5722 size_t cEntries = propMap.size();
5723
5724 aNames.resize(cEntries);
5725 aValues.resize(cEntries);
5726 aTimestamps.resize(cEntries);
5727 aFlags.resize(cEntries);
5728
5729 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5730 size_t i = 0;
5731 for (HWData::GuestPropertyMap::const_iterator
5732 it = propMap.begin();
5733 it != propMap.end();
5734 ++it, ++i)
5735 {
5736 aNames[i] = it->first;
5737 aValues[i] = it->second.strValue;
5738 aTimestamps[i] = it->second.mTimestamp;
5739 GuestPropWriteFlags(it->second.mFlags, szFlags);
5740 aFlags[i] = Utf8Str(szFlags);
5741 }
5742
5743 return S_OK;
5744}
5745
5746/**
5747 * Enumerate the properties managed by a VM.
5748 * @returns E_ACCESSDENIED if the VM process is not available or not
5749 * currently handling queries and the setting should then be done in
5750 * VBoxSVC.
5751 */
5752HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5753 std::vector<com::Utf8Str> &aNames,
5754 std::vector<com::Utf8Str> &aValues,
5755 std::vector<LONG64> &aTimestamps,
5756 std::vector<com::Utf8Str> &aFlags)
5757{
5758 HRESULT rc;
5759 ComPtr<IInternalSessionControl> directControl;
5760 {
5761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5762 if (mData->mSession.mLockType == LockType_VM)
5763 directControl = mData->mSession.mDirectControl;
5764 }
5765
5766 com::SafeArray<BSTR> bNames;
5767 com::SafeArray<BSTR> bValues;
5768 com::SafeArray<LONG64> bTimestamps;
5769 com::SafeArray<BSTR> bFlags;
5770
5771 if (!directControl)
5772 rc = E_ACCESSDENIED;
5773 else
5774 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5775 ComSafeArrayAsOutParam(bNames),
5776 ComSafeArrayAsOutParam(bValues),
5777 ComSafeArrayAsOutParam(bTimestamps),
5778 ComSafeArrayAsOutParam(bFlags));
5779 size_t i;
5780 aNames.resize(bNames.size());
5781 for (i = 0; i < bNames.size(); ++i)
5782 aNames[i] = Utf8Str(bNames[i]);
5783 aValues.resize(bValues.size());
5784 for (i = 0; i < bValues.size(); ++i)
5785 aValues[i] = Utf8Str(bValues[i]);
5786 aTimestamps.resize(bTimestamps.size());
5787 for (i = 0; i < bTimestamps.size(); ++i)
5788 aTimestamps[i] = bTimestamps[i];
5789 aFlags.resize(bFlags.size());
5790 for (i = 0; i < bFlags.size(); ++i)
5791 aFlags[i] = Utf8Str(bFlags[i]);
5792
5793 return rc;
5794}
5795#endif // VBOX_WITH_GUEST_PROPS
5796HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5797 std::vector<com::Utf8Str> &aNames,
5798 std::vector<com::Utf8Str> &aValues,
5799 std::vector<LONG64> &aTimestamps,
5800 std::vector<com::Utf8Str> &aFlags)
5801{
5802#ifndef VBOX_WITH_GUEST_PROPS
5803 ReturnComNotImplemented();
5804#else // VBOX_WITH_GUEST_PROPS
5805
5806 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5807
5808 if (rc == E_ACCESSDENIED)
5809 /* The VM is not running or the service is not (yet) accessible */
5810 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5811 return rc;
5812#endif // VBOX_WITH_GUEST_PROPS
5813}
5814
5815HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5816 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5817{
5818 MediumAttachmentList atts;
5819
5820 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5821 if (FAILED(rc)) return rc;
5822
5823 aMediumAttachments.resize(atts.size());
5824 size_t i = 0;
5825 for (MediumAttachmentList::const_iterator
5826 it = atts.begin();
5827 it != atts.end();
5828 ++it, ++i)
5829 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5830
5831 return S_OK;
5832}
5833
5834HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5835 LONG aControllerPort,
5836 LONG aDevice,
5837 ComPtr<IMediumAttachment> &aAttachment)
5838{
5839 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5840 aName.c_str(), aControllerPort, aDevice));
5841
5842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5843
5844 aAttachment = NULL;
5845
5846 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5847 aName,
5848 aControllerPort,
5849 aDevice);
5850 if (pAttach.isNull())
5851 return setError(VBOX_E_OBJECT_NOT_FOUND,
5852 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5853 aDevice, aControllerPort, aName.c_str());
5854
5855 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5856
5857 return S_OK;
5858}
5859
5860
5861HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5862 StorageBus_T aConnectionType,
5863 ComPtr<IStorageController> &aController)
5864{
5865 if ( (aConnectionType <= StorageBus_Null)
5866 || (aConnectionType > StorageBus_VirtioSCSI))
5867 return setError(E_INVALIDARG,
5868 tr("Invalid connection type: %d"),
5869 aConnectionType);
5870
5871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5872
5873 HRESULT rc = i_checkStateDependency(MutableStateDep);
5874 if (FAILED(rc)) return rc;
5875
5876 /* try to find one with the name first. */
5877 ComObjPtr<StorageController> ctrl;
5878
5879 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5880 if (SUCCEEDED(rc))
5881 return setError(VBOX_E_OBJECT_IN_USE,
5882 tr("Storage controller named '%s' already exists"),
5883 aName.c_str());
5884
5885 ctrl.createObject();
5886
5887 /* get a new instance number for the storage controller */
5888 ULONG ulInstance = 0;
5889 bool fBootable = true;
5890 for (StorageControllerList::const_iterator
5891 it = mStorageControllers->begin();
5892 it != mStorageControllers->end();
5893 ++it)
5894 {
5895 if ((*it)->i_getStorageBus() == aConnectionType)
5896 {
5897 ULONG ulCurInst = (*it)->i_getInstance();
5898
5899 if (ulCurInst >= ulInstance)
5900 ulInstance = ulCurInst + 1;
5901
5902 /* Only one controller of each type can be marked as bootable. */
5903 if ((*it)->i_getBootable())
5904 fBootable = false;
5905 }
5906 }
5907
5908 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5909 if (FAILED(rc)) return rc;
5910
5911 i_setModified(IsModified_Storage);
5912 mStorageControllers.backup();
5913 mStorageControllers->push_back(ctrl);
5914
5915 ctrl.queryInterfaceTo(aController.asOutParam());
5916
5917 /* inform the direct session if any */
5918 alock.release();
5919 i_onStorageControllerChange(i_getId(), aName);
5920
5921 return S_OK;
5922}
5923
5924HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5925 ComPtr<IStorageController> &aStorageController)
5926{
5927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5928
5929 ComObjPtr<StorageController> ctrl;
5930
5931 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5932 if (SUCCEEDED(rc))
5933 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5934
5935 return rc;
5936}
5937
5938HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5939 ULONG aInstance,
5940 ComPtr<IStorageController> &aStorageController)
5941{
5942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5943
5944 for (StorageControllerList::const_iterator
5945 it = mStorageControllers->begin();
5946 it != mStorageControllers->end();
5947 ++it)
5948 {
5949 if ( (*it)->i_getStorageBus() == aConnectionType
5950 && (*it)->i_getInstance() == aInstance)
5951 {
5952 (*it).queryInterfaceTo(aStorageController.asOutParam());
5953 return S_OK;
5954 }
5955 }
5956
5957 return setError(VBOX_E_OBJECT_NOT_FOUND,
5958 tr("Could not find a storage controller with instance number '%lu'"),
5959 aInstance);
5960}
5961
5962HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5963{
5964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5965
5966 HRESULT rc = i_checkStateDependency(MutableStateDep);
5967 if (FAILED(rc)) return rc;
5968
5969 ComObjPtr<StorageController> ctrl;
5970
5971 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5972 if (SUCCEEDED(rc))
5973 {
5974 /* Ensure that only one controller of each type is marked as bootable. */
5975 if (aBootable == TRUE)
5976 {
5977 for (StorageControllerList::const_iterator
5978 it = mStorageControllers->begin();
5979 it != mStorageControllers->end();
5980 ++it)
5981 {
5982 ComObjPtr<StorageController> aCtrl = (*it);
5983
5984 if ( (aCtrl->i_getName() != aName)
5985 && aCtrl->i_getBootable() == TRUE
5986 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5987 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5988 {
5989 aCtrl->i_setBootable(FALSE);
5990 break;
5991 }
5992 }
5993 }
5994
5995 if (SUCCEEDED(rc))
5996 {
5997 ctrl->i_setBootable(aBootable);
5998 i_setModified(IsModified_Storage);
5999 }
6000 }
6001
6002 if (SUCCEEDED(rc))
6003 {
6004 /* inform the direct session if any */
6005 alock.release();
6006 i_onStorageControllerChange(i_getId(), aName);
6007 }
6008
6009 return rc;
6010}
6011
6012HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6013{
6014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6015
6016 HRESULT rc = i_checkStateDependency(MutableStateDep);
6017 if (FAILED(rc)) return rc;
6018
6019 ComObjPtr<StorageController> ctrl;
6020 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6021 if (FAILED(rc)) return rc;
6022
6023 MediumAttachmentList llDetachedAttachments;
6024 {
6025 /* find all attached devices to the appropriate storage controller and detach them all */
6026 // make a temporary list because detachDevice invalidates iterators into
6027 // mMediumAttachments
6028 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6029
6030 for (MediumAttachmentList::const_iterator
6031 it = llAttachments2.begin();
6032 it != llAttachments2.end();
6033 ++it)
6034 {
6035 MediumAttachment *pAttachTemp = *it;
6036
6037 AutoCaller localAutoCaller(pAttachTemp);
6038 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6039
6040 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6041
6042 if (pAttachTemp->i_getControllerName() == aName)
6043 {
6044 llDetachedAttachments.push_back(pAttachTemp);
6045 rc = i_detachDevice(pAttachTemp, alock, NULL);
6046 if (FAILED(rc)) return rc;
6047 }
6048 }
6049 }
6050
6051 /* send event about detached devices before removing parent controller */
6052 for (MediumAttachmentList::const_iterator
6053 it = llDetachedAttachments.begin();
6054 it != llDetachedAttachments.end();
6055 ++it)
6056 {
6057 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6058 }
6059
6060 /* We can remove it now. */
6061 i_setModified(IsModified_Storage);
6062 mStorageControllers.backup();
6063
6064 ctrl->i_unshare();
6065
6066 mStorageControllers->remove(ctrl);
6067
6068 /* inform the direct session if any */
6069 alock.release();
6070 i_onStorageControllerChange(i_getId(), aName);
6071
6072 return S_OK;
6073}
6074
6075HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6076 ComPtr<IUSBController> &aController)
6077{
6078 if ( (aType <= USBControllerType_Null)
6079 || (aType >= USBControllerType_Last))
6080 return setError(E_INVALIDARG,
6081 tr("Invalid USB controller type: %d"),
6082 aType);
6083
6084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6085
6086 HRESULT rc = i_checkStateDependency(MutableStateDep);
6087 if (FAILED(rc)) return rc;
6088
6089 /* try to find one with the same type first. */
6090 ComObjPtr<USBController> ctrl;
6091
6092 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6093 if (SUCCEEDED(rc))
6094 return setError(VBOX_E_OBJECT_IN_USE,
6095 tr("USB controller named '%s' already exists"),
6096 aName.c_str());
6097
6098 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6099 ULONG maxInstances;
6100 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6101 if (FAILED(rc))
6102 return rc;
6103
6104 ULONG cInstances = i_getUSBControllerCountByType(aType);
6105 if (cInstances >= maxInstances)
6106 return setError(E_INVALIDARG,
6107 tr("Too many USB controllers of this type"));
6108
6109 ctrl.createObject();
6110
6111 rc = ctrl->init(this, aName, aType);
6112 if (FAILED(rc)) return rc;
6113
6114 i_setModified(IsModified_USB);
6115 mUSBControllers.backup();
6116 mUSBControllers->push_back(ctrl);
6117
6118 ctrl.queryInterfaceTo(aController.asOutParam());
6119
6120 /* inform the direct session if any */
6121 alock.release();
6122 i_onUSBControllerChange();
6123
6124 return S_OK;
6125}
6126
6127HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6128{
6129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6130
6131 ComObjPtr<USBController> ctrl;
6132
6133 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6134 if (SUCCEEDED(rc))
6135 ctrl.queryInterfaceTo(aController.asOutParam());
6136
6137 return rc;
6138}
6139
6140HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6141 ULONG *aControllers)
6142{
6143 if ( (aType <= USBControllerType_Null)
6144 || (aType >= USBControllerType_Last))
6145 return setError(E_INVALIDARG,
6146 tr("Invalid USB controller type: %d"),
6147 aType);
6148
6149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6150
6151 ComObjPtr<USBController> ctrl;
6152
6153 *aControllers = i_getUSBControllerCountByType(aType);
6154
6155 return S_OK;
6156}
6157
6158HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6159{
6160
6161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6162
6163 HRESULT rc = i_checkStateDependency(MutableStateDep);
6164 if (FAILED(rc)) return rc;
6165
6166 ComObjPtr<USBController> ctrl;
6167 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6168 if (FAILED(rc)) return rc;
6169
6170 i_setModified(IsModified_USB);
6171 mUSBControllers.backup();
6172
6173 ctrl->i_unshare();
6174
6175 mUSBControllers->remove(ctrl);
6176
6177 /* inform the direct session if any */
6178 alock.release();
6179 i_onUSBControllerChange();
6180
6181 return S_OK;
6182}
6183
6184HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6185 ULONG *aOriginX,
6186 ULONG *aOriginY,
6187 ULONG *aWidth,
6188 ULONG *aHeight,
6189 BOOL *aEnabled)
6190{
6191 uint32_t u32OriginX= 0;
6192 uint32_t u32OriginY= 0;
6193 uint32_t u32Width = 0;
6194 uint32_t u32Height = 0;
6195 uint16_t u16Flags = 0;
6196
6197 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6198 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6199 if (RT_FAILURE(vrc))
6200 {
6201#ifdef RT_OS_WINDOWS
6202 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6203 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6204 * So just assign fEnable to TRUE again.
6205 * The right fix would be to change GUI API wrappers to make sure that parameters
6206 * are changed only if API succeeds.
6207 */
6208 *aEnabled = TRUE;
6209#endif
6210 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6211 tr("Saved guest size is not available (%Rrc)"),
6212 vrc);
6213 }
6214
6215 *aOriginX = u32OriginX;
6216 *aOriginY = u32OriginY;
6217 *aWidth = u32Width;
6218 *aHeight = u32Height;
6219 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6220
6221 return S_OK;
6222}
6223
6224HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6225 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6226{
6227 if (aScreenId != 0)
6228 return E_NOTIMPL;
6229
6230 if ( aBitmapFormat != BitmapFormat_BGR0
6231 && aBitmapFormat != BitmapFormat_BGRA
6232 && aBitmapFormat != BitmapFormat_RGBA
6233 && aBitmapFormat != BitmapFormat_PNG)
6234 return setError(E_NOTIMPL,
6235 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6236
6237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6238
6239 uint8_t *pu8Data = NULL;
6240 uint32_t cbData = 0;
6241 uint32_t u32Width = 0;
6242 uint32_t u32Height = 0;
6243
6244 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6245
6246 if (RT_FAILURE(vrc))
6247 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6248 tr("Saved thumbnail data is not available (%Rrc)"),
6249 vrc);
6250
6251 HRESULT hr = S_OK;
6252
6253 *aWidth = u32Width;
6254 *aHeight = u32Height;
6255
6256 if (cbData > 0)
6257 {
6258 /* Convert pixels to the format expected by the API caller. */
6259 if (aBitmapFormat == BitmapFormat_BGR0)
6260 {
6261 /* [0] B, [1] G, [2] R, [3] 0. */
6262 aData.resize(cbData);
6263 memcpy(&aData.front(), pu8Data, cbData);
6264 }
6265 else if (aBitmapFormat == BitmapFormat_BGRA)
6266 {
6267 /* [0] B, [1] G, [2] R, [3] A. */
6268 aData.resize(cbData);
6269 for (uint32_t i = 0; i < cbData; i += 4)
6270 {
6271 aData[i] = pu8Data[i];
6272 aData[i + 1] = pu8Data[i + 1];
6273 aData[i + 2] = pu8Data[i + 2];
6274 aData[i + 3] = 0xff;
6275 }
6276 }
6277 else if (aBitmapFormat == BitmapFormat_RGBA)
6278 {
6279 /* [0] R, [1] G, [2] B, [3] A. */
6280 aData.resize(cbData);
6281 for (uint32_t i = 0; i < cbData; i += 4)
6282 {
6283 aData[i] = pu8Data[i + 2];
6284 aData[i + 1] = pu8Data[i + 1];
6285 aData[i + 2] = pu8Data[i];
6286 aData[i + 3] = 0xff;
6287 }
6288 }
6289 else if (aBitmapFormat == BitmapFormat_PNG)
6290 {
6291 uint8_t *pu8PNG = NULL;
6292 uint32_t cbPNG = 0;
6293 uint32_t cxPNG = 0;
6294 uint32_t cyPNG = 0;
6295
6296 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6297
6298 if (RT_SUCCESS(vrc))
6299 {
6300 aData.resize(cbPNG);
6301 if (cbPNG)
6302 memcpy(&aData.front(), pu8PNG, cbPNG);
6303 }
6304 else
6305 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6306 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6307 vrc);
6308
6309 RTMemFree(pu8PNG);
6310 }
6311 }
6312
6313 freeSavedDisplayScreenshot(pu8Data);
6314
6315 return hr;
6316}
6317
6318HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6319 ULONG *aWidth,
6320 ULONG *aHeight,
6321 std::vector<BitmapFormat_T> &aBitmapFormats)
6322{
6323 if (aScreenId != 0)
6324 return E_NOTIMPL;
6325
6326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6327
6328 uint8_t *pu8Data = NULL;
6329 uint32_t cbData = 0;
6330 uint32_t u32Width = 0;
6331 uint32_t u32Height = 0;
6332
6333 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6334
6335 if (RT_FAILURE(vrc))
6336 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6337 tr("Saved screenshot data is not available (%Rrc)"),
6338 vrc);
6339
6340 *aWidth = u32Width;
6341 *aHeight = u32Height;
6342 aBitmapFormats.resize(1);
6343 aBitmapFormats[0] = BitmapFormat_PNG;
6344
6345 freeSavedDisplayScreenshot(pu8Data);
6346
6347 return S_OK;
6348}
6349
6350HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6351 BitmapFormat_T aBitmapFormat,
6352 ULONG *aWidth,
6353 ULONG *aHeight,
6354 std::vector<BYTE> &aData)
6355{
6356 if (aScreenId != 0)
6357 return E_NOTIMPL;
6358
6359 if (aBitmapFormat != BitmapFormat_PNG)
6360 return E_NOTIMPL;
6361
6362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6363
6364 uint8_t *pu8Data = NULL;
6365 uint32_t cbData = 0;
6366 uint32_t u32Width = 0;
6367 uint32_t u32Height = 0;
6368
6369 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6370
6371 if (RT_FAILURE(vrc))
6372 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6373 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6374 vrc);
6375
6376 *aWidth = u32Width;
6377 *aHeight = u32Height;
6378
6379 aData.resize(cbData);
6380 if (cbData)
6381 memcpy(&aData.front(), pu8Data, cbData);
6382
6383 freeSavedDisplayScreenshot(pu8Data);
6384
6385 return S_OK;
6386}
6387
6388HRESULT Machine::hotPlugCPU(ULONG aCpu)
6389{
6390 HRESULT rc = S_OK;
6391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6392
6393 if (!mHWData->mCPUHotPlugEnabled)
6394 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6395
6396 if (aCpu >= mHWData->mCPUCount)
6397 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6398
6399 if (mHWData->mCPUAttached[aCpu])
6400 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6401
6402 alock.release();
6403 rc = i_onCPUChange(aCpu, false);
6404 alock.acquire();
6405 if (FAILED(rc)) return rc;
6406
6407 i_setModified(IsModified_MachineData);
6408 mHWData.backup();
6409 mHWData->mCPUAttached[aCpu] = true;
6410
6411 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6412 if (Global::IsOnline(mData->mMachineState))
6413 i_saveSettings(NULL);
6414
6415 return S_OK;
6416}
6417
6418HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6419{
6420 HRESULT rc = S_OK;
6421
6422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6423
6424 if (!mHWData->mCPUHotPlugEnabled)
6425 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6426
6427 if (aCpu >= SchemaDefs::MaxCPUCount)
6428 return setError(E_INVALIDARG,
6429 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6430 SchemaDefs::MaxCPUCount);
6431
6432 if (!mHWData->mCPUAttached[aCpu])
6433 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6434
6435 /* CPU 0 can't be detached */
6436 if (aCpu == 0)
6437 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6438
6439 alock.release();
6440 rc = i_onCPUChange(aCpu, true);
6441 alock.acquire();
6442 if (FAILED(rc)) return rc;
6443
6444 i_setModified(IsModified_MachineData);
6445 mHWData.backup();
6446 mHWData->mCPUAttached[aCpu] = false;
6447
6448 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6449 if (Global::IsOnline(mData->mMachineState))
6450 i_saveSettings(NULL);
6451
6452 return S_OK;
6453}
6454
6455HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6456{
6457 *aAttached = false;
6458
6459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6460
6461 /* If hotplug is enabled the CPU is always enabled. */
6462 if (!mHWData->mCPUHotPlugEnabled)
6463 {
6464 if (aCpu < mHWData->mCPUCount)
6465 *aAttached = true;
6466 }
6467 else
6468 {
6469 if (aCpu < SchemaDefs::MaxCPUCount)
6470 *aAttached = mHWData->mCPUAttached[aCpu];
6471 }
6472
6473 return S_OK;
6474}
6475
6476HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6477{
6478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 Utf8Str log = i_getLogFilename(aIdx);
6481 if (!RTFileExists(log.c_str()))
6482 log.setNull();
6483 aFilename = log;
6484
6485 return S_OK;
6486}
6487
6488HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6489{
6490 if (aSize < 0)
6491 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6492
6493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6494
6495 HRESULT rc = S_OK;
6496 Utf8Str log = i_getLogFilename(aIdx);
6497
6498 /* do not unnecessarily hold the lock while doing something which does
6499 * not need the lock and potentially takes a long time. */
6500 alock.release();
6501
6502 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6503 * keeps the SOAP reply size under 1M for the webservice (we're using
6504 * base64 encoded strings for binary data for years now, avoiding the
6505 * expansion of each byte array element to approx. 25 bytes of XML. */
6506 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6507 aData.resize(cbData);
6508
6509 RTFILE LogFile;
6510 int vrc = RTFileOpen(&LogFile, log.c_str(),
6511 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6512 if (RT_SUCCESS(vrc))
6513 {
6514 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6515 if (RT_SUCCESS(vrc))
6516 aData.resize(cbData);
6517 else
6518 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6519 tr("Could not read log file '%s' (%Rrc)"),
6520 log.c_str(), vrc);
6521 RTFileClose(LogFile);
6522 }
6523 else
6524 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6525 tr("Could not open log file '%s' (%Rrc)"),
6526 log.c_str(), vrc);
6527
6528 if (FAILED(rc))
6529 aData.resize(0);
6530
6531 return rc;
6532}
6533
6534
6535/**
6536 * Currently this method doesn't attach device to the running VM,
6537 * just makes sure it's plugged on next VM start.
6538 */
6539HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6540{
6541 // lock scope
6542 {
6543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6544
6545 HRESULT rc = i_checkStateDependency(MutableStateDep);
6546 if (FAILED(rc)) return rc;
6547
6548 ChipsetType_T aChipset = ChipsetType_PIIX3;
6549 COMGETTER(ChipsetType)(&aChipset);
6550
6551 if (aChipset != ChipsetType_ICH9)
6552 {
6553 return setError(E_INVALIDARG,
6554 tr("Host PCI attachment only supported with ICH9 chipset"));
6555 }
6556
6557 // check if device with this host PCI address already attached
6558 for (HWData::PCIDeviceAssignmentList::const_iterator
6559 it = mHWData->mPCIDeviceAssignments.begin();
6560 it != mHWData->mPCIDeviceAssignments.end();
6561 ++it)
6562 {
6563 LONG iHostAddress = -1;
6564 ComPtr<PCIDeviceAttachment> pAttach;
6565 pAttach = *it;
6566 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6567 if (iHostAddress == aHostAddress)
6568 return setError(E_INVALIDARG,
6569 tr("Device with host PCI address already attached to this VM"));
6570 }
6571
6572 ComObjPtr<PCIDeviceAttachment> pda;
6573 char name[32];
6574
6575 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6576 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6577 pda.createObject();
6578 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6579 i_setModified(IsModified_MachineData);
6580 mHWData.backup();
6581 mHWData->mPCIDeviceAssignments.push_back(pda);
6582 }
6583
6584 return S_OK;
6585}
6586
6587/**
6588 * Currently this method doesn't detach device from the running VM,
6589 * just makes sure it's not plugged on next VM start.
6590 */
6591HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6592{
6593 ComObjPtr<PCIDeviceAttachment> pAttach;
6594 bool fRemoved = false;
6595 HRESULT rc;
6596
6597 // lock scope
6598 {
6599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 rc = i_checkStateDependency(MutableStateDep);
6602 if (FAILED(rc)) return rc;
6603
6604 for (HWData::PCIDeviceAssignmentList::const_iterator
6605 it = mHWData->mPCIDeviceAssignments.begin();
6606 it != mHWData->mPCIDeviceAssignments.end();
6607 ++it)
6608 {
6609 LONG iHostAddress = -1;
6610 pAttach = *it;
6611 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6612 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6613 {
6614 i_setModified(IsModified_MachineData);
6615 mHWData.backup();
6616 mHWData->mPCIDeviceAssignments.remove(pAttach);
6617 fRemoved = true;
6618 break;
6619 }
6620 }
6621 }
6622
6623
6624 /* Fire event outside of the lock */
6625 if (fRemoved)
6626 {
6627 Assert(!pAttach.isNull());
6628 ComPtr<IEventSource> es;
6629 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6630 Assert(SUCCEEDED(rc));
6631 Bstr mid;
6632 rc = this->COMGETTER(Id)(mid.asOutParam());
6633 Assert(SUCCEEDED(rc));
6634 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6635 }
6636
6637 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6638 tr("No host PCI device %08x attached"),
6639 aHostAddress
6640 );
6641}
6642
6643HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6644{
6645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6646
6647 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6648 size_t i = 0;
6649 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6650 it = mHWData->mPCIDeviceAssignments.begin();
6651 it != mHWData->mPCIDeviceAssignments.end();
6652 ++it, ++i)
6653 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6654
6655 return S_OK;
6656}
6657
6658HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6659{
6660 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6661
6662 return S_OK;
6663}
6664
6665HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6666{
6667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6668
6669 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6670
6671 return S_OK;
6672}
6673
6674HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6675{
6676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6677 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6678 if (SUCCEEDED(hrc))
6679 {
6680 hrc = mHWData.backupEx();
6681 if (SUCCEEDED(hrc))
6682 {
6683 i_setModified(IsModified_MachineData);
6684 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6685 }
6686 }
6687 return hrc;
6688}
6689
6690HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6691{
6692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6693 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6694 return S_OK;
6695}
6696
6697HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6698{
6699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6700 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6701 if (SUCCEEDED(hrc))
6702 {
6703 hrc = mHWData.backupEx();
6704 if (SUCCEEDED(hrc))
6705 {
6706 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6707 if (SUCCEEDED(hrc))
6708 i_setModified(IsModified_MachineData);
6709 }
6710 }
6711 return hrc;
6712}
6713
6714HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6715{
6716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6719
6720 return S_OK;
6721}
6722
6723HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6724{
6725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6726 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6727 if (SUCCEEDED(hrc))
6728 {
6729 hrc = mHWData.backupEx();
6730 if (SUCCEEDED(hrc))
6731 {
6732 i_setModified(IsModified_MachineData);
6733 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6734 }
6735 }
6736 return hrc;
6737}
6738
6739HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6740{
6741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6742
6743 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6749{
6750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6751
6752 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6753 if ( SUCCEEDED(hrc)
6754 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6755 {
6756 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6757 int vrc;
6758
6759 if (aAutostartEnabled)
6760 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6761 else
6762 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6763
6764 if (RT_SUCCESS(vrc))
6765 {
6766 hrc = mHWData.backupEx();
6767 if (SUCCEEDED(hrc))
6768 {
6769 i_setModified(IsModified_MachineData);
6770 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6771 }
6772 }
6773 else if (vrc == VERR_NOT_SUPPORTED)
6774 hrc = setError(VBOX_E_NOT_SUPPORTED,
6775 tr("The VM autostart feature is not supported on this platform"));
6776 else if (vrc == VERR_PATH_NOT_FOUND)
6777 hrc = setError(E_FAIL,
6778 tr("The path to the autostart database is not set"));
6779 else
6780 hrc = setError(E_UNEXPECTED,
6781 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6782 aAutostartEnabled ? "Adding" : "Removing",
6783 mUserData->s.strName.c_str(), vrc);
6784 }
6785 return hrc;
6786}
6787
6788HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6789{
6790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6791
6792 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6793
6794 return S_OK;
6795}
6796
6797HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6798{
6799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6800 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6801 if (SUCCEEDED(hrc))
6802 {
6803 hrc = mHWData.backupEx();
6804 if (SUCCEEDED(hrc))
6805 {
6806 i_setModified(IsModified_MachineData);
6807 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6808 }
6809 }
6810 return hrc;
6811}
6812
6813HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6814{
6815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6816
6817 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6818
6819 return S_OK;
6820}
6821
6822HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6823{
6824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6825 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6826 if ( SUCCEEDED(hrc)
6827 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6828 {
6829 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6830 int vrc;
6831
6832 if (aAutostopType != AutostopType_Disabled)
6833 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6834 else
6835 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6836
6837 if (RT_SUCCESS(vrc))
6838 {
6839 hrc = mHWData.backupEx();
6840 if (SUCCEEDED(hrc))
6841 {
6842 i_setModified(IsModified_MachineData);
6843 mHWData->mAutostart.enmAutostopType = aAutostopType;
6844 }
6845 }
6846 else if (vrc == VERR_NOT_SUPPORTED)
6847 hrc = setError(VBOX_E_NOT_SUPPORTED,
6848 tr("The VM autostop feature is not supported on this platform"));
6849 else if (vrc == VERR_PATH_NOT_FOUND)
6850 hrc = setError(E_FAIL,
6851 tr("The path to the autostart database is not set"));
6852 else
6853 hrc = setError(E_UNEXPECTED,
6854 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6855 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6856 mUserData->s.strName.c_str(), vrc);
6857 }
6858 return hrc;
6859}
6860
6861HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6862{
6863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6864
6865 aDefaultFrontend = mHWData->mDefaultFrontend;
6866
6867 return S_OK;
6868}
6869
6870HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6871{
6872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6873 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6874 if (SUCCEEDED(hrc))
6875 {
6876 hrc = mHWData.backupEx();
6877 if (SUCCEEDED(hrc))
6878 {
6879 i_setModified(IsModified_MachineData);
6880 mHWData->mDefaultFrontend = aDefaultFrontend;
6881 }
6882 }
6883 return hrc;
6884}
6885
6886HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6887{
6888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6889 size_t cbIcon = mUserData->s.ovIcon.size();
6890 aIcon.resize(cbIcon);
6891 if (cbIcon)
6892 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6893 return S_OK;
6894}
6895
6896HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6897{
6898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6899 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6900 if (SUCCEEDED(hrc))
6901 {
6902 i_setModified(IsModified_MachineData);
6903 mUserData.backup();
6904 size_t cbIcon = aIcon.size();
6905 mUserData->s.ovIcon.resize(cbIcon);
6906 if (cbIcon)
6907 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6908 }
6909 return hrc;
6910}
6911
6912HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6913{
6914#ifdef VBOX_WITH_USB
6915 *aUSBProxyAvailable = true;
6916#else
6917 *aUSBProxyAvailable = false;
6918#endif
6919 return S_OK;
6920}
6921
6922HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6923{
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925
6926 *aVMProcessPriority = mUserData->s.enmVMPriority;
6927
6928 return S_OK;
6929}
6930
6931HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6932{
6933 RT_NOREF(aVMProcessPriority);
6934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6935 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6936 if (SUCCEEDED(hrc))
6937 {
6938 hrc = mUserData.backupEx();
6939 if (SUCCEEDED(hrc))
6940 {
6941 i_setModified(IsModified_MachineData);
6942 mUserData->s.enmVMPriority = aVMProcessPriority;
6943 }
6944 }
6945 alock.release();
6946 if (SUCCEEDED(hrc))
6947 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6948 return hrc;
6949}
6950
6951HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6952 ComPtr<IProgress> &aProgress)
6953{
6954 ComObjPtr<Progress> pP;
6955 Progress *ppP = pP;
6956 IProgress *iP = static_cast<IProgress *>(ppP);
6957 IProgress **pProgress = &iP;
6958
6959 IMachine *pTarget = aTarget;
6960
6961 /* Convert the options. */
6962 RTCList<CloneOptions_T> optList;
6963 if (aOptions.size())
6964 for (size_t i = 0; i < aOptions.size(); ++i)
6965 optList.append(aOptions[i]);
6966
6967 if (optList.contains(CloneOptions_Link))
6968 {
6969 if (!i_isSnapshotMachine())
6970 return setError(E_INVALIDARG,
6971 tr("Linked clone can only be created from a snapshot"));
6972 if (aMode != CloneMode_MachineState)
6973 return setError(E_INVALIDARG,
6974 tr("Linked clone can only be created for a single machine state"));
6975 }
6976 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6977
6978 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6979
6980 HRESULT rc = pWorker->start(pProgress);
6981
6982 pP = static_cast<Progress *>(*pProgress);
6983 pP.queryInterfaceTo(aProgress.asOutParam());
6984
6985 return rc;
6986
6987}
6988
6989HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6990 const com::Utf8Str &aType,
6991 ComPtr<IProgress> &aProgress)
6992{
6993 LogFlowThisFuncEnter();
6994
6995 ComObjPtr<Progress> ptrProgress;
6996 HRESULT hrc = ptrProgress.createObject();
6997 if (SUCCEEDED(hrc))
6998 {
6999 com::Utf8Str strDefaultPath;
7000 if (aTargetPath.isEmpty())
7001 i_calculateFullPath(".", strDefaultPath);
7002
7003 /* Initialize our worker task */
7004 MachineMoveVM *pTask = NULL;
7005 try
7006 {
7007 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7008 }
7009 catch (std::bad_alloc &)
7010 {
7011 return E_OUTOFMEMORY;
7012 }
7013
7014 hrc = pTask->init();//no exceptions are thrown
7015
7016 if (SUCCEEDED(hrc))
7017 {
7018 hrc = pTask->createThread();
7019 pTask = NULL; /* Consumed by createThread(). */
7020 if (SUCCEEDED(hrc))
7021 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7022 else
7023 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7024 }
7025 else
7026 delete pTask;
7027 }
7028
7029 LogFlowThisFuncLeave();
7030 return hrc;
7031
7032}
7033
7034HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7035{
7036 NOREF(aProgress);
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038
7039 // This check should always fail.
7040 HRESULT rc = i_checkStateDependency(MutableStateDep);
7041 if (FAILED(rc)) return rc;
7042
7043 AssertFailedReturn(E_NOTIMPL);
7044}
7045
7046HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7047{
7048 NOREF(aSavedStateFile);
7049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7050
7051 // This check should always fail.
7052 HRESULT rc = i_checkStateDependency(MutableStateDep);
7053 if (FAILED(rc)) return rc;
7054
7055 AssertFailedReturn(E_NOTIMPL);
7056}
7057
7058HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7059{
7060 NOREF(aFRemoveFile);
7061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7062
7063 // This check should always fail.
7064 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7065 if (FAILED(rc)) return rc;
7066
7067 AssertFailedReturn(E_NOTIMPL);
7068}
7069
7070// public methods for internal purposes
7071/////////////////////////////////////////////////////////////////////////////
7072
7073/**
7074 * Adds the given IsModified_* flag to the dirty flags of the machine.
7075 * This must be called either during i_loadSettings or under the machine write lock.
7076 * @param fl Flag
7077 * @param fAllowStateModification If state modifications are allowed.
7078 */
7079void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7080{
7081 mData->flModifications |= fl;
7082 if (fAllowStateModification && i_isStateModificationAllowed())
7083 mData->mCurrentStateModified = true;
7084}
7085
7086/**
7087 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7088 * care of the write locking.
7089 *
7090 * @param fModification The flag to add.
7091 * @param fAllowStateModification If state modifications are allowed.
7092 */
7093void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7094{
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096 i_setModified(fModification, fAllowStateModification);
7097}
7098
7099/**
7100 * Saves the registry entry of this machine to the given configuration node.
7101 *
7102 * @param data Machine registry data.
7103 *
7104 * @note locks this object for reading.
7105 */
7106HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7107{
7108 AutoLimitedCaller autoCaller(this);
7109 AssertComRCReturnRC(autoCaller.rc());
7110
7111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7112
7113 data.uuid = mData->mUuid;
7114 data.strSettingsFile = mData->m_strConfigFile;
7115
7116 return S_OK;
7117}
7118
7119/**
7120 * Calculates the absolute path of the given path taking the directory of the
7121 * machine settings file as the current directory.
7122 *
7123 * @param strPath Path to calculate the absolute path for.
7124 * @param aResult Where to put the result (used only on success, can be the
7125 * same Utf8Str instance as passed in @a aPath).
7126 * @return IPRT result.
7127 *
7128 * @note Locks this object for reading.
7129 */
7130int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7131{
7132 AutoCaller autoCaller(this);
7133 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7134
7135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7136
7137 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7138
7139 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7140
7141 strSettingsDir.stripFilename();
7142 char szFolder[RTPATH_MAX];
7143 size_t cbFolder = sizeof(szFolder);
7144 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7145 if (RT_SUCCESS(vrc))
7146 aResult = szFolder;
7147
7148 return vrc;
7149}
7150
7151/**
7152 * Copies strSource to strTarget, making it relative to the machine folder
7153 * if it is a subdirectory thereof, or simply copying it otherwise.
7154 *
7155 * @param strSource Path to evaluate and copy.
7156 * @param strTarget Buffer to receive target path.
7157 *
7158 * @note Locks this object for reading.
7159 */
7160void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7161 Utf8Str &strTarget)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturn(autoCaller.rc(), (void)0);
7165
7166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7167
7168 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7169 // use strTarget as a temporary buffer to hold the machine settings dir
7170 strTarget = mData->m_strConfigFileFull;
7171 strTarget.stripFilename();
7172 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7173 {
7174 // is relative: then append what's left
7175 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7176 // for empty paths (only possible for subdirs) use "." to avoid
7177 // triggering default settings for not present config attributes.
7178 if (strTarget.isEmpty())
7179 strTarget = ".";
7180 }
7181 else
7182 // is not relative: then overwrite
7183 strTarget = strSource;
7184}
7185
7186/**
7187 * Returns the full path to the machine's log folder in the
7188 * \a aLogFolder argument.
7189 */
7190void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7191{
7192 AutoCaller autoCaller(this);
7193 AssertComRCReturnVoid(autoCaller.rc());
7194
7195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 char szTmp[RTPATH_MAX];
7198 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7199 if (RT_SUCCESS(vrc))
7200 {
7201 if (szTmp[0] && !mUserData.isNull())
7202 {
7203 char szTmp2[RTPATH_MAX];
7204 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7205 if (RT_SUCCESS(vrc))
7206 aLogFolder.printf("%s%c%s",
7207 szTmp2,
7208 RTPATH_DELIMITER,
7209 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7210 }
7211 else
7212 vrc = VERR_PATH_IS_RELATIVE;
7213 }
7214
7215 if (RT_FAILURE(vrc))
7216 {
7217 // fallback if VBOX_USER_LOGHOME is not set or invalid
7218 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7219 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7220 aLogFolder.append(RTPATH_DELIMITER);
7221 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7222 }
7223}
7224
7225/**
7226 * Returns the full path to the machine's log file for an given index.
7227 */
7228Utf8Str Machine::i_getLogFilename(ULONG idx)
7229{
7230 Utf8Str logFolder;
7231 getLogFolder(logFolder);
7232 Assert(logFolder.length());
7233
7234 Utf8Str log;
7235 if (idx == 0)
7236 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7237#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7238 else if (idx == 1)
7239 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7240 else
7241 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7242#else
7243 else
7244 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7245#endif
7246 return log;
7247}
7248
7249/**
7250 * Returns the full path to the machine's hardened log file.
7251 */
7252Utf8Str Machine::i_getHardeningLogFilename(void)
7253{
7254 Utf8Str strFilename;
7255 getLogFolder(strFilename);
7256 Assert(strFilename.length());
7257 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7258 return strFilename;
7259}
7260
7261/**
7262 * Returns the default NVRAM filename based on the location of the VM config.
7263 * Note that this is a relative path.
7264 */
7265Utf8Str Machine::i_getDefaultNVRAMFilename()
7266{
7267 AutoCaller autoCaller(this);
7268 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7269
7270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7271
7272 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7273 || i_isSnapshotMachine())
7274 return Utf8Str::Empty;
7275
7276 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7277 strNVRAMFilePath.stripPath();
7278 strNVRAMFilePath.stripSuffix();
7279 strNVRAMFilePath += ".nvram";
7280
7281 return strNVRAMFilePath;
7282}
7283
7284/**
7285 * Returns the NVRAM filename for a new snapshot. This intentionally works
7286 * similarly to the saved state file naming. Note that this is usually
7287 * a relative path, unless the snapshot folder is absolute.
7288 */
7289Utf8Str Machine::i_getSnapshotNVRAMFilename()
7290{
7291 AutoCaller autoCaller(this);
7292 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7293
7294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7295
7296 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7297 return Utf8Str::Empty;
7298
7299 RTTIMESPEC ts;
7300 RTTimeNow(&ts);
7301 RTTIME time;
7302 RTTimeExplode(&time, &ts);
7303
7304 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7305 strNVRAMFilePath += RTPATH_DELIMITER;
7306 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7307 time.i32Year, time.u8Month, time.u8MonthDay,
7308 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7309
7310 return strNVRAMFilePath;
7311}
7312
7313/**
7314 * Composes a unique saved state filename based on the current system time. The filename is
7315 * granular to the second so this will work so long as no more than one snapshot is taken on
7316 * a machine per second.
7317 *
7318 * Before version 4.1, we used this formula for saved state files:
7319 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7320 * which no longer works because saved state files can now be shared between the saved state of the
7321 * "saved" machine and an online snapshot, and the following would cause problems:
7322 * 1) save machine
7323 * 2) create online snapshot from that machine state --> reusing saved state file
7324 * 3) save machine again --> filename would be reused, breaking the online snapshot
7325 *
7326 * So instead we now use a timestamp.
7327 *
7328 * @param strStateFilePath
7329 */
7330
7331void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7332{
7333 AutoCaller autoCaller(this);
7334 AssertComRCReturnVoid(autoCaller.rc());
7335
7336 {
7337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7338 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7339 }
7340
7341 RTTIMESPEC ts;
7342 RTTimeNow(&ts);
7343 RTTIME time;
7344 RTTimeExplode(&time, &ts);
7345
7346 strStateFilePath += RTPATH_DELIMITER;
7347 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7348 time.i32Year, time.u8Month, time.u8MonthDay,
7349 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7350}
7351
7352/**
7353 * Returns whether at least one USB controller is present for the VM.
7354 */
7355bool Machine::i_isUSBControllerPresent()
7356{
7357 AutoCaller autoCaller(this);
7358 AssertComRCReturn(autoCaller.rc(), false);
7359
7360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7361
7362 return (mUSBControllers->size() > 0);
7363}
7364
7365
7366/**
7367 * @note Locks this object for writing, calls the client process
7368 * (inside the lock).
7369 */
7370HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7371 const Utf8Str &strFrontend,
7372 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7373 ProgressProxy *aProgress)
7374{
7375 LogFlowThisFuncEnter();
7376
7377 AssertReturn(aControl, E_FAIL);
7378 AssertReturn(aProgress, E_FAIL);
7379 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7380
7381 AutoCaller autoCaller(this);
7382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7383
7384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7385
7386 if (!mData->mRegistered)
7387 return setError(E_UNEXPECTED,
7388 tr("The machine '%s' is not registered"),
7389 mUserData->s.strName.c_str());
7390
7391 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7392
7393 /* The process started when launching a VM with separate UI/VM processes is always
7394 * the UI process, i.e. needs special handling as it won't claim the session. */
7395 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7396
7397 if (fSeparate)
7398 {
7399 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7400 return setError(VBOX_E_INVALID_OBJECT_STATE,
7401 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7402 mUserData->s.strName.c_str());
7403 }
7404 else
7405 {
7406 if ( mData->mSession.mState == SessionState_Locked
7407 || mData->mSession.mState == SessionState_Spawning
7408 || mData->mSession.mState == SessionState_Unlocking)
7409 return setError(VBOX_E_INVALID_OBJECT_STATE,
7410 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7411 mUserData->s.strName.c_str());
7412
7413 /* may not be busy */
7414 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7415 }
7416
7417 /* Hardening logging */
7418#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7419 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7420 {
7421 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7422 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7423 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7424 {
7425 Utf8Str strStartupLogDir = strHardeningLogFile;
7426 strStartupLogDir.stripFilename();
7427 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7428 file without stripping the file. */
7429 }
7430 strSupHardeningLogArg.append(strHardeningLogFile);
7431
7432 /* Remove legacy log filename to avoid confusion. */
7433 Utf8Str strOldStartupLogFile;
7434 getLogFolder(strOldStartupLogFile);
7435 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7436 RTFileDelete(strOldStartupLogFile.c_str());
7437 }
7438#else
7439 Utf8Str strSupHardeningLogArg;
7440#endif
7441
7442 Utf8Str strAppOverride;
7443#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7444 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7445#endif
7446
7447 bool fUseVBoxSDS = false;
7448 Utf8Str strCanonicalName;
7449 if (false)
7450 { }
7451#ifdef VBOX_WITH_QTGUI
7452 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7453 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7454 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7455 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7456 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7457 {
7458 strCanonicalName = "GUI/Qt";
7459 fUseVBoxSDS = true;
7460 }
7461#endif
7462#ifdef VBOX_WITH_VBOXSDL
7463 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7464 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7465 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7466 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7467 {
7468 strCanonicalName = "GUI/SDL";
7469 fUseVBoxSDS = true;
7470 }
7471#endif
7472#ifdef VBOX_WITH_HEADLESS
7473 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7474 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7475 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7476 {
7477 strCanonicalName = "headless";
7478 }
7479#endif
7480 else
7481 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7482
7483 Utf8Str idStr = mData->mUuid.toString();
7484 Utf8Str const &strMachineName = mUserData->s.strName;
7485 RTPROCESS pid = NIL_RTPROCESS;
7486
7487#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7488 RT_NOREF(fUseVBoxSDS);
7489#else
7490 DWORD idCallerSession = ~(DWORD)0;
7491 if (fUseVBoxSDS)
7492 {
7493 /*
7494 * The VBoxSDS should be used for process launching the VM with
7495 * GUI only if the caller and the VBoxSDS are in different Windows
7496 * sessions and the caller in the interactive one.
7497 */
7498 fUseVBoxSDS = false;
7499
7500 /* Get windows session of the current process. The process token used
7501 due to several reasons:
7502 1. The token is absent for the current thread except someone set it
7503 for us.
7504 2. Needs to get the id of the session where the process is started.
7505 We only need to do this once, though. */
7506 static DWORD s_idCurrentSession = ~(DWORD)0;
7507 DWORD idCurrentSession = s_idCurrentSession;
7508 if (idCurrentSession == ~(DWORD)0)
7509 {
7510 HANDLE hCurrentProcessToken = NULL;
7511 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7512 {
7513 DWORD cbIgn = 0;
7514 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7515 s_idCurrentSession = idCurrentSession;
7516 else
7517 {
7518 idCurrentSession = ~(DWORD)0;
7519 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7520 }
7521 CloseHandle(hCurrentProcessToken);
7522 }
7523 else
7524 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7525 }
7526
7527 /* get the caller's session */
7528 HRESULT hrc = CoImpersonateClient();
7529 if (SUCCEEDED(hrc))
7530 {
7531 HANDLE hCallerThreadToken;
7532 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7533 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7534 &hCallerThreadToken))
7535 {
7536 SetLastError(NO_ERROR);
7537 DWORD cbIgn = 0;
7538 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7539 {
7540 /* Only need to use SDS if the session ID differs: */
7541 if (idCurrentSession != idCallerSession)
7542 {
7543 fUseVBoxSDS = false;
7544
7545 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7546 DWORD cbTokenGroups = 0;
7547 PTOKEN_GROUPS pTokenGroups = NULL;
7548 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7549 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7550 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7551 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7552 {
7553 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7554 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7555 PSID pInteractiveSid = NULL;
7556 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7557 {
7558 /* Iterate over the groups looking for the interactive SID: */
7559 fUseVBoxSDS = false;
7560 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7561 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7562 {
7563 fUseVBoxSDS = true;
7564 break;
7565 }
7566 FreeSid(pInteractiveSid);
7567 }
7568 }
7569 else
7570 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7571 RTMemTmpFree(pTokenGroups);
7572 }
7573 }
7574 else
7575 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7576 CloseHandle(hCallerThreadToken);
7577 }
7578 else
7579 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7580 CoRevertToSelf();
7581 }
7582 else
7583 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7584 }
7585 if (fUseVBoxSDS)
7586 {
7587 /* connect to VBoxSDS */
7588 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7589 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7590 if (FAILED(rc))
7591 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7592 strMachineName.c_str());
7593
7594 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7595 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7596 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7597 service to access the files. */
7598 rc = CoSetProxyBlanket(pVBoxSDS,
7599 RPC_C_AUTHN_DEFAULT,
7600 RPC_C_AUTHZ_DEFAULT,
7601 COLE_DEFAULT_PRINCIPAL,
7602 RPC_C_AUTHN_LEVEL_DEFAULT,
7603 RPC_C_IMP_LEVEL_IMPERSONATE,
7604 NULL,
7605 EOAC_DEFAULT);
7606 if (FAILED(rc))
7607 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7608
7609 size_t const cEnvVars = aEnvironmentChanges.size();
7610 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7611 for (size_t i = 0; i < cEnvVars; i++)
7612 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7613
7614 ULONG uPid = 0;
7615 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7616 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7617 idCallerSession, &uPid);
7618 if (FAILED(rc))
7619 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7620 pid = (RTPROCESS)uPid;
7621 }
7622 else
7623#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7624 {
7625 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7626 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7627 if (RT_FAILURE(vrc))
7628 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7629 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7630 }
7631
7632 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7633 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7634
7635 if (!fSeparate)
7636 {
7637 /*
7638 * Note that we don't release the lock here before calling the client,
7639 * because it doesn't need to call us back if called with a NULL argument.
7640 * Releasing the lock here is dangerous because we didn't prepare the
7641 * launch data yet, but the client we've just started may happen to be
7642 * too fast and call LockMachine() that will fail (because of PID, etc.),
7643 * so that the Machine will never get out of the Spawning session state.
7644 */
7645
7646 /* inform the session that it will be a remote one */
7647 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7648#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7649 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7650#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7651 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7652#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7653 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7654
7655 if (FAILED(rc))
7656 {
7657 /* restore the session state */
7658 mData->mSession.mState = SessionState_Unlocked;
7659 alock.release();
7660 mParent->i_addProcessToReap(pid);
7661 /* The failure may occur w/o any error info (from RPC), so provide one */
7662 return setError(VBOX_E_VM_ERROR,
7663 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7664 }
7665
7666 /* attach launch data to the machine */
7667 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7668 mData->mSession.mRemoteControls.push_back(aControl);
7669 mData->mSession.mProgress = aProgress;
7670 mData->mSession.mPID = pid;
7671 mData->mSession.mState = SessionState_Spawning;
7672 Assert(strCanonicalName.isNotEmpty());
7673 mData->mSession.mName = strCanonicalName;
7674 }
7675 else
7676 {
7677 /* For separate UI process we declare the launch as completed instantly, as the
7678 * actual headless VM start may or may not come. No point in remembering anything
7679 * yet, as what matters for us is when the headless VM gets started. */
7680 aProgress->i_notifyComplete(S_OK);
7681 }
7682
7683 alock.release();
7684 mParent->i_addProcessToReap(pid);
7685
7686 LogFlowThisFuncLeave();
7687 return S_OK;
7688}
7689
7690/**
7691 * Returns @c true if the given session machine instance has an open direct
7692 * session (and optionally also for direct sessions which are closing) and
7693 * returns the session control machine instance if so.
7694 *
7695 * Note that when the method returns @c false, the arguments remain unchanged.
7696 *
7697 * @param aMachine Session machine object.
7698 * @param aControl Direct session control object (optional).
7699 * @param aRequireVM If true then only allow VM sessions.
7700 * @param aAllowClosing If true then additionally a session which is currently
7701 * being closed will also be allowed.
7702 *
7703 * @note locks this object for reading.
7704 */
7705bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7706 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7707 bool aRequireVM /*= false*/,
7708 bool aAllowClosing /*= false*/)
7709{
7710 AutoLimitedCaller autoCaller(this);
7711 AssertComRCReturn(autoCaller.rc(), false);
7712
7713 /* just return false for inaccessible machines */
7714 if (getObjectState().getState() != ObjectState::Ready)
7715 return false;
7716
7717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7718
7719 if ( ( mData->mSession.mState == SessionState_Locked
7720 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7721 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7722 )
7723 {
7724 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7725
7726 aMachine = mData->mSession.mMachine;
7727
7728 if (aControl != NULL)
7729 *aControl = mData->mSession.mDirectControl;
7730
7731 return true;
7732 }
7733
7734 return false;
7735}
7736
7737/**
7738 * Returns @c true if the given machine has an spawning direct session.
7739 *
7740 * @note locks this object for reading.
7741 */
7742bool Machine::i_isSessionSpawning()
7743{
7744 AutoLimitedCaller autoCaller(this);
7745 AssertComRCReturn(autoCaller.rc(), false);
7746
7747 /* just return false for inaccessible machines */
7748 if (getObjectState().getState() != ObjectState::Ready)
7749 return false;
7750
7751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7752
7753 if (mData->mSession.mState == SessionState_Spawning)
7754 return true;
7755
7756 return false;
7757}
7758
7759/**
7760 * Called from the client watcher thread to check for unexpected client process
7761 * death during Session_Spawning state (e.g. before it successfully opened a
7762 * direct session).
7763 *
7764 * On Win32 and on OS/2, this method is called only when we've got the
7765 * direct client's process termination notification, so it always returns @c
7766 * true.
7767 *
7768 * On other platforms, this method returns @c true if the client process is
7769 * terminated and @c false if it's still alive.
7770 *
7771 * @note Locks this object for writing.
7772 */
7773bool Machine::i_checkForSpawnFailure()
7774{
7775 AutoCaller autoCaller(this);
7776 if (!autoCaller.isOk())
7777 {
7778 /* nothing to do */
7779 LogFlowThisFunc(("Already uninitialized!\n"));
7780 return true;
7781 }
7782
7783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7784
7785 if (mData->mSession.mState != SessionState_Spawning)
7786 {
7787 /* nothing to do */
7788 LogFlowThisFunc(("Not spawning any more!\n"));
7789 return true;
7790 }
7791
7792 HRESULT rc = S_OK;
7793
7794 /* PID not yet initialized, skip check. */
7795 if (mData->mSession.mPID == NIL_RTPROCESS)
7796 return false;
7797
7798 RTPROCSTATUS status;
7799 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7800
7801 if (vrc != VERR_PROCESS_RUNNING)
7802 {
7803 Utf8Str strExtraInfo;
7804
7805#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7806 /* If the startup logfile exists and is of non-zero length, tell the
7807 user to look there for more details to encourage them to attach it
7808 when reporting startup issues. */
7809 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7810 uint64_t cbStartupLogFile = 0;
7811 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7812 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7813 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7814#endif
7815
7816 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7817 rc = setError(E_FAIL,
7818 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7819 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7820 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7821 rc = setError(E_FAIL,
7822 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7823 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7824 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7825 rc = setError(E_FAIL,
7826 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7827 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7828 else
7829 rc = setErrorBoth(E_FAIL, vrc,
7830 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7831 i_getName().c_str(), vrc, strExtraInfo.c_str());
7832 }
7833
7834 if (FAILED(rc))
7835 {
7836 /* Close the remote session, remove the remote control from the list
7837 * and reset session state to Closed (@note keep the code in sync with
7838 * the relevant part in LockMachine()). */
7839
7840 Assert(mData->mSession.mRemoteControls.size() == 1);
7841 if (mData->mSession.mRemoteControls.size() == 1)
7842 {
7843 ErrorInfoKeeper eik;
7844 mData->mSession.mRemoteControls.front()->Uninitialize();
7845 }
7846
7847 mData->mSession.mRemoteControls.clear();
7848 mData->mSession.mState = SessionState_Unlocked;
7849
7850 /* finalize the progress after setting the state */
7851 if (!mData->mSession.mProgress.isNull())
7852 {
7853 mData->mSession.mProgress->notifyComplete(rc);
7854 mData->mSession.mProgress.setNull();
7855 }
7856
7857 mData->mSession.mPID = NIL_RTPROCESS;
7858
7859 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7860 return true;
7861 }
7862
7863 return false;
7864}
7865
7866/**
7867 * Checks whether the machine can be registered. If so, commits and saves
7868 * all settings.
7869 *
7870 * @note Must be called from mParent's write lock. Locks this object and
7871 * children for writing.
7872 */
7873HRESULT Machine::i_prepareRegister()
7874{
7875 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7876
7877 AutoLimitedCaller autoCaller(this);
7878 AssertComRCReturnRC(autoCaller.rc());
7879
7880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7881
7882 /* wait for state dependents to drop to zero */
7883 i_ensureNoStateDependencies();
7884
7885 if (!mData->mAccessible)
7886 return setError(VBOX_E_INVALID_OBJECT_STATE,
7887 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7888 mUserData->s.strName.c_str(),
7889 mData->mUuid.toString().c_str());
7890
7891 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7892
7893 if (mData->mRegistered)
7894 return setError(VBOX_E_INVALID_OBJECT_STATE,
7895 tr("The machine '%s' with UUID {%s} is already registered"),
7896 mUserData->s.strName.c_str(),
7897 mData->mUuid.toString().c_str());
7898
7899 HRESULT rc = S_OK;
7900
7901 // Ensure the settings are saved. If we are going to be registered and
7902 // no config file exists yet, create it by calling i_saveSettings() too.
7903 if ( (mData->flModifications)
7904 || (!mData->pMachineConfigFile->fileExists())
7905 )
7906 {
7907 rc = i_saveSettings(NULL);
7908 // no need to check whether VirtualBox.xml needs saving too since
7909 // we can't have a machine XML file rename pending
7910 if (FAILED(rc)) return rc;
7911 }
7912
7913 /* more config checking goes here */
7914
7915 if (SUCCEEDED(rc))
7916 {
7917 /* we may have had implicit modifications we want to fix on success */
7918 i_commit();
7919
7920 mData->mRegistered = true;
7921 }
7922 else
7923 {
7924 /* we may have had implicit modifications we want to cancel on failure*/
7925 i_rollback(false /* aNotify */);
7926 }
7927
7928 return rc;
7929}
7930
7931/**
7932 * Increases the number of objects dependent on the machine state or on the
7933 * registered state. Guarantees that these two states will not change at least
7934 * until #i_releaseStateDependency() is called.
7935 *
7936 * Depending on the @a aDepType value, additional state checks may be made.
7937 * These checks will set extended error info on failure. See
7938 * #i_checkStateDependency() for more info.
7939 *
7940 * If this method returns a failure, the dependency is not added and the caller
7941 * is not allowed to rely on any particular machine state or registration state
7942 * value and may return the failed result code to the upper level.
7943 *
7944 * @param aDepType Dependency type to add.
7945 * @param aState Current machine state (NULL if not interested).
7946 * @param aRegistered Current registered state (NULL if not interested).
7947 *
7948 * @note Locks this object for writing.
7949 */
7950HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7951 MachineState_T *aState /* = NULL */,
7952 BOOL *aRegistered /* = NULL */)
7953{
7954 AutoCaller autoCaller(this);
7955 AssertComRCReturnRC(autoCaller.rc());
7956
7957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7958
7959 HRESULT rc = i_checkStateDependency(aDepType);
7960 if (FAILED(rc)) return rc;
7961
7962 {
7963 if (mData->mMachineStateChangePending != 0)
7964 {
7965 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7966 * drop to zero so don't add more. It may make sense to wait a bit
7967 * and retry before reporting an error (since the pending state
7968 * transition should be really quick) but let's just assert for
7969 * now to see if it ever happens on practice. */
7970
7971 AssertFailed();
7972
7973 return setError(E_ACCESSDENIED,
7974 tr("Machine state change is in progress. Please retry the operation later."));
7975 }
7976
7977 ++mData->mMachineStateDeps;
7978 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7979 }
7980
7981 if (aState)
7982 *aState = mData->mMachineState;
7983 if (aRegistered)
7984 *aRegistered = mData->mRegistered;
7985
7986 return S_OK;
7987}
7988
7989/**
7990 * Decreases the number of objects dependent on the machine state.
7991 * Must always complete the #i_addStateDependency() call after the state
7992 * dependency is no more necessary.
7993 */
7994void Machine::i_releaseStateDependency()
7995{
7996 AutoCaller autoCaller(this);
7997 AssertComRCReturnVoid(autoCaller.rc());
7998
7999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8000
8001 /* releaseStateDependency() w/o addStateDependency()? */
8002 AssertReturnVoid(mData->mMachineStateDeps != 0);
8003 -- mData->mMachineStateDeps;
8004
8005 if (mData->mMachineStateDeps == 0)
8006 {
8007 /* inform i_ensureNoStateDependencies() that there are no more deps */
8008 if (mData->mMachineStateChangePending != 0)
8009 {
8010 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8011 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8012 }
8013 }
8014}
8015
8016Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8017{
8018 /* start with nothing found */
8019 Utf8Str strResult("");
8020
8021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8022
8023 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8024 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8025 // found:
8026 strResult = it->second; // source is a Utf8Str
8027
8028 return strResult;
8029}
8030
8031// protected methods
8032/////////////////////////////////////////////////////////////////////////////
8033
8034/**
8035 * Performs machine state checks based on the @a aDepType value. If a check
8036 * fails, this method will set extended error info, otherwise it will return
8037 * S_OK. It is supposed, that on failure, the caller will immediately return
8038 * the return value of this method to the upper level.
8039 *
8040 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8041 *
8042 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8043 * current state of this machine object allows to change settings of the
8044 * machine (i.e. the machine is not registered, or registered but not running
8045 * and not saved). It is useful to call this method from Machine setters
8046 * before performing any change.
8047 *
8048 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8049 * as for MutableStateDep except that if the machine is saved, S_OK is also
8050 * returned. This is useful in setters which allow changing machine
8051 * properties when it is in the saved state.
8052 *
8053 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8054 * if the current state of this machine object allows to change runtime
8055 * changeable settings of the machine (i.e. the machine is not registered, or
8056 * registered but either running or not running and not saved). It is useful
8057 * to call this method from Machine setters before performing any changes to
8058 * runtime changeable settings.
8059 *
8060 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8061 * the same as for MutableOrRunningStateDep except that if the machine is
8062 * saved, S_OK is also returned. This is useful in setters which allow
8063 * changing runtime and saved state changeable machine properties.
8064 *
8065 * @param aDepType Dependency type to check.
8066 *
8067 * @note Non Machine based classes should use #i_addStateDependency() and
8068 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8069 * template.
8070 *
8071 * @note This method must be called from under this object's read or write
8072 * lock.
8073 */
8074HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8075{
8076 switch (aDepType)
8077 {
8078 case AnyStateDep:
8079 {
8080 break;
8081 }
8082 case MutableStateDep:
8083 {
8084 if ( mData->mRegistered
8085 && ( !i_isSessionMachine()
8086 || ( mData->mMachineState != MachineState_Aborted
8087 && mData->mMachineState != MachineState_Teleported
8088 && mData->mMachineState != MachineState_PoweredOff
8089 )
8090 )
8091 )
8092 return setError(VBOX_E_INVALID_VM_STATE,
8093 tr("The machine is not mutable (state is %s)"),
8094 Global::stringifyMachineState(mData->mMachineState));
8095 break;
8096 }
8097 case MutableOrSavedStateDep:
8098 {
8099 if ( mData->mRegistered
8100 && ( !i_isSessionMachine()
8101 || ( mData->mMachineState != MachineState_Aborted
8102 && mData->mMachineState != MachineState_Teleported
8103 && mData->mMachineState != MachineState_Saved
8104 && mData->mMachineState != MachineState_AbortedSaved
8105 && mData->mMachineState != MachineState_PoweredOff
8106 )
8107 )
8108 )
8109 return setError(VBOX_E_INVALID_VM_STATE,
8110 tr("The machine is not mutable or saved (state is %s)"),
8111 Global::stringifyMachineState(mData->mMachineState));
8112 break;
8113 }
8114 case MutableOrRunningStateDep:
8115 {
8116 if ( mData->mRegistered
8117 && ( !i_isSessionMachine()
8118 || ( mData->mMachineState != MachineState_Aborted
8119 && mData->mMachineState != MachineState_Teleported
8120 && mData->mMachineState != MachineState_PoweredOff
8121 && !Global::IsOnline(mData->mMachineState)
8122 )
8123 )
8124 )
8125 return setError(VBOX_E_INVALID_VM_STATE,
8126 tr("The machine is not mutable or running (state is %s)"),
8127 Global::stringifyMachineState(mData->mMachineState));
8128 break;
8129 }
8130 case MutableOrSavedOrRunningStateDep:
8131 {
8132 if ( mData->mRegistered
8133 && ( !i_isSessionMachine()
8134 || ( mData->mMachineState != MachineState_Aborted
8135 && mData->mMachineState != MachineState_Teleported
8136 && mData->mMachineState != MachineState_Saved
8137 && mData->mMachineState != MachineState_AbortedSaved
8138 && mData->mMachineState != MachineState_PoweredOff
8139 && !Global::IsOnline(mData->mMachineState)
8140 )
8141 )
8142 )
8143 return setError(VBOX_E_INVALID_VM_STATE,
8144 tr("The machine is not mutable, saved or running (state is %s)"),
8145 Global::stringifyMachineState(mData->mMachineState));
8146 break;
8147 }
8148 }
8149
8150 return S_OK;
8151}
8152
8153/**
8154 * Helper to initialize all associated child objects and allocate data
8155 * structures.
8156 *
8157 * This method must be called as a part of the object's initialization procedure
8158 * (usually done in the #init() method).
8159 *
8160 * @note Must be called only from #init() or from #i_registeredInit().
8161 */
8162HRESULT Machine::initDataAndChildObjects()
8163{
8164 AutoCaller autoCaller(this);
8165 AssertComRCReturnRC(autoCaller.rc());
8166 AssertReturn( getObjectState().getState() == ObjectState::InInit
8167 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8168
8169 AssertReturn(!mData->mAccessible, E_FAIL);
8170
8171 /* allocate data structures */
8172 mSSData.allocate();
8173 mUserData.allocate();
8174 mHWData.allocate();
8175 mMediumAttachments.allocate();
8176 mStorageControllers.allocate();
8177 mUSBControllers.allocate();
8178
8179 /* initialize mOSTypeId */
8180 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8181
8182/** @todo r=bird: init() methods never fails, right? Why don't we make them
8183 * return void then! */
8184
8185 /* create associated BIOS settings object */
8186 unconst(mBIOSSettings).createObject();
8187 mBIOSSettings->init(this);
8188
8189 /* create associated trusted platform module object */
8190 unconst(mTrustedPlatformModule).createObject();
8191 mTrustedPlatformModule->init(this);
8192
8193 /* create associated NVRAM store object */
8194 unconst(mNvramStore).createObject();
8195 mNvramStore->init(this);
8196
8197 /* create associated record settings object */
8198 unconst(mRecordingSettings).createObject();
8199 mRecordingSettings->init(this);
8200
8201 /* create the graphics adapter object (always present) */
8202 unconst(mGraphicsAdapter).createObject();
8203 mGraphicsAdapter->init(this);
8204
8205 /* create an associated VRDE object (default is disabled) */
8206 unconst(mVRDEServer).createObject();
8207 mVRDEServer->init(this);
8208
8209 /* create associated serial port objects */
8210 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8211 {
8212 unconst(mSerialPorts[slot]).createObject();
8213 mSerialPorts[slot]->init(this, slot);
8214 }
8215
8216 /* create associated parallel port objects */
8217 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8218 {
8219 unconst(mParallelPorts[slot]).createObject();
8220 mParallelPorts[slot]->init(this, slot);
8221 }
8222
8223 /* create the audio adapter object (always present, default is disabled) */
8224 unconst(mAudioAdapter).createObject();
8225 mAudioAdapter->init(this);
8226
8227 /* create the USB device filters object (always present) */
8228 unconst(mUSBDeviceFilters).createObject();
8229 mUSBDeviceFilters->init(this);
8230
8231 /* create associated network adapter objects */
8232 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8233 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8234 {
8235 unconst(mNetworkAdapters[slot]).createObject();
8236 mNetworkAdapters[slot]->init(this, slot);
8237 }
8238
8239 /* create the bandwidth control */
8240 unconst(mBandwidthControl).createObject();
8241 mBandwidthControl->init(this);
8242
8243 return S_OK;
8244}
8245
8246/**
8247 * Helper to uninitialize all associated child objects and to free all data
8248 * structures.
8249 *
8250 * This method must be called as a part of the object's uninitialization
8251 * procedure (usually done in the #uninit() method).
8252 *
8253 * @note Must be called only from #uninit() or from #i_registeredInit().
8254 */
8255void Machine::uninitDataAndChildObjects()
8256{
8257 AutoCaller autoCaller(this);
8258 AssertComRCReturnVoid(autoCaller.rc());
8259 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8260 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8261 || getObjectState().getState() == ObjectState::InUninit
8262 || getObjectState().getState() == ObjectState::Limited);
8263
8264 /* tell all our other child objects we've been uninitialized */
8265 if (mBandwidthControl)
8266 {
8267 mBandwidthControl->uninit();
8268 unconst(mBandwidthControl).setNull();
8269 }
8270
8271 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8272 {
8273 if (mNetworkAdapters[slot])
8274 {
8275 mNetworkAdapters[slot]->uninit();
8276 unconst(mNetworkAdapters[slot]).setNull();
8277 }
8278 }
8279
8280 if (mUSBDeviceFilters)
8281 {
8282 mUSBDeviceFilters->uninit();
8283 unconst(mUSBDeviceFilters).setNull();
8284 }
8285
8286 if (mAudioAdapter)
8287 {
8288 mAudioAdapter->uninit();
8289 unconst(mAudioAdapter).setNull();
8290 }
8291
8292 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8293 {
8294 if (mParallelPorts[slot])
8295 {
8296 mParallelPorts[slot]->uninit();
8297 unconst(mParallelPorts[slot]).setNull();
8298 }
8299 }
8300
8301 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8302 {
8303 if (mSerialPorts[slot])
8304 {
8305 mSerialPorts[slot]->uninit();
8306 unconst(mSerialPorts[slot]).setNull();
8307 }
8308 }
8309
8310 if (mVRDEServer)
8311 {
8312 mVRDEServer->uninit();
8313 unconst(mVRDEServer).setNull();
8314 }
8315
8316 if (mGraphicsAdapter)
8317 {
8318 mGraphicsAdapter->uninit();
8319 unconst(mGraphicsAdapter).setNull();
8320 }
8321
8322 if (mBIOSSettings)
8323 {
8324 mBIOSSettings->uninit();
8325 unconst(mBIOSSettings).setNull();
8326 }
8327
8328 if (mTrustedPlatformModule)
8329 {
8330 mTrustedPlatformModule->uninit();
8331 unconst(mTrustedPlatformModule).setNull();
8332 }
8333
8334 if (mNvramStore)
8335 {
8336 mNvramStore->uninit();
8337 unconst(mNvramStore).setNull();
8338 }
8339
8340 if (mRecordingSettings)
8341 {
8342 mRecordingSettings->uninit();
8343 unconst(mRecordingSettings).setNull();
8344 }
8345
8346 /* Deassociate media (only when a real Machine or a SnapshotMachine
8347 * instance is uninitialized; SessionMachine instances refer to real
8348 * Machine media). This is necessary for a clean re-initialization of
8349 * the VM after successfully re-checking the accessibility state. Note
8350 * that in case of normal Machine or SnapshotMachine uninitialization (as
8351 * a result of unregistering or deleting the snapshot), outdated media
8352 * attachments will already be uninitialized and deleted, so this
8353 * code will not affect them. */
8354 if ( !mMediumAttachments.isNull()
8355 && !i_isSessionMachine()
8356 )
8357 {
8358 for (MediumAttachmentList::const_iterator
8359 it = mMediumAttachments->begin();
8360 it != mMediumAttachments->end();
8361 ++it)
8362 {
8363 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8364 if (pMedium.isNull())
8365 continue;
8366 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8367 AssertComRC(rc);
8368 }
8369 }
8370
8371 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8372 {
8373 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8374 if (mData->mFirstSnapshot)
8375 {
8376 // snapshots tree is protected by machine write lock; strictly
8377 // this isn't necessary here since we're deleting the entire
8378 // machine, but otherwise we assert in Snapshot::uninit()
8379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8380 mData->mFirstSnapshot->uninit();
8381 mData->mFirstSnapshot.setNull();
8382 }
8383
8384 mData->mCurrentSnapshot.setNull();
8385 }
8386
8387 /* free data structures (the essential mData structure is not freed here
8388 * since it may be still in use) */
8389 mMediumAttachments.free();
8390 mStorageControllers.free();
8391 mUSBControllers.free();
8392 mHWData.free();
8393 mUserData.free();
8394 mSSData.free();
8395}
8396
8397/**
8398 * Returns a pointer to the Machine object for this machine that acts like a
8399 * parent for complex machine data objects such as shared folders, etc.
8400 *
8401 * For primary Machine objects and for SnapshotMachine objects, returns this
8402 * object's pointer itself. For SessionMachine objects, returns the peer
8403 * (primary) machine pointer.
8404 */
8405Machine *Machine::i_getMachine()
8406{
8407 if (i_isSessionMachine())
8408 return (Machine*)mPeer;
8409 return this;
8410}
8411
8412/**
8413 * Makes sure that there are no machine state dependents. If necessary, waits
8414 * for the number of dependents to drop to zero.
8415 *
8416 * Make sure this method is called from under this object's write lock to
8417 * guarantee that no new dependents may be added when this method returns
8418 * control to the caller.
8419 *
8420 * @note Locks this object for writing. The lock will be released while waiting
8421 * (if necessary).
8422 *
8423 * @warning To be used only in methods that change the machine state!
8424 */
8425void Machine::i_ensureNoStateDependencies()
8426{
8427 AssertReturnVoid(isWriteLockOnCurrentThread());
8428
8429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8430
8431 /* Wait for all state dependents if necessary */
8432 if (mData->mMachineStateDeps != 0)
8433 {
8434 /* lazy semaphore creation */
8435 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8436 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8437
8438 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8439 mData->mMachineStateDeps));
8440
8441 ++mData->mMachineStateChangePending;
8442
8443 /* reset the semaphore before waiting, the last dependent will signal
8444 * it */
8445 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8446
8447 alock.release();
8448
8449 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8450
8451 alock.acquire();
8452
8453 -- mData->mMachineStateChangePending;
8454 }
8455}
8456
8457/**
8458 * Changes the machine state and informs callbacks.
8459 *
8460 * This method is not intended to fail so it either returns S_OK or asserts (and
8461 * returns a failure).
8462 *
8463 * @note Locks this object for writing.
8464 */
8465HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8466{
8467 LogFlowThisFuncEnter();
8468 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8469 Assert(aMachineState != MachineState_Null);
8470
8471 AutoCaller autoCaller(this);
8472 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8473
8474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8475
8476 /* wait for state dependents to drop to zero */
8477 i_ensureNoStateDependencies();
8478
8479 MachineState_T const enmOldState = mData->mMachineState;
8480 if (enmOldState != aMachineState)
8481 {
8482 mData->mMachineState = aMachineState;
8483 RTTimeNow(&mData->mLastStateChange);
8484
8485#ifdef VBOX_WITH_DTRACE_R3_MAIN
8486 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8487#endif
8488 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8489 }
8490
8491 LogFlowThisFuncLeave();
8492 return S_OK;
8493}
8494
8495/**
8496 * Searches for a shared folder with the given logical name
8497 * in the collection of shared folders.
8498 *
8499 * @param aName logical name of the shared folder
8500 * @param aSharedFolder where to return the found object
8501 * @param aSetError whether to set the error info if the folder is
8502 * not found
8503 * @return
8504 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8505 *
8506 * @note
8507 * must be called from under the object's lock!
8508 */
8509HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8510 ComObjPtr<SharedFolder> &aSharedFolder,
8511 bool aSetError /* = false */)
8512{
8513 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8514 for (HWData::SharedFolderList::const_iterator
8515 it = mHWData->mSharedFolders.begin();
8516 it != mHWData->mSharedFolders.end();
8517 ++it)
8518 {
8519 SharedFolder *pSF = *it;
8520 AutoCaller autoCaller(pSF);
8521 if (pSF->i_getName() == aName)
8522 {
8523 aSharedFolder = pSF;
8524 rc = S_OK;
8525 break;
8526 }
8527 }
8528
8529 if (aSetError && FAILED(rc))
8530 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8531
8532 return rc;
8533}
8534
8535/**
8536 * Initializes all machine instance data from the given settings structures
8537 * from XML. The exception is the machine UUID which needs special handling
8538 * depending on the caller's use case, so the caller needs to set that herself.
8539 *
8540 * This gets called in several contexts during machine initialization:
8541 *
8542 * -- When machine XML exists on disk already and needs to be loaded into memory,
8543 * for example, from #i_registeredInit() to load all registered machines on
8544 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8545 * attached to the machine should be part of some media registry already.
8546 *
8547 * -- During OVF import, when a machine config has been constructed from an
8548 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8549 * ensure that the media listed as attachments in the config (which have
8550 * been imported from the OVF) receive the correct registry ID.
8551 *
8552 * -- During VM cloning.
8553 *
8554 * @param config Machine settings from XML.
8555 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8556 * for each attached medium in the config.
8557 * @return
8558 */
8559HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8560 const Guid *puuidRegistry)
8561{
8562 // copy name, description, OS type, teleporter, UTC etc.
8563 mUserData->s = config.machineUserData;
8564
8565 // look up the object by Id to check it is valid
8566 ComObjPtr<GuestOSType> pGuestOSType;
8567 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8568 if (!pGuestOSType.isNull())
8569 mUserData->s.strOsType = pGuestOSType->i_id();
8570
8571 // stateFile (optional)
8572 if (config.strStateFile.isEmpty())
8573 mSSData->strStateFilePath.setNull();
8574 else
8575 {
8576 Utf8Str stateFilePathFull(config.strStateFile);
8577 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8578 if (RT_FAILURE(vrc))
8579 return setErrorBoth(E_FAIL, vrc,
8580 tr("Invalid saved state file path '%s' (%Rrc)"),
8581 config.strStateFile.c_str(),
8582 vrc);
8583 mSSData->strStateFilePath = stateFilePathFull;
8584 }
8585
8586 // snapshot folder needs special processing so set it again
8587 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8588 if (FAILED(rc)) return rc;
8589
8590 /* Copy the extra data items (config may or may not be the same as
8591 * mData->pMachineConfigFile) if necessary. When loading the XML files
8592 * from disk they are the same, but not for OVF import. */
8593 if (mData->pMachineConfigFile != &config)
8594 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8595
8596 /* currentStateModified (optional, default is true) */
8597 mData->mCurrentStateModified = config.fCurrentStateModified;
8598
8599 mData->mLastStateChange = config.timeLastStateChange;
8600
8601 /*
8602 * note: all mUserData members must be assigned prior this point because
8603 * we need to commit changes in order to let mUserData be shared by all
8604 * snapshot machine instances.
8605 */
8606 mUserData.commitCopy();
8607
8608 // machine registry, if present (must be loaded before snapshots)
8609 if (config.canHaveOwnMediaRegistry())
8610 {
8611 // determine machine folder
8612 Utf8Str strMachineFolder = i_getSettingsFileFull();
8613 strMachineFolder.stripFilename();
8614 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8615 config.mediaRegistry,
8616 strMachineFolder);
8617 if (FAILED(rc)) return rc;
8618 }
8619
8620 /* Snapshot node (optional) */
8621 size_t cRootSnapshots;
8622 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8623 {
8624 // there must be only one root snapshot
8625 Assert(cRootSnapshots == 1);
8626
8627 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8628
8629 rc = i_loadSnapshot(snap,
8630 config.uuidCurrentSnapshot,
8631 NULL); // no parent == first snapshot
8632 if (FAILED(rc)) return rc;
8633 }
8634
8635 // hardware data
8636 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8637 if (FAILED(rc)) return rc;
8638
8639 /*
8640 * NOTE: the assignment below must be the last thing to do,
8641 * otherwise it will be not possible to change the settings
8642 * somewhere in the code above because all setters will be
8643 * blocked by i_checkStateDependency(MutableStateDep).
8644 */
8645
8646 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8647 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8648 {
8649 /* no need to use i_setMachineState() during init() */
8650 mData->mMachineState = MachineState_AbortedSaved;
8651 }
8652 else if (config.fAborted)
8653 {
8654 mSSData->strStateFilePath.setNull();
8655
8656 /* no need to use i_setMachineState() during init() */
8657 mData->mMachineState = MachineState_Aborted;
8658 }
8659 else if (!mSSData->strStateFilePath.isEmpty())
8660 {
8661 /* no need to use i_setMachineState() during init() */
8662 mData->mMachineState = MachineState_Saved;
8663 }
8664
8665 // after loading settings, we are no longer different from the XML on disk
8666 mData->flModifications = 0;
8667
8668 return S_OK;
8669}
8670
8671/**
8672 * Recursively loads all snapshots starting from the given.
8673 *
8674 * @param data snapshot settings.
8675 * @param aCurSnapshotId Current snapshot ID from the settings file.
8676 * @param aParentSnapshot Parent snapshot.
8677 */
8678HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8679 const Guid &aCurSnapshotId,
8680 Snapshot *aParentSnapshot)
8681{
8682 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8683 AssertReturn(!i_isSessionMachine(), E_FAIL);
8684
8685 HRESULT rc = S_OK;
8686
8687 Utf8Str strStateFile;
8688 if (!data.strStateFile.isEmpty())
8689 {
8690 /* optional */
8691 strStateFile = data.strStateFile;
8692 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8693 if (RT_FAILURE(vrc))
8694 return setErrorBoth(E_FAIL, vrc,
8695 tr("Invalid saved state file path '%s' (%Rrc)"),
8696 strStateFile.c_str(),
8697 vrc);
8698 }
8699
8700 /* create a snapshot machine object */
8701 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8702 pSnapshotMachine.createObject();
8703 rc = pSnapshotMachine->initFromSettings(this,
8704 data.hardware,
8705 &data.debugging,
8706 &data.autostart,
8707 data.uuid.ref(),
8708 strStateFile);
8709 if (FAILED(rc)) return rc;
8710
8711 /* create a snapshot object */
8712 ComObjPtr<Snapshot> pSnapshot;
8713 pSnapshot.createObject();
8714 /* initialize the snapshot */
8715 rc = pSnapshot->init(mParent, // VirtualBox object
8716 data.uuid,
8717 data.strName,
8718 data.strDescription,
8719 data.timestamp,
8720 pSnapshotMachine,
8721 aParentSnapshot);
8722 if (FAILED(rc)) return rc;
8723
8724 /* memorize the first snapshot if necessary */
8725 if (!mData->mFirstSnapshot)
8726 mData->mFirstSnapshot = pSnapshot;
8727
8728 /* memorize the current snapshot when appropriate */
8729 if ( !mData->mCurrentSnapshot
8730 && pSnapshot->i_getId() == aCurSnapshotId
8731 )
8732 mData->mCurrentSnapshot = pSnapshot;
8733
8734 // now create the children
8735 for (settings::SnapshotsList::const_iterator
8736 it = data.llChildSnapshots.begin();
8737 it != data.llChildSnapshots.end();
8738 ++it)
8739 {
8740 const settings::Snapshot &childData = *it;
8741 // recurse
8742 rc = i_loadSnapshot(childData,
8743 aCurSnapshotId,
8744 pSnapshot); // parent = the one we created above
8745 if (FAILED(rc)) return rc;
8746 }
8747
8748 return rc;
8749}
8750
8751/**
8752 * Loads settings into mHWData.
8753 *
8754 * @param puuidRegistry Registry ID.
8755 * @param puuidSnapshot Snapshot ID
8756 * @param data Reference to the hardware settings.
8757 * @param pDbg Pointer to the debugging settings.
8758 * @param pAutostart Pointer to the autostart settings.
8759 */
8760HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8761 const Guid *puuidSnapshot,
8762 const settings::Hardware &data,
8763 const settings::Debugging *pDbg,
8764 const settings::Autostart *pAutostart)
8765{
8766 AssertReturn(!i_isSessionMachine(), E_FAIL);
8767
8768 HRESULT rc = S_OK;
8769
8770 try
8771 {
8772 ComObjPtr<GuestOSType> pGuestOSType;
8773 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8774
8775 /* The hardware version attribute (optional). */
8776 mHWData->mHWVersion = data.strVersion;
8777 mHWData->mHardwareUUID = data.uuid;
8778
8779 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8780 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8781 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8782 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8783 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8784 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8785 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8786 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8787 mHWData->mPAEEnabled = data.fPAE;
8788 mHWData->mLongMode = data.enmLongMode;
8789 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8790 mHWData->mAPIC = data.fAPIC;
8791 mHWData->mX2APIC = data.fX2APIC;
8792 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8793 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8794 mHWData->mSpecCtrl = data.fSpecCtrl;
8795 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8796 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8797 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8798 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8799 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8800 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8801 mHWData->mCPUCount = data.cCPUs;
8802 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8803 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8804 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8805 mHWData->mCpuProfile = data.strCpuProfile;
8806
8807 // cpu
8808 if (mHWData->mCPUHotPlugEnabled)
8809 {
8810 for (settings::CpuList::const_iterator
8811 it = data.llCpus.begin();
8812 it != data.llCpus.end();
8813 ++it)
8814 {
8815 const settings::Cpu &cpu = *it;
8816
8817 mHWData->mCPUAttached[cpu.ulId] = true;
8818 }
8819 }
8820
8821 // cpuid leafs
8822 for (settings::CpuIdLeafsList::const_iterator
8823 it = data.llCpuIdLeafs.begin();
8824 it != data.llCpuIdLeafs.end();
8825 ++it)
8826 {
8827 const settings::CpuIdLeaf &rLeaf= *it;
8828 if ( rLeaf.idx < UINT32_C(0x20)
8829 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8830 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8831 mHWData->mCpuIdLeafList.push_back(rLeaf);
8832 /* else: just ignore */
8833 }
8834
8835 mHWData->mMemorySize = data.ulMemorySizeMB;
8836 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8837
8838 // boot order
8839 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8840 {
8841 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8842 if (it == data.mapBootOrder.end())
8843 mHWData->mBootOrder[i] = DeviceType_Null;
8844 else
8845 mHWData->mBootOrder[i] = it->second;
8846 }
8847
8848 mHWData->mFirmwareType = data.firmwareType;
8849 mHWData->mPointingHIDType = data.pointingHIDType;
8850 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8851 mHWData->mChipsetType = data.chipsetType;
8852 mHWData->mIommuType = data.iommuType;
8853 mHWData->mParavirtProvider = data.paravirtProvider;
8854 mHWData->mParavirtDebug = data.strParavirtDebug;
8855 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8856 mHWData->mHPETEnabled = data.fHPETEnabled;
8857
8858 /* GraphicsAdapter */
8859 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8860 if (FAILED(rc)) return rc;
8861
8862 /* VRDEServer */
8863 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8864 if (FAILED(rc)) return rc;
8865
8866 /* BIOS */
8867 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8868 if (FAILED(rc)) return rc;
8869
8870 /* Trusted Platform Module */
8871 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8872 if (FAILED(rc)) return rc;
8873
8874 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8875 if (FAILED(rc)) return rc;
8876
8877 /* Recording settings */
8878 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8879 if (FAILED(rc)) return rc;
8880
8881 // Bandwidth control (must come before network adapters)
8882 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8883 if (FAILED(rc)) return rc;
8884
8885 /* USB controllers */
8886 for (settings::USBControllerList::const_iterator
8887 it = data.usbSettings.llUSBControllers.begin();
8888 it != data.usbSettings.llUSBControllers.end();
8889 ++it)
8890 {
8891 const settings::USBController &settingsCtrl = *it;
8892 ComObjPtr<USBController> newCtrl;
8893
8894 newCtrl.createObject();
8895 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8896 mUSBControllers->push_back(newCtrl);
8897 }
8898
8899 /* USB device filters */
8900 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8901 if (FAILED(rc)) return rc;
8902
8903 // network adapters (establish array size first and apply defaults, to
8904 // ensure reading the same settings as we saved, since the list skips
8905 // adapters having defaults)
8906 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8907 size_t oldCount = mNetworkAdapters.size();
8908 if (newCount > oldCount)
8909 {
8910 mNetworkAdapters.resize(newCount);
8911 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8912 {
8913 unconst(mNetworkAdapters[slot]).createObject();
8914 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8915 }
8916 }
8917 else if (newCount < oldCount)
8918 mNetworkAdapters.resize(newCount);
8919 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8920 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8921 for (settings::NetworkAdaptersList::const_iterator
8922 it = data.llNetworkAdapters.begin();
8923 it != data.llNetworkAdapters.end();
8924 ++it)
8925 {
8926 const settings::NetworkAdapter &nic = *it;
8927
8928 /* slot uniqueness is guaranteed by XML Schema */
8929 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8930 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8931 if (FAILED(rc)) return rc;
8932 }
8933
8934 // serial ports (establish defaults first, to ensure reading the same
8935 // settings as we saved, since the list skips ports having defaults)
8936 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8937 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8938 for (settings::SerialPortsList::const_iterator
8939 it = data.llSerialPorts.begin();
8940 it != data.llSerialPorts.end();
8941 ++it)
8942 {
8943 const settings::SerialPort &s = *it;
8944
8945 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8946 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8947 if (FAILED(rc)) return rc;
8948 }
8949
8950 // parallel ports (establish defaults first, to ensure reading the same
8951 // settings as we saved, since the list skips ports having defaults)
8952 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8953 mParallelPorts[i]->i_applyDefaults();
8954 for (settings::ParallelPortsList::const_iterator
8955 it = data.llParallelPorts.begin();
8956 it != data.llParallelPorts.end();
8957 ++it)
8958 {
8959 const settings::ParallelPort &p = *it;
8960
8961 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8962 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8963 if (FAILED(rc)) return rc;
8964 }
8965
8966 /* AudioAdapter */
8967 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8968 if (FAILED(rc)) return rc;
8969
8970 /* storage controllers */
8971 rc = i_loadStorageControllers(data.storage,
8972 puuidRegistry,
8973 puuidSnapshot);
8974 if (FAILED(rc)) return rc;
8975
8976 /* Shared folders */
8977 for (settings::SharedFoldersList::const_iterator
8978 it = data.llSharedFolders.begin();
8979 it != data.llSharedFolders.end();
8980 ++it)
8981 {
8982 const settings::SharedFolder &sf = *it;
8983
8984 ComObjPtr<SharedFolder> sharedFolder;
8985 /* Check for double entries. Not allowed! */
8986 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8987 if (SUCCEEDED(rc))
8988 return setError(VBOX_E_OBJECT_IN_USE,
8989 tr("Shared folder named '%s' already exists"),
8990 sf.strName.c_str());
8991
8992 /* Create the new shared folder. Don't break on error. This will be
8993 * reported when the machine starts. */
8994 sharedFolder.createObject();
8995 rc = sharedFolder->init(i_getMachine(),
8996 sf.strName,
8997 sf.strHostPath,
8998 RT_BOOL(sf.fWritable),
8999 RT_BOOL(sf.fAutoMount),
9000 sf.strAutoMountPoint,
9001 false /* fFailOnError */);
9002 if (FAILED(rc)) return rc;
9003 mHWData->mSharedFolders.push_back(sharedFolder);
9004 }
9005
9006 // Clipboard
9007 mHWData->mClipboardMode = data.clipboardMode;
9008 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9009
9010 // drag'n'drop
9011 mHWData->mDnDMode = data.dndMode;
9012
9013 // guest settings
9014 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9015
9016 // IO settings
9017 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9018 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9019
9020 // Host PCI devices
9021 for (settings::HostPCIDeviceAttachmentList::const_iterator
9022 it = data.pciAttachments.begin();
9023 it != data.pciAttachments.end();
9024 ++it)
9025 {
9026 const settings::HostPCIDeviceAttachment &hpda = *it;
9027 ComObjPtr<PCIDeviceAttachment> pda;
9028
9029 pda.createObject();
9030 pda->i_loadSettings(this, hpda);
9031 mHWData->mPCIDeviceAssignments.push_back(pda);
9032 }
9033
9034 /*
9035 * (The following isn't really real hardware, but it lives in HWData
9036 * for reasons of convenience.)
9037 */
9038
9039#ifdef VBOX_WITH_GUEST_PROPS
9040 /* Guest properties (optional) */
9041
9042 /* Only load transient guest properties for configs which have saved
9043 * state, because there shouldn't be any for powered off VMs. The same
9044 * logic applies for snapshots, as offline snapshots shouldn't have
9045 * any such properties. They confuse the code in various places.
9046 * Note: can't rely on the machine state, as it isn't set yet. */
9047 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9048 /* apologies for the hacky unconst() usage, but this needs hacking
9049 * actually inconsistent settings into consistency, otherwise there
9050 * will be some corner cases where the inconsistency survives
9051 * surprisingly long without getting fixed, especially for snapshots
9052 * as there are no config changes. */
9053 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9054 for (settings::GuestPropertiesList::iterator
9055 it = llGuestProperties.begin();
9056 it != llGuestProperties.end();
9057 /*nothing*/)
9058 {
9059 const settings::GuestProperty &prop = *it;
9060 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9061 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9062 if ( fSkipTransientGuestProperties
9063 && ( fFlags & GUEST_PROP_F_TRANSIENT
9064 || fFlags & GUEST_PROP_F_TRANSRESET))
9065 {
9066 it = llGuestProperties.erase(it);
9067 continue;
9068 }
9069 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9070 mHWData->mGuestProperties[prop.strName] = property;
9071 ++it;
9072 }
9073#endif /* VBOX_WITH_GUEST_PROPS defined */
9074
9075 rc = i_loadDebugging(pDbg);
9076 if (FAILED(rc))
9077 return rc;
9078
9079 mHWData->mAutostart = *pAutostart;
9080
9081 /* default frontend */
9082 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9083 }
9084 catch (std::bad_alloc &)
9085 {
9086 return E_OUTOFMEMORY;
9087 }
9088
9089 AssertComRC(rc);
9090 return rc;
9091}
9092
9093/**
9094 * Called from i_loadHardware() to load the debugging settings of the
9095 * machine.
9096 *
9097 * @param pDbg Pointer to the settings.
9098 */
9099HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9100{
9101 mHWData->mDebugging = *pDbg;
9102 /* no more processing currently required, this will probably change. */
9103 return S_OK;
9104}
9105
9106/**
9107 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9108 *
9109 * @param data storage settings.
9110 * @param puuidRegistry media registry ID to set media to or NULL;
9111 * see Machine::i_loadMachineDataFromSettings()
9112 * @param puuidSnapshot snapshot ID
9113 * @return
9114 */
9115HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9116 const Guid *puuidRegistry,
9117 const Guid *puuidSnapshot)
9118{
9119 AssertReturn(!i_isSessionMachine(), E_FAIL);
9120
9121 HRESULT rc = S_OK;
9122
9123 for (settings::StorageControllersList::const_iterator
9124 it = data.llStorageControllers.begin();
9125 it != data.llStorageControllers.end();
9126 ++it)
9127 {
9128 const settings::StorageController &ctlData = *it;
9129
9130 ComObjPtr<StorageController> pCtl;
9131 /* Try to find one with the name first. */
9132 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9133 if (SUCCEEDED(rc))
9134 return setError(VBOX_E_OBJECT_IN_USE,
9135 tr("Storage controller named '%s' already exists"),
9136 ctlData.strName.c_str());
9137
9138 pCtl.createObject();
9139 rc = pCtl->init(this,
9140 ctlData.strName,
9141 ctlData.storageBus,
9142 ctlData.ulInstance,
9143 ctlData.fBootable);
9144 if (FAILED(rc)) return rc;
9145
9146 mStorageControllers->push_back(pCtl);
9147
9148 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9149 if (FAILED(rc)) return rc;
9150
9151 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9152 if (FAILED(rc)) return rc;
9153
9154 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9155 if (FAILED(rc)) return rc;
9156
9157 /* Load the attached devices now. */
9158 rc = i_loadStorageDevices(pCtl,
9159 ctlData,
9160 puuidRegistry,
9161 puuidSnapshot);
9162 if (FAILED(rc)) return rc;
9163 }
9164
9165 return S_OK;
9166}
9167
9168/**
9169 * Called from i_loadStorageControllers for a controller's devices.
9170 *
9171 * @param aStorageController
9172 * @param data
9173 * @param puuidRegistry media registry ID to set media to or NULL; see
9174 * Machine::i_loadMachineDataFromSettings()
9175 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9176 * @return
9177 */
9178HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9179 const settings::StorageController &data,
9180 const Guid *puuidRegistry,
9181 const Guid *puuidSnapshot)
9182{
9183 HRESULT rc = S_OK;
9184
9185 /* paranoia: detect duplicate attachments */
9186 for (settings::AttachedDevicesList::const_iterator
9187 it = data.llAttachedDevices.begin();
9188 it != data.llAttachedDevices.end();
9189 ++it)
9190 {
9191 const settings::AttachedDevice &ad = *it;
9192
9193 for (settings::AttachedDevicesList::const_iterator it2 = it;
9194 it2 != data.llAttachedDevices.end();
9195 ++it2)
9196 {
9197 if (it == it2)
9198 continue;
9199
9200 const settings::AttachedDevice &ad2 = *it2;
9201
9202 if ( ad.lPort == ad2.lPort
9203 && ad.lDevice == ad2.lDevice)
9204 {
9205 return setError(E_FAIL,
9206 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9207 aStorageController->i_getName().c_str(),
9208 ad.lPort,
9209 ad.lDevice,
9210 mUserData->s.strName.c_str());
9211 }
9212 }
9213 }
9214
9215 for (settings::AttachedDevicesList::const_iterator
9216 it = data.llAttachedDevices.begin();
9217 it != data.llAttachedDevices.end();
9218 ++it)
9219 {
9220 const settings::AttachedDevice &dev = *it;
9221 ComObjPtr<Medium> medium;
9222
9223 switch (dev.deviceType)
9224 {
9225 case DeviceType_Floppy:
9226 case DeviceType_DVD:
9227 if (dev.strHostDriveSrc.isNotEmpty())
9228 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9229 false /* fRefresh */, medium);
9230 else
9231 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9232 dev.uuid,
9233 false /* fRefresh */,
9234 false /* aSetError */,
9235 medium);
9236 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9237 // This is not an error. The host drive or UUID might have vanished, so just go
9238 // ahead without this removeable medium attachment
9239 rc = S_OK;
9240 break;
9241
9242 case DeviceType_HardDisk:
9243 {
9244 /* find a hard disk by UUID */
9245 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9246 if (FAILED(rc))
9247 {
9248 if (i_isSnapshotMachine())
9249 {
9250 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9251 // so the user knows that the bad disk is in a snapshot somewhere
9252 com::ErrorInfo info;
9253 return setError(E_FAIL,
9254 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9255 puuidSnapshot->raw(),
9256 info.getText().raw());
9257 }
9258 else
9259 return rc;
9260 }
9261
9262 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9263
9264 if (medium->i_getType() == MediumType_Immutable)
9265 {
9266 if (i_isSnapshotMachine())
9267 return setError(E_FAIL,
9268 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9269 "of the virtual machine '%s' ('%s')"),
9270 medium->i_getLocationFull().c_str(),
9271 dev.uuid.raw(),
9272 puuidSnapshot->raw(),
9273 mUserData->s.strName.c_str(),
9274 mData->m_strConfigFileFull.c_str());
9275
9276 return setError(E_FAIL,
9277 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9278 medium->i_getLocationFull().c_str(),
9279 dev.uuid.raw(),
9280 mUserData->s.strName.c_str(),
9281 mData->m_strConfigFileFull.c_str());
9282 }
9283
9284 if (medium->i_getType() == MediumType_MultiAttach)
9285 {
9286 if (i_isSnapshotMachine())
9287 return setError(E_FAIL,
9288 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9289 "of the virtual machine '%s' ('%s')"),
9290 medium->i_getLocationFull().c_str(),
9291 dev.uuid.raw(),
9292 puuidSnapshot->raw(),
9293 mUserData->s.strName.c_str(),
9294 mData->m_strConfigFileFull.c_str());
9295
9296 return setError(E_FAIL,
9297 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9298 medium->i_getLocationFull().c_str(),
9299 dev.uuid.raw(),
9300 mUserData->s.strName.c_str(),
9301 mData->m_strConfigFileFull.c_str());
9302 }
9303
9304 if ( !i_isSnapshotMachine()
9305 && medium->i_getChildren().size() != 0
9306 )
9307 return setError(E_FAIL,
9308 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9309 "because it has %d differencing child hard disks"),
9310 medium->i_getLocationFull().c_str(),
9311 dev.uuid.raw(),
9312 mUserData->s.strName.c_str(),
9313 mData->m_strConfigFileFull.c_str(),
9314 medium->i_getChildren().size());
9315
9316 if (i_findAttachment(*mMediumAttachments.data(),
9317 medium))
9318 return setError(E_FAIL,
9319 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9320 medium->i_getLocationFull().c_str(),
9321 dev.uuid.raw(),
9322 mUserData->s.strName.c_str(),
9323 mData->m_strConfigFileFull.c_str());
9324
9325 break;
9326 }
9327
9328 default:
9329 return setError(E_FAIL,
9330 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9331 medium->i_getLocationFull().c_str(),
9332 mUserData->s.strName.c_str(),
9333 mData->m_strConfigFileFull.c_str());
9334 }
9335
9336 if (FAILED(rc))
9337 break;
9338
9339 /* Bandwidth groups are loaded at this point. */
9340 ComObjPtr<BandwidthGroup> pBwGroup;
9341
9342 if (!dev.strBwGroup.isEmpty())
9343 {
9344 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9345 if (FAILED(rc))
9346 return setError(E_FAIL,
9347 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9348 medium->i_getLocationFull().c_str(),
9349 dev.strBwGroup.c_str(),
9350 mUserData->s.strName.c_str(),
9351 mData->m_strConfigFileFull.c_str());
9352 pBwGroup->i_reference();
9353 }
9354
9355 const Utf8Str controllerName = aStorageController->i_getName();
9356 ComObjPtr<MediumAttachment> pAttachment;
9357 pAttachment.createObject();
9358 rc = pAttachment->init(this,
9359 medium,
9360 controllerName,
9361 dev.lPort,
9362 dev.lDevice,
9363 dev.deviceType,
9364 false,
9365 dev.fPassThrough,
9366 dev.fTempEject,
9367 dev.fNonRotational,
9368 dev.fDiscard,
9369 dev.fHotPluggable,
9370 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9371 if (FAILED(rc)) break;
9372
9373 /* associate the medium with this machine and snapshot */
9374 if (!medium.isNull())
9375 {
9376 AutoCaller medCaller(medium);
9377 if (FAILED(medCaller.rc())) return medCaller.rc();
9378 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9379
9380 if (i_isSnapshotMachine())
9381 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9382 else
9383 rc = medium->i_addBackReference(mData->mUuid);
9384 /* If the medium->addBackReference fails it sets an appropriate
9385 * error message, so no need to do any guesswork here. */
9386
9387 if (puuidRegistry)
9388 // caller wants registry ID to be set on all attached media (OVF import case)
9389 medium->i_addRegistry(*puuidRegistry);
9390 }
9391
9392 if (FAILED(rc))
9393 break;
9394
9395 /* back up mMediumAttachments to let registeredInit() properly rollback
9396 * on failure (= limited accessibility) */
9397 i_setModified(IsModified_Storage);
9398 mMediumAttachments.backup();
9399 mMediumAttachments->push_back(pAttachment);
9400 }
9401
9402 return rc;
9403}
9404
9405/**
9406 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9407 *
9408 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9409 * @param aSnapshot where to return the found snapshot
9410 * @param aSetError true to set extended error info on failure
9411 */
9412HRESULT Machine::i_findSnapshotById(const Guid &aId,
9413 ComObjPtr<Snapshot> &aSnapshot,
9414 bool aSetError /* = false */)
9415{
9416 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9417
9418 if (!mData->mFirstSnapshot)
9419 {
9420 if (aSetError)
9421 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9422 return E_FAIL;
9423 }
9424
9425 if (aId.isZero())
9426 aSnapshot = mData->mFirstSnapshot;
9427 else
9428 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9429
9430 if (!aSnapshot)
9431 {
9432 if (aSetError)
9433 return setError(E_FAIL,
9434 tr("Could not find a snapshot with UUID {%s}"),
9435 aId.toString().c_str());
9436 return E_FAIL;
9437 }
9438
9439 return S_OK;
9440}
9441
9442/**
9443 * Returns the snapshot with the given name or fails of no such snapshot.
9444 *
9445 * @param strName snapshot name to find
9446 * @param aSnapshot where to return the found snapshot
9447 * @param aSetError true to set extended error info on failure
9448 */
9449HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9450 ComObjPtr<Snapshot> &aSnapshot,
9451 bool aSetError /* = false */)
9452{
9453 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9454
9455 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9456
9457 if (!mData->mFirstSnapshot)
9458 {
9459 if (aSetError)
9460 return setError(VBOX_E_OBJECT_NOT_FOUND,
9461 tr("This machine does not have any snapshots"));
9462 return VBOX_E_OBJECT_NOT_FOUND;
9463 }
9464
9465 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9466
9467 if (!aSnapshot)
9468 {
9469 if (aSetError)
9470 return setError(VBOX_E_OBJECT_NOT_FOUND,
9471 tr("Could not find a snapshot named '%s'"), strName.c_str());
9472 return VBOX_E_OBJECT_NOT_FOUND;
9473 }
9474
9475 return S_OK;
9476}
9477
9478/**
9479 * Returns a storage controller object with the given name.
9480 *
9481 * @param aName storage controller name to find
9482 * @param aStorageController where to return the found storage controller
9483 * @param aSetError true to set extended error info on failure
9484 */
9485HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9486 ComObjPtr<StorageController> &aStorageController,
9487 bool aSetError /* = false */)
9488{
9489 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9490
9491 for (StorageControllerList::const_iterator
9492 it = mStorageControllers->begin();
9493 it != mStorageControllers->end();
9494 ++it)
9495 {
9496 if ((*it)->i_getName() == aName)
9497 {
9498 aStorageController = (*it);
9499 return S_OK;
9500 }
9501 }
9502
9503 if (aSetError)
9504 return setError(VBOX_E_OBJECT_NOT_FOUND,
9505 tr("Could not find a storage controller named '%s'"),
9506 aName.c_str());
9507 return VBOX_E_OBJECT_NOT_FOUND;
9508}
9509
9510/**
9511 * Returns a USB controller object with the given name.
9512 *
9513 * @param aName USB controller name to find
9514 * @param aUSBController where to return the found USB controller
9515 * @param aSetError true to set extended error info on failure
9516 */
9517HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9518 ComObjPtr<USBController> &aUSBController,
9519 bool aSetError /* = false */)
9520{
9521 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9522
9523 for (USBControllerList::const_iterator
9524 it = mUSBControllers->begin();
9525 it != mUSBControllers->end();
9526 ++it)
9527 {
9528 if ((*it)->i_getName() == aName)
9529 {
9530 aUSBController = (*it);
9531 return S_OK;
9532 }
9533 }
9534
9535 if (aSetError)
9536 return setError(VBOX_E_OBJECT_NOT_FOUND,
9537 tr("Could not find a storage controller named '%s'"),
9538 aName.c_str());
9539 return VBOX_E_OBJECT_NOT_FOUND;
9540}
9541
9542/**
9543 * Returns the number of USB controller instance of the given type.
9544 *
9545 * @param enmType USB controller type.
9546 */
9547ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9548{
9549 ULONG cCtrls = 0;
9550
9551 for (USBControllerList::const_iterator
9552 it = mUSBControllers->begin();
9553 it != mUSBControllers->end();
9554 ++it)
9555 {
9556 if ((*it)->i_getControllerType() == enmType)
9557 cCtrls++;
9558 }
9559
9560 return cCtrls;
9561}
9562
9563HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9564 MediumAttachmentList &atts)
9565{
9566 AutoCaller autoCaller(this);
9567 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9568
9569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9570
9571 for (MediumAttachmentList::const_iterator
9572 it = mMediumAttachments->begin();
9573 it != mMediumAttachments->end();
9574 ++it)
9575 {
9576 const ComObjPtr<MediumAttachment> &pAtt = *it;
9577 // should never happen, but deal with NULL pointers in the list.
9578 AssertContinue(!pAtt.isNull());
9579
9580 // getControllerName() needs caller+read lock
9581 AutoCaller autoAttCaller(pAtt);
9582 if (FAILED(autoAttCaller.rc()))
9583 {
9584 atts.clear();
9585 return autoAttCaller.rc();
9586 }
9587 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9588
9589 if (pAtt->i_getControllerName() == aName)
9590 atts.push_back(pAtt);
9591 }
9592
9593 return S_OK;
9594}
9595
9596
9597/**
9598 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9599 * file if the machine name was changed and about creating a new settings file
9600 * if this is a new machine.
9601 *
9602 * @note Must be never called directly but only from #saveSettings().
9603 */
9604HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9605{
9606 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9607
9608 HRESULT rc = S_OK;
9609
9610 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9611
9612 /// @todo need to handle primary group change, too
9613
9614 /* attempt to rename the settings file if machine name is changed */
9615 if ( mUserData->s.fNameSync
9616 && mUserData.isBackedUp()
9617 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9618 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9619 )
9620 {
9621 bool dirRenamed = false;
9622 bool fileRenamed = false;
9623
9624 Utf8Str configFile, newConfigFile;
9625 Utf8Str configFilePrev, newConfigFilePrev;
9626 Utf8Str NVRAMFile, newNVRAMFile;
9627 Utf8Str configDir, newConfigDir;
9628
9629 do
9630 {
9631 int vrc = VINF_SUCCESS;
9632
9633 Utf8Str name = mUserData.backedUpData()->s.strName;
9634 Utf8Str newName = mUserData->s.strName;
9635 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9636 if (group == "/")
9637 group.setNull();
9638 Utf8Str newGroup = mUserData->s.llGroups.front();
9639 if (newGroup == "/")
9640 newGroup.setNull();
9641
9642 configFile = mData->m_strConfigFileFull;
9643
9644 /* first, rename the directory if it matches the group and machine name */
9645 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9646 /** @todo hack, make somehow use of ComposeMachineFilename */
9647 if (mUserData->s.fDirectoryIncludesUUID)
9648 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9649 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9650 /** @todo hack, make somehow use of ComposeMachineFilename */
9651 if (mUserData->s.fDirectoryIncludesUUID)
9652 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9653 configDir = configFile;
9654 configDir.stripFilename();
9655 newConfigDir = configDir;
9656 if ( configDir.length() >= groupPlusName.length()
9657 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9658 groupPlusName.c_str()))
9659 {
9660 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9661 Utf8Str newConfigBaseDir(newConfigDir);
9662 newConfigDir.append(newGroupPlusName);
9663 /* consistency: use \ if appropriate on the platform */
9664 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9665 /* new dir and old dir cannot be equal here because of 'if'
9666 * above and because name != newName */
9667 Assert(configDir != newConfigDir);
9668 if (!fSettingsFileIsNew)
9669 {
9670 /* perform real rename only if the machine is not new */
9671 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9672 if ( vrc == VERR_FILE_NOT_FOUND
9673 || vrc == VERR_PATH_NOT_FOUND)
9674 {
9675 /* create the parent directory, then retry renaming */
9676 Utf8Str parent(newConfigDir);
9677 parent.stripFilename();
9678 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9679 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9680 }
9681 if (RT_FAILURE(vrc))
9682 {
9683 rc = setErrorBoth(E_FAIL, vrc,
9684 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9685 configDir.c_str(),
9686 newConfigDir.c_str(),
9687 vrc);
9688 break;
9689 }
9690 /* delete subdirectories which are no longer needed */
9691 Utf8Str dir(configDir);
9692 dir.stripFilename();
9693 while (dir != newConfigBaseDir && dir != ".")
9694 {
9695 vrc = RTDirRemove(dir.c_str());
9696 if (RT_FAILURE(vrc))
9697 break;
9698 dir.stripFilename();
9699 }
9700 dirRenamed = true;
9701 }
9702 }
9703
9704 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9705
9706 /* then try to rename the settings file itself */
9707 if (newConfigFile != configFile)
9708 {
9709 /* get the path to old settings file in renamed directory */
9710 Assert(mData->m_strConfigFileFull == configFile);
9711 configFile.printf("%s%c%s",
9712 newConfigDir.c_str(),
9713 RTPATH_DELIMITER,
9714 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9715 if (!fSettingsFileIsNew)
9716 {
9717 /* perform real rename only if the machine is not new */
9718 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9719 if (RT_FAILURE(vrc))
9720 {
9721 rc = setErrorBoth(E_FAIL, vrc,
9722 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9723 configFile.c_str(),
9724 newConfigFile.c_str(),
9725 vrc);
9726 break;
9727 }
9728 fileRenamed = true;
9729 configFilePrev = configFile;
9730 configFilePrev += "-prev";
9731 newConfigFilePrev = newConfigFile;
9732 newConfigFilePrev += "-prev";
9733 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9734 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9735 if (NVRAMFile.isNotEmpty())
9736 {
9737 // in the NVRAM file path, replace the old directory with the new directory
9738 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9739 {
9740 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9741 NVRAMFile = newConfigDir + strNVRAMFile;
9742 }
9743 newNVRAMFile = newConfigFile;
9744 newNVRAMFile.stripSuffix();
9745 newNVRAMFile += ".nvram";
9746 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9747 }
9748 }
9749 }
9750
9751 // update m_strConfigFileFull amd mConfigFile
9752 mData->m_strConfigFileFull = newConfigFile;
9753 // compute the relative path too
9754 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9755
9756 // store the old and new so that VirtualBox::i_saveSettings() can update
9757 // the media registry
9758 if ( mData->mRegistered
9759 && (configDir != newConfigDir || configFile != newConfigFile))
9760 {
9761 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9762
9763 if (pfNeedsGlobalSaveSettings)
9764 *pfNeedsGlobalSaveSettings = true;
9765 }
9766
9767 // in the saved state file path, replace the old directory with the new directory
9768 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9769 {
9770 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9771 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9772 }
9773 if (newNVRAMFile.isNotEmpty())
9774 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9775
9776 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9777 if (mData->mFirstSnapshot)
9778 {
9779 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9780 newConfigDir.c_str());
9781 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9782 newConfigDir.c_str());
9783 }
9784 }
9785 while (0);
9786
9787 if (FAILED(rc))
9788 {
9789 /* silently try to rename everything back */
9790 if (fileRenamed)
9791 {
9792 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9793 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9794 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9795 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9796 }
9797 if (dirRenamed)
9798 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9799 }
9800
9801 if (FAILED(rc)) return rc;
9802 }
9803
9804 if (fSettingsFileIsNew)
9805 {
9806 /* create a virgin config file */
9807 int vrc = VINF_SUCCESS;
9808
9809 /* ensure the settings directory exists */
9810 Utf8Str path(mData->m_strConfigFileFull);
9811 path.stripFilename();
9812 if (!RTDirExists(path.c_str()))
9813 {
9814 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9815 if (RT_FAILURE(vrc))
9816 {
9817 return setErrorBoth(E_FAIL, vrc,
9818 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9819 path.c_str(),
9820 vrc);
9821 }
9822 }
9823
9824 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9825 path = Utf8Str(mData->m_strConfigFileFull);
9826 RTFILE f = NIL_RTFILE;
9827 vrc = RTFileOpen(&f, path.c_str(),
9828 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9829 if (RT_FAILURE(vrc))
9830 return setErrorBoth(E_FAIL, vrc,
9831 tr("Could not create the settings file '%s' (%Rrc)"),
9832 path.c_str(),
9833 vrc);
9834 RTFileClose(f);
9835 }
9836
9837 return rc;
9838}
9839
9840/**
9841 * Saves and commits machine data, user data and hardware data.
9842 *
9843 * Note that on failure, the data remains uncommitted.
9844 *
9845 * @a aFlags may combine the following flags:
9846 *
9847 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9848 * Used when saving settings after an operation that makes them 100%
9849 * correspond to the settings from the current snapshot.
9850 * - SaveS_Force: settings will be saved without doing a deep compare of the
9851 * settings structures. This is used when this is called because snapshots
9852 * have changed to avoid the overhead of the deep compare.
9853 *
9854 * @note Must be called from under this object's write lock. Locks children for
9855 * writing.
9856 *
9857 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9858 * initialized to false and that will be set to true by this function if
9859 * the caller must invoke VirtualBox::i_saveSettings() because the global
9860 * settings have changed. This will happen if a machine rename has been
9861 * saved and the global machine and media registries will therefore need
9862 * updating.
9863 * @param aFlags Flags.
9864 */
9865HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9866 int aFlags /*= 0*/)
9867{
9868 LogFlowThisFuncEnter();
9869
9870 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9871
9872 /* make sure child objects are unable to modify the settings while we are
9873 * saving them */
9874 i_ensureNoStateDependencies();
9875
9876 AssertReturn(!i_isSnapshotMachine(),
9877 E_FAIL);
9878
9879 if (!mData->mAccessible)
9880 return setError(VBOX_E_INVALID_VM_STATE,
9881 tr("The machine is not accessible, so cannot save settings"));
9882
9883 HRESULT rc = S_OK;
9884 bool fNeedsWrite = false;
9885
9886 /* First, prepare to save settings. It will care about renaming the
9887 * settings directory and file if the machine name was changed and about
9888 * creating a new settings file if this is a new machine. */
9889 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9890 if (FAILED(rc)) return rc;
9891
9892 // keep a pointer to the current settings structures
9893 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9894 settings::MachineConfigFile *pNewConfig = NULL;
9895
9896 try
9897 {
9898 // make a fresh one to have everyone write stuff into
9899 pNewConfig = new settings::MachineConfigFile(NULL);
9900 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9901
9902 // now go and copy all the settings data from COM to the settings structures
9903 // (this calls i_saveSettings() on all the COM objects in the machine)
9904 i_copyMachineDataToSettings(*pNewConfig);
9905
9906 if (aFlags & SaveS_ResetCurStateModified)
9907 {
9908 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9909 mData->mCurrentStateModified = FALSE;
9910 fNeedsWrite = true; // always, no need to compare
9911 }
9912 else if (aFlags & SaveS_Force)
9913 {
9914 fNeedsWrite = true; // always, no need to compare
9915 }
9916 else
9917 {
9918 if (!mData->mCurrentStateModified)
9919 {
9920 // do a deep compare of the settings that we just saved with the settings
9921 // previously stored in the config file; this invokes MachineConfigFile::operator==
9922 // which does a deep compare of all the settings, which is expensive but less expensive
9923 // than writing out XML in vain
9924 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9925
9926 // could still be modified if any settings changed
9927 mData->mCurrentStateModified = fAnySettingsChanged;
9928
9929 fNeedsWrite = fAnySettingsChanged;
9930 }
9931 else
9932 fNeedsWrite = true;
9933 }
9934
9935 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9936
9937 if (fNeedsWrite)
9938 // now spit it all out!
9939 pNewConfig->write(mData->m_strConfigFileFull);
9940
9941 mData->pMachineConfigFile = pNewConfig;
9942 delete pOldConfig;
9943 i_commit();
9944
9945 // after saving settings, we are no longer different from the XML on disk
9946 mData->flModifications = 0;
9947 }
9948 catch (HRESULT err)
9949 {
9950 // we assume that error info is set by the thrower
9951 rc = err;
9952
9953 // restore old config
9954 delete pNewConfig;
9955 mData->pMachineConfigFile = pOldConfig;
9956 }
9957 catch (...)
9958 {
9959 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9960 }
9961
9962 if (fNeedsWrite)
9963 {
9964 /* Fire the data change event, even on failure (since we've already
9965 * committed all data). This is done only for SessionMachines because
9966 * mutable Machine instances are always not registered (i.e. private
9967 * to the client process that creates them) and thus don't need to
9968 * inform callbacks. */
9969 if (i_isSessionMachine())
9970 mParent->i_onMachineDataChanged(mData->mUuid);
9971 }
9972
9973 LogFlowThisFunc(("rc=%08X\n", rc));
9974 LogFlowThisFuncLeave();
9975 return rc;
9976}
9977
9978/**
9979 * Implementation for saving the machine settings into the given
9980 * settings::MachineConfigFile instance. This copies machine extradata
9981 * from the previous machine config file in the instance data, if any.
9982 *
9983 * This gets called from two locations:
9984 *
9985 * -- Machine::i_saveSettings(), during the regular XML writing;
9986 *
9987 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9988 * exported to OVF and we write the VirtualBox proprietary XML
9989 * into a <vbox:Machine> tag.
9990 *
9991 * This routine fills all the fields in there, including snapshots, *except*
9992 * for the following:
9993 *
9994 * -- fCurrentStateModified. There is some special logic associated with that.
9995 *
9996 * The caller can then call MachineConfigFile::write() or do something else
9997 * with it.
9998 *
9999 * Caller must hold the machine lock!
10000 *
10001 * This throws XML errors and HRESULT, so the caller must have a catch block!
10002 */
10003void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10004{
10005 // deep copy extradata, being extra careful with self assignment (the STL
10006 // map assignment on Mac OS X clang based Xcode isn't checking)
10007 if (&config != mData->pMachineConfigFile)
10008 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10009
10010 config.uuid = mData->mUuid;
10011
10012 // copy name, description, OS type, teleport, UTC etc.
10013 config.machineUserData = mUserData->s;
10014
10015 if ( mData->mMachineState == MachineState_Saved
10016 || mData->mMachineState == MachineState_AbortedSaved
10017 || mData->mMachineState == MachineState_Restoring
10018 // when doing certain snapshot operations we may or may not have
10019 // a saved state in the current state, so keep everything as is
10020 || ( ( mData->mMachineState == MachineState_Snapshotting
10021 || mData->mMachineState == MachineState_DeletingSnapshot
10022 || mData->mMachineState == MachineState_RestoringSnapshot)
10023 && (!mSSData->strStateFilePath.isEmpty())
10024 )
10025 )
10026 {
10027 Assert(!mSSData->strStateFilePath.isEmpty());
10028 /* try to make the file name relative to the settings file dir */
10029 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10030 }
10031 else
10032 {
10033 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10034 config.strStateFile.setNull();
10035 }
10036
10037 if (mData->mCurrentSnapshot)
10038 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10039 else
10040 config.uuidCurrentSnapshot.clear();
10041
10042 config.timeLastStateChange = mData->mLastStateChange;
10043 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10044 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10045
10046 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10047 if (FAILED(rc)) throw rc;
10048
10049 // save machine's media registry if this is VirtualBox 4.0 or later
10050 if (config.canHaveOwnMediaRegistry())
10051 {
10052 // determine machine folder
10053 Utf8Str strMachineFolder = i_getSettingsFileFull();
10054 strMachineFolder.stripFilename();
10055 mParent->i_saveMediaRegistry(config.mediaRegistry,
10056 i_getId(), // only media with registry ID == machine UUID
10057 strMachineFolder);
10058 // this throws HRESULT
10059 }
10060
10061 // save snapshots
10062 rc = i_saveAllSnapshots(config);
10063 if (FAILED(rc)) throw rc;
10064}
10065
10066/**
10067 * Saves all snapshots of the machine into the given machine config file. Called
10068 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10069 * @param config
10070 * @return
10071 */
10072HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10073{
10074 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10075
10076 HRESULT rc = S_OK;
10077
10078 try
10079 {
10080 config.llFirstSnapshot.clear();
10081
10082 if (mData->mFirstSnapshot)
10083 {
10084 // the settings use a list for "the first snapshot"
10085 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10086
10087 // get reference to the snapshot on the list and work on that
10088 // element straight in the list to avoid excessive copying later
10089 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10090 if (FAILED(rc)) throw rc;
10091 }
10092
10093// if (mType == IsSessionMachine)
10094// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10095
10096 }
10097 catch (HRESULT err)
10098 {
10099 /* we assume that error info is set by the thrower */
10100 rc = err;
10101 }
10102 catch (...)
10103 {
10104 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10105 }
10106
10107 return rc;
10108}
10109
10110/**
10111 * Saves the VM hardware configuration. It is assumed that the
10112 * given node is empty.
10113 *
10114 * @param data Reference to the settings object for the hardware config.
10115 * @param pDbg Pointer to the settings object for the debugging config
10116 * which happens to live in mHWData.
10117 * @param pAutostart Pointer to the settings object for the autostart config
10118 * which happens to live in mHWData.
10119 */
10120HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10121 settings::Autostart *pAutostart)
10122{
10123 HRESULT rc = S_OK;
10124
10125 try
10126 {
10127 /* The hardware version attribute (optional).
10128 Automatically upgrade from 1 to current default hardware version
10129 when there is no saved state. (ugly!) */
10130 if ( mHWData->mHWVersion == "1"
10131 && mSSData->strStateFilePath.isEmpty()
10132 )
10133 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10134
10135 data.strVersion = mHWData->mHWVersion;
10136 data.uuid = mHWData->mHardwareUUID;
10137
10138 // CPU
10139 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10140 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10141 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10142 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10143 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10144 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10145 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10146 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10147 data.fPAE = !!mHWData->mPAEEnabled;
10148 data.enmLongMode = mHWData->mLongMode;
10149 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10150 data.fAPIC = !!mHWData->mAPIC;
10151 data.fX2APIC = !!mHWData->mX2APIC;
10152 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10153 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10154 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10155 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10156 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10157 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10158 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10159 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10160 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10161 data.cCPUs = mHWData->mCPUCount;
10162 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10163 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10164 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10165 data.strCpuProfile = mHWData->mCpuProfile;
10166
10167 data.llCpus.clear();
10168 if (data.fCpuHotPlug)
10169 {
10170 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10171 {
10172 if (mHWData->mCPUAttached[idx])
10173 {
10174 settings::Cpu cpu;
10175 cpu.ulId = idx;
10176 data.llCpus.push_back(cpu);
10177 }
10178 }
10179 }
10180
10181 /* Standard and Extended CPUID leafs. */
10182 data.llCpuIdLeafs.clear();
10183 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10184
10185 // memory
10186 data.ulMemorySizeMB = mHWData->mMemorySize;
10187 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10188
10189 // firmware
10190 data.firmwareType = mHWData->mFirmwareType;
10191
10192 // HID
10193 data.pointingHIDType = mHWData->mPointingHIDType;
10194 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10195
10196 // chipset
10197 data.chipsetType = mHWData->mChipsetType;
10198
10199 // iommu
10200 data.iommuType = mHWData->mIommuType;
10201
10202 // paravirt
10203 data.paravirtProvider = mHWData->mParavirtProvider;
10204 data.strParavirtDebug = mHWData->mParavirtDebug;
10205
10206 // emulated USB card reader
10207 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10208
10209 // HPET
10210 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10211
10212 // boot order
10213 data.mapBootOrder.clear();
10214 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10215 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10216
10217 /* VRDEServer settings (optional) */
10218 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10219 if (FAILED(rc)) throw rc;
10220
10221 /* BIOS settings (required) */
10222 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10223 if (FAILED(rc)) throw rc;
10224
10225 /* Trusted Platform Module settings (required) */
10226 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10227 if (FAILED(rc)) throw rc;
10228
10229 /* NVRAM settings (required) */
10230 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10231 if (FAILED(rc)) throw rc;
10232
10233 /* Recording settings (required) */
10234 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10235 if (FAILED(rc)) throw rc;
10236
10237 /* GraphicsAdapter settings (required) */
10238 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10239 if (FAILED(rc)) throw rc;
10240
10241 /* USB Controller (required) */
10242 data.usbSettings.llUSBControllers.clear();
10243 for (USBControllerList::const_iterator
10244 it = mUSBControllers->begin();
10245 it != mUSBControllers->end();
10246 ++it)
10247 {
10248 ComObjPtr<USBController> ctrl = *it;
10249 settings::USBController settingsCtrl;
10250
10251 settingsCtrl.strName = ctrl->i_getName();
10252 settingsCtrl.enmType = ctrl->i_getControllerType();
10253
10254 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10255 }
10256
10257 /* USB device filters (required) */
10258 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10259 if (FAILED(rc)) throw rc;
10260
10261 /* Network adapters (required) */
10262 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10263 data.llNetworkAdapters.clear();
10264 /* Write out only the nominal number of network adapters for this
10265 * chipset type. Since Machine::commit() hasn't been called there
10266 * may be extra NIC settings in the vector. */
10267 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10268 {
10269 settings::NetworkAdapter nic;
10270 nic.ulSlot = (uint32_t)slot;
10271 /* paranoia check... must not be NULL, but must not crash either. */
10272 if (mNetworkAdapters[slot])
10273 {
10274 if (mNetworkAdapters[slot]->i_hasDefaults())
10275 continue;
10276
10277 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10278 if (FAILED(rc)) throw rc;
10279
10280 data.llNetworkAdapters.push_back(nic);
10281 }
10282 }
10283
10284 /* Serial ports */
10285 data.llSerialPorts.clear();
10286 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10287 {
10288 if (mSerialPorts[slot]->i_hasDefaults())
10289 continue;
10290
10291 settings::SerialPort s;
10292 s.ulSlot = slot;
10293 rc = mSerialPorts[slot]->i_saveSettings(s);
10294 if (FAILED(rc)) return rc;
10295
10296 data.llSerialPorts.push_back(s);
10297 }
10298
10299 /* Parallel ports */
10300 data.llParallelPorts.clear();
10301 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10302 {
10303 if (mParallelPorts[slot]->i_hasDefaults())
10304 continue;
10305
10306 settings::ParallelPort p;
10307 p.ulSlot = slot;
10308 rc = mParallelPorts[slot]->i_saveSettings(p);
10309 if (FAILED(rc)) return rc;
10310
10311 data.llParallelPorts.push_back(p);
10312 }
10313
10314 /* Audio adapter */
10315 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10316 if (FAILED(rc)) return rc;
10317
10318 rc = i_saveStorageControllers(data.storage);
10319 if (FAILED(rc)) return rc;
10320
10321 /* Shared folders */
10322 data.llSharedFolders.clear();
10323 for (HWData::SharedFolderList::const_iterator
10324 it = mHWData->mSharedFolders.begin();
10325 it != mHWData->mSharedFolders.end();
10326 ++it)
10327 {
10328 SharedFolder *pSF = *it;
10329 AutoCaller sfCaller(pSF);
10330 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10331 settings::SharedFolder sf;
10332 sf.strName = pSF->i_getName();
10333 sf.strHostPath = pSF->i_getHostPath();
10334 sf.fWritable = !!pSF->i_isWritable();
10335 sf.fAutoMount = !!pSF->i_isAutoMounted();
10336 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10337
10338 data.llSharedFolders.push_back(sf);
10339 }
10340
10341 // clipboard
10342 data.clipboardMode = mHWData->mClipboardMode;
10343 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10344
10345 // drag'n'drop
10346 data.dndMode = mHWData->mDnDMode;
10347
10348 /* Guest */
10349 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10350
10351 // IO settings
10352 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10353 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10354
10355 /* BandwidthControl (required) */
10356 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10357 if (FAILED(rc)) throw rc;
10358
10359 /* Host PCI devices */
10360 data.pciAttachments.clear();
10361 for (HWData::PCIDeviceAssignmentList::const_iterator
10362 it = mHWData->mPCIDeviceAssignments.begin();
10363 it != mHWData->mPCIDeviceAssignments.end();
10364 ++it)
10365 {
10366 ComObjPtr<PCIDeviceAttachment> pda = *it;
10367 settings::HostPCIDeviceAttachment hpda;
10368
10369 rc = pda->i_saveSettings(hpda);
10370 if (FAILED(rc)) throw rc;
10371
10372 data.pciAttachments.push_back(hpda);
10373 }
10374
10375 // guest properties
10376 data.llGuestProperties.clear();
10377#ifdef VBOX_WITH_GUEST_PROPS
10378 for (HWData::GuestPropertyMap::const_iterator
10379 it = mHWData->mGuestProperties.begin();
10380 it != mHWData->mGuestProperties.end();
10381 ++it)
10382 {
10383 HWData::GuestProperty property = it->second;
10384
10385 /* Remove transient guest properties at shutdown unless we
10386 * are saving state. Note that restoring snapshot intentionally
10387 * keeps them, they will be removed if appropriate once the final
10388 * machine state is set (as crashes etc. need to work). */
10389 if ( ( mData->mMachineState == MachineState_PoweredOff
10390 || mData->mMachineState == MachineState_Aborted
10391 || mData->mMachineState == MachineState_Teleported)
10392 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10393 continue;
10394 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10395 prop.strName = it->first;
10396 prop.strValue = property.strValue;
10397 prop.timestamp = (uint64_t)property.mTimestamp;
10398 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10399 GuestPropWriteFlags(property.mFlags, szFlags);
10400 prop.strFlags = szFlags;
10401
10402 data.llGuestProperties.push_back(prop);
10403 }
10404
10405 /* I presume this doesn't require a backup(). */
10406 mData->mGuestPropertiesModified = FALSE;
10407#endif /* VBOX_WITH_GUEST_PROPS defined */
10408
10409 *pDbg = mHWData->mDebugging;
10410 *pAutostart = mHWData->mAutostart;
10411
10412 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10413 }
10414 catch (std::bad_alloc &)
10415 {
10416 return E_OUTOFMEMORY;
10417 }
10418
10419 AssertComRC(rc);
10420 return rc;
10421}
10422
10423/**
10424 * Saves the storage controller configuration.
10425 *
10426 * @param data storage settings.
10427 */
10428HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10429{
10430 data.llStorageControllers.clear();
10431
10432 for (StorageControllerList::const_iterator
10433 it = mStorageControllers->begin();
10434 it != mStorageControllers->end();
10435 ++it)
10436 {
10437 HRESULT rc;
10438 ComObjPtr<StorageController> pCtl = *it;
10439
10440 settings::StorageController ctl;
10441 ctl.strName = pCtl->i_getName();
10442 ctl.controllerType = pCtl->i_getControllerType();
10443 ctl.storageBus = pCtl->i_getStorageBus();
10444 ctl.ulInstance = pCtl->i_getInstance();
10445 ctl.fBootable = pCtl->i_getBootable();
10446
10447 /* Save the port count. */
10448 ULONG portCount;
10449 rc = pCtl->COMGETTER(PortCount)(&portCount);
10450 ComAssertComRCRet(rc, rc);
10451 ctl.ulPortCount = portCount;
10452
10453 /* Save fUseHostIOCache */
10454 BOOL fUseHostIOCache;
10455 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10456 ComAssertComRCRet(rc, rc);
10457 ctl.fUseHostIOCache = !!fUseHostIOCache;
10458
10459 /* save the devices now. */
10460 rc = i_saveStorageDevices(pCtl, ctl);
10461 ComAssertComRCRet(rc, rc);
10462
10463 data.llStorageControllers.push_back(ctl);
10464 }
10465
10466 return S_OK;
10467}
10468
10469/**
10470 * Saves the hard disk configuration.
10471 */
10472HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10473 settings::StorageController &data)
10474{
10475 MediumAttachmentList atts;
10476
10477 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10478 if (FAILED(rc)) return rc;
10479
10480 data.llAttachedDevices.clear();
10481 for (MediumAttachmentList::const_iterator
10482 it = atts.begin();
10483 it != atts.end();
10484 ++it)
10485 {
10486 settings::AttachedDevice dev;
10487 IMediumAttachment *iA = *it;
10488 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10489 Medium *pMedium = pAttach->i_getMedium();
10490
10491 dev.deviceType = pAttach->i_getType();
10492 dev.lPort = pAttach->i_getPort();
10493 dev.lDevice = pAttach->i_getDevice();
10494 dev.fPassThrough = pAttach->i_getPassthrough();
10495 dev.fHotPluggable = pAttach->i_getHotPluggable();
10496 if (pMedium)
10497 {
10498 if (pMedium->i_isHostDrive())
10499 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10500 else
10501 dev.uuid = pMedium->i_getId();
10502 dev.fTempEject = pAttach->i_getTempEject();
10503 dev.fNonRotational = pAttach->i_getNonRotational();
10504 dev.fDiscard = pAttach->i_getDiscard();
10505 }
10506
10507 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10508
10509 data.llAttachedDevices.push_back(dev);
10510 }
10511
10512 return S_OK;
10513}
10514
10515/**
10516 * Saves machine state settings as defined by aFlags
10517 * (SaveSTS_* values).
10518 *
10519 * @param aFlags Combination of SaveSTS_* flags.
10520 *
10521 * @note Locks objects for writing.
10522 */
10523HRESULT Machine::i_saveStateSettings(int aFlags)
10524{
10525 if (aFlags == 0)
10526 return S_OK;
10527
10528 AutoCaller autoCaller(this);
10529 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10530
10531 /* This object's write lock is also necessary to serialize file access
10532 * (prevent concurrent reads and writes) */
10533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10534
10535 HRESULT rc = S_OK;
10536
10537 Assert(mData->pMachineConfigFile);
10538
10539 try
10540 {
10541 if (aFlags & SaveSTS_CurStateModified)
10542 mData->pMachineConfigFile->fCurrentStateModified = true;
10543
10544 if (aFlags & SaveSTS_StateFilePath)
10545 {
10546 if (!mSSData->strStateFilePath.isEmpty())
10547 /* try to make the file name relative to the settings file dir */
10548 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10549 else
10550 mData->pMachineConfigFile->strStateFile.setNull();
10551 }
10552
10553 if (aFlags & SaveSTS_StateTimeStamp)
10554 {
10555 Assert( mData->mMachineState != MachineState_Aborted
10556 || mSSData->strStateFilePath.isEmpty());
10557
10558 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10559
10560 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10561 || mData->mMachineState == MachineState_AbortedSaved);
10562/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10563 }
10564
10565 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10566 }
10567 catch (...)
10568 {
10569 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10570 }
10571
10572 return rc;
10573}
10574
10575/**
10576 * Ensures that the given medium is added to a media registry. If this machine
10577 * was created with 4.0 or later, then the machine registry is used. Otherwise
10578 * the global VirtualBox media registry is used.
10579 *
10580 * Caller must NOT hold machine lock, media tree or any medium locks!
10581 *
10582 * @param pMedium
10583 */
10584void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10585{
10586 /* Paranoia checks: do not hold machine or media tree locks. */
10587 AssertReturnVoid(!isWriteLockOnCurrentThread());
10588 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10589
10590 ComObjPtr<Medium> pBase;
10591 {
10592 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10593 pBase = pMedium->i_getBase();
10594 }
10595
10596 /* Paranoia checks: do not hold medium locks. */
10597 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10598 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10599
10600 // decide which medium registry to use now that the medium is attached:
10601 Guid uuid;
10602 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10603 if (fCanHaveOwnMediaRegistry)
10604 // machine XML is VirtualBox 4.0 or higher:
10605 uuid = i_getId(); // machine UUID
10606 else
10607 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10608
10609 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10610 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10611 if (pMedium->i_addRegistry(uuid))
10612 mParent->i_markRegistryModified(uuid);
10613
10614 /* For more complex hard disk structures it can happen that the base
10615 * medium isn't yet associated with any medium registry. Do that now. */
10616 if (pMedium != pBase)
10617 {
10618 /* Tree lock needed by Medium::addRegistry when recursing. */
10619 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10620 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10621 {
10622 treeLock.release();
10623 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10624 treeLock.acquire();
10625 }
10626 if (pBase->i_addRegistryRecursive(uuid))
10627 {
10628 treeLock.release();
10629 mParent->i_markRegistryModified(uuid);
10630 }
10631 }
10632}
10633
10634/**
10635 * Creates differencing hard disks for all normal hard disks attached to this
10636 * machine and a new set of attachments to refer to created disks.
10637 *
10638 * Used when taking a snapshot or when deleting the current state. Gets called
10639 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10640 *
10641 * This method assumes that mMediumAttachments contains the original hard disk
10642 * attachments it needs to create diffs for. On success, these attachments will
10643 * be replaced with the created diffs.
10644 *
10645 * Attachments with non-normal hard disks are left as is.
10646 *
10647 * If @a aOnline is @c false then the original hard disks that require implicit
10648 * diffs will be locked for reading. Otherwise it is assumed that they are
10649 * already locked for writing (when the VM was started). Note that in the latter
10650 * case it is responsibility of the caller to lock the newly created diffs for
10651 * writing if this method succeeds.
10652 *
10653 * @param aProgress Progress object to run (must contain at least as
10654 * many operations left as the number of hard disks
10655 * attached).
10656 * @param aWeight Weight of this operation.
10657 * @param aOnline Whether the VM was online prior to this operation.
10658 *
10659 * @note The progress object is not marked as completed, neither on success nor
10660 * on failure. This is a responsibility of the caller.
10661 *
10662 * @note Locks this object and the media tree for writing.
10663 */
10664HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10665 ULONG aWeight,
10666 bool aOnline)
10667{
10668 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10669
10670 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10671 AssertReturn(!!pProgressControl, E_INVALIDARG);
10672
10673 AutoCaller autoCaller(this);
10674 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10675
10676 AutoMultiWriteLock2 alock(this->lockHandle(),
10677 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10678
10679 /* must be in a protective state because we release the lock below */
10680 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10681 || mData->mMachineState == MachineState_OnlineSnapshotting
10682 || mData->mMachineState == MachineState_LiveSnapshotting
10683 || mData->mMachineState == MachineState_RestoringSnapshot
10684 || mData->mMachineState == MachineState_DeletingSnapshot
10685 , E_FAIL);
10686
10687 HRESULT rc = S_OK;
10688
10689 // use appropriate locked media map (online or offline)
10690 MediumLockListMap lockedMediaOffline;
10691 MediumLockListMap *lockedMediaMap;
10692 if (aOnline)
10693 lockedMediaMap = &mData->mSession.mLockedMedia;
10694 else
10695 lockedMediaMap = &lockedMediaOffline;
10696
10697 try
10698 {
10699 if (!aOnline)
10700 {
10701 /* lock all attached hard disks early to detect "in use"
10702 * situations before creating actual diffs */
10703 for (MediumAttachmentList::const_iterator
10704 it = mMediumAttachments->begin();
10705 it != mMediumAttachments->end();
10706 ++it)
10707 {
10708 MediumAttachment *pAtt = *it;
10709 if (pAtt->i_getType() == DeviceType_HardDisk)
10710 {
10711 Medium *pMedium = pAtt->i_getMedium();
10712 Assert(pMedium);
10713
10714 MediumLockList *pMediumLockList(new MediumLockList());
10715 alock.release();
10716 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10717 NULL /* pToLockWrite */,
10718 false /* fMediumLockWriteAll */,
10719 NULL,
10720 *pMediumLockList);
10721 alock.acquire();
10722 if (FAILED(rc))
10723 {
10724 delete pMediumLockList;
10725 throw rc;
10726 }
10727 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10728 if (FAILED(rc))
10729 {
10730 throw setError(rc,
10731 tr("Collecting locking information for all attached media failed"));
10732 }
10733 }
10734 }
10735
10736 /* Now lock all media. If this fails, nothing is locked. */
10737 alock.release();
10738 rc = lockedMediaMap->Lock();
10739 alock.acquire();
10740 if (FAILED(rc))
10741 {
10742 throw setError(rc,
10743 tr("Locking of attached media failed"));
10744 }
10745 }
10746
10747 /* remember the current list (note that we don't use backup() since
10748 * mMediumAttachments may be already backed up) */
10749 MediumAttachmentList atts = *mMediumAttachments.data();
10750
10751 /* start from scratch */
10752 mMediumAttachments->clear();
10753
10754 /* go through remembered attachments and create diffs for normal hard
10755 * disks and attach them */
10756 for (MediumAttachmentList::const_iterator
10757 it = atts.begin();
10758 it != atts.end();
10759 ++it)
10760 {
10761 MediumAttachment *pAtt = *it;
10762
10763 DeviceType_T devType = pAtt->i_getType();
10764 Medium *pMedium = pAtt->i_getMedium();
10765
10766 if ( devType != DeviceType_HardDisk
10767 || pMedium == NULL
10768 || pMedium->i_getType() != MediumType_Normal)
10769 {
10770 /* copy the attachment as is */
10771
10772 /** @todo the progress object created in SessionMachine::TakeSnaphot
10773 * only expects operations for hard disks. Later other
10774 * device types need to show up in the progress as well. */
10775 if (devType == DeviceType_HardDisk)
10776 {
10777 if (pMedium == NULL)
10778 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10779 aWeight); // weight
10780 else
10781 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10782 pMedium->i_getBase()->i_getName().c_str()).raw(),
10783 aWeight); // weight
10784 }
10785
10786 mMediumAttachments->push_back(pAtt);
10787 continue;
10788 }
10789
10790 /* need a diff */
10791 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10792 pMedium->i_getBase()->i_getName().c_str()).raw(),
10793 aWeight); // weight
10794
10795 Utf8Str strFullSnapshotFolder;
10796 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10797
10798 ComObjPtr<Medium> diff;
10799 diff.createObject();
10800 // store the diff in the same registry as the parent
10801 // (this cannot fail here because we can't create implicit diffs for
10802 // unregistered images)
10803 Guid uuidRegistryParent;
10804 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10805 Assert(fInRegistry); NOREF(fInRegistry);
10806 rc = diff->init(mParent,
10807 pMedium->i_getPreferredDiffFormat(),
10808 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10809 uuidRegistryParent,
10810 DeviceType_HardDisk);
10811 if (FAILED(rc)) throw rc;
10812
10813 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10814 * the push_back? Looks like we're going to release medium with the
10815 * wrong kind of lock (general issue with if we fail anywhere at all)
10816 * and an orphaned VDI in the snapshots folder. */
10817
10818 /* update the appropriate lock list */
10819 MediumLockList *pMediumLockList;
10820 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10821 AssertComRCThrowRC(rc);
10822 if (aOnline)
10823 {
10824 alock.release();
10825 /* The currently attached medium will be read-only, change
10826 * the lock type to read. */
10827 rc = pMediumLockList->Update(pMedium, false);
10828 alock.acquire();
10829 AssertComRCThrowRC(rc);
10830 }
10831
10832 /* release the locks before the potentially lengthy operation */
10833 alock.release();
10834 rc = pMedium->i_createDiffStorage(diff,
10835 pMedium->i_getPreferredDiffVariant(),
10836 pMediumLockList,
10837 NULL /* aProgress */,
10838 true /* aWait */,
10839 false /* aNotify */);
10840 alock.acquire();
10841 if (FAILED(rc)) throw rc;
10842
10843 /* actual lock list update is done in Machine::i_commitMedia */
10844
10845 rc = diff->i_addBackReference(mData->mUuid);
10846 AssertComRCThrowRC(rc);
10847
10848 /* add a new attachment */
10849 ComObjPtr<MediumAttachment> attachment;
10850 attachment.createObject();
10851 rc = attachment->init(this,
10852 diff,
10853 pAtt->i_getControllerName(),
10854 pAtt->i_getPort(),
10855 pAtt->i_getDevice(),
10856 DeviceType_HardDisk,
10857 true /* aImplicit */,
10858 false /* aPassthrough */,
10859 false /* aTempEject */,
10860 pAtt->i_getNonRotational(),
10861 pAtt->i_getDiscard(),
10862 pAtt->i_getHotPluggable(),
10863 pAtt->i_getBandwidthGroup());
10864 if (FAILED(rc)) throw rc;
10865
10866 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10867 AssertComRCThrowRC(rc);
10868 mMediumAttachments->push_back(attachment);
10869 }
10870 }
10871 catch (HRESULT aRC) { rc = aRC; }
10872
10873 /* unlock all hard disks we locked when there is no VM */
10874 if (!aOnline)
10875 {
10876 ErrorInfoKeeper eik;
10877
10878 HRESULT rc1 = lockedMediaMap->Clear();
10879 AssertComRC(rc1);
10880 }
10881
10882 return rc;
10883}
10884
10885/**
10886 * Deletes implicit differencing hard disks created either by
10887 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10888 * mMediumAttachments.
10889 *
10890 * Note that to delete hard disks created by #attachDevice() this method is
10891 * called from #i_rollbackMedia() when the changes are rolled back.
10892 *
10893 * @note Locks this object and the media tree for writing.
10894 */
10895HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10896{
10897 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10898
10899 AutoCaller autoCaller(this);
10900 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10901
10902 AutoMultiWriteLock2 alock(this->lockHandle(),
10903 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10904
10905 /* We absolutely must have backed up state. */
10906 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10907
10908 /* Check if there are any implicitly created diff images. */
10909 bool fImplicitDiffs = false;
10910 for (MediumAttachmentList::const_iterator
10911 it = mMediumAttachments->begin();
10912 it != mMediumAttachments->end();
10913 ++it)
10914 {
10915 const ComObjPtr<MediumAttachment> &pAtt = *it;
10916 if (pAtt->i_isImplicit())
10917 {
10918 fImplicitDiffs = true;
10919 break;
10920 }
10921 }
10922 /* If there is nothing to do, leave early. This saves lots of image locking
10923 * effort. It also avoids a MachineStateChanged event without real reason.
10924 * This is important e.g. when loading a VM config, because there should be
10925 * no events. Otherwise API clients can become thoroughly confused for
10926 * inaccessible VMs (the code for loading VM configs uses this method for
10927 * cleanup if the config makes no sense), as they take such events as an
10928 * indication that the VM is alive, and they would force the VM config to
10929 * be reread, leading to an endless loop. */
10930 if (!fImplicitDiffs)
10931 return S_OK;
10932
10933 HRESULT rc = S_OK;
10934 MachineState_T oldState = mData->mMachineState;
10935
10936 /* will release the lock before the potentially lengthy operation,
10937 * so protect with the special state (unless already protected) */
10938 if ( oldState != MachineState_Snapshotting
10939 && oldState != MachineState_OnlineSnapshotting
10940 && oldState != MachineState_LiveSnapshotting
10941 && oldState != MachineState_RestoringSnapshot
10942 && oldState != MachineState_DeletingSnapshot
10943 && oldState != MachineState_DeletingSnapshotOnline
10944 && oldState != MachineState_DeletingSnapshotPaused
10945 )
10946 i_setMachineState(MachineState_SettingUp);
10947
10948 // use appropriate locked media map (online or offline)
10949 MediumLockListMap lockedMediaOffline;
10950 MediumLockListMap *lockedMediaMap;
10951 if (aOnline)
10952 lockedMediaMap = &mData->mSession.mLockedMedia;
10953 else
10954 lockedMediaMap = &lockedMediaOffline;
10955
10956 try
10957 {
10958 if (!aOnline)
10959 {
10960 /* lock all attached hard disks early to detect "in use"
10961 * situations before deleting actual diffs */
10962 for (MediumAttachmentList::const_iterator
10963 it = mMediumAttachments->begin();
10964 it != mMediumAttachments->end();
10965 ++it)
10966 {
10967 MediumAttachment *pAtt = *it;
10968 if (pAtt->i_getType() == DeviceType_HardDisk)
10969 {
10970 Medium *pMedium = pAtt->i_getMedium();
10971 Assert(pMedium);
10972
10973 MediumLockList *pMediumLockList(new MediumLockList());
10974 alock.release();
10975 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10976 NULL /* pToLockWrite */,
10977 false /* fMediumLockWriteAll */,
10978 NULL,
10979 *pMediumLockList);
10980 alock.acquire();
10981
10982 if (FAILED(rc))
10983 {
10984 delete pMediumLockList;
10985 throw rc;
10986 }
10987
10988 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10989 if (FAILED(rc))
10990 throw rc;
10991 }
10992 }
10993
10994 if (FAILED(rc))
10995 throw rc;
10996 } // end of offline
10997
10998 /* Lock lists are now up to date and include implicitly created media */
10999
11000 /* Go through remembered attachments and delete all implicitly created
11001 * diffs and fix up the attachment information */
11002 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11003 MediumAttachmentList implicitAtts;
11004 for (MediumAttachmentList::const_iterator
11005 it = mMediumAttachments->begin();
11006 it != mMediumAttachments->end();
11007 ++it)
11008 {
11009 ComObjPtr<MediumAttachment> pAtt = *it;
11010 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11011 if (pMedium.isNull())
11012 continue;
11013
11014 // Implicit attachments go on the list for deletion and back references are removed.
11015 if (pAtt->i_isImplicit())
11016 {
11017 /* Deassociate and mark for deletion */
11018 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11019 rc = pMedium->i_removeBackReference(mData->mUuid);
11020 if (FAILED(rc))
11021 throw rc;
11022 implicitAtts.push_back(pAtt);
11023 continue;
11024 }
11025
11026 /* Was this medium attached before? */
11027 if (!i_findAttachment(oldAtts, pMedium))
11028 {
11029 /* no: de-associate */
11030 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11031 rc = pMedium->i_removeBackReference(mData->mUuid);
11032 if (FAILED(rc))
11033 throw rc;
11034 continue;
11035 }
11036 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11037 }
11038
11039 /* If there are implicit attachments to delete, throw away the lock
11040 * map contents (which will unlock all media) since the medium
11041 * attachments will be rolled back. Below we need to completely
11042 * recreate the lock map anyway since it is infinitely complex to
11043 * do this incrementally (would need reconstructing each attachment
11044 * change, which would be extremely hairy). */
11045 if (implicitAtts.size() != 0)
11046 {
11047 ErrorInfoKeeper eik;
11048
11049 HRESULT rc1 = lockedMediaMap->Clear();
11050 AssertComRC(rc1);
11051 }
11052
11053 /* rollback hard disk changes */
11054 mMediumAttachments.rollback();
11055
11056 MultiResult mrc(S_OK);
11057
11058 // Delete unused implicit diffs.
11059 if (implicitAtts.size() != 0)
11060 {
11061 alock.release();
11062
11063 for (MediumAttachmentList::const_iterator
11064 it = implicitAtts.begin();
11065 it != implicitAtts.end();
11066 ++it)
11067 {
11068 // Remove medium associated with this attachment.
11069 ComObjPtr<MediumAttachment> pAtt = *it;
11070 Assert(pAtt);
11071 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11072 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11073 Assert(pMedium);
11074
11075 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11076 // continue on delete failure, just collect error messages
11077 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11078 pMedium->i_getLocationFull().c_str() ));
11079 mrc = rc;
11080 }
11081 // Clear the list of deleted implicit attachments now, while not
11082 // holding the lock, as it will ultimately trigger Medium::uninit()
11083 // calls which assume that the media tree lock isn't held.
11084 implicitAtts.clear();
11085
11086 alock.acquire();
11087
11088 /* if there is a VM recreate media lock map as mentioned above,
11089 * otherwise it is a waste of time and we leave things unlocked */
11090 if (aOnline)
11091 {
11092 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11093 /* must never be NULL, but better safe than sorry */
11094 if (!pMachine.isNull())
11095 {
11096 alock.release();
11097 rc = mData->mSession.mMachine->i_lockMedia();
11098 alock.acquire();
11099 if (FAILED(rc))
11100 throw rc;
11101 }
11102 }
11103 }
11104 }
11105 catch (HRESULT aRC) {rc = aRC;}
11106
11107 if (mData->mMachineState == MachineState_SettingUp)
11108 i_setMachineState(oldState);
11109
11110 /* unlock all hard disks we locked when there is no VM */
11111 if (!aOnline)
11112 {
11113 ErrorInfoKeeper eik;
11114
11115 HRESULT rc1 = lockedMediaMap->Clear();
11116 AssertComRC(rc1);
11117 }
11118
11119 return rc;
11120}
11121
11122
11123/**
11124 * Looks through the given list of media attachments for one with the given parameters
11125 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11126 * can be searched as well if needed.
11127 *
11128 * @param ll
11129 * @param aControllerName
11130 * @param aControllerPort
11131 * @param aDevice
11132 * @return
11133 */
11134MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11135 const Utf8Str &aControllerName,
11136 LONG aControllerPort,
11137 LONG aDevice)
11138{
11139 for (MediumAttachmentList::const_iterator
11140 it = ll.begin();
11141 it != ll.end();
11142 ++it)
11143 {
11144 MediumAttachment *pAttach = *it;
11145 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11146 return pAttach;
11147 }
11148
11149 return NULL;
11150}
11151
11152/**
11153 * Looks through the given list of media attachments for one with the given parameters
11154 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11155 * can be searched as well if needed.
11156 *
11157 * @param ll
11158 * @param pMedium
11159 * @return
11160 */
11161MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11162 ComObjPtr<Medium> pMedium)
11163{
11164 for (MediumAttachmentList::const_iterator
11165 it = ll.begin();
11166 it != ll.end();
11167 ++it)
11168 {
11169 MediumAttachment *pAttach = *it;
11170 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11171 if (pMediumThis == pMedium)
11172 return pAttach;
11173 }
11174
11175 return NULL;
11176}
11177
11178/**
11179 * Looks through the given list of media attachments for one with the given parameters
11180 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11181 * can be searched as well if needed.
11182 *
11183 * @param ll
11184 * @param id
11185 * @return
11186 */
11187MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11188 Guid &id)
11189{
11190 for (MediumAttachmentList::const_iterator
11191 it = ll.begin();
11192 it != ll.end();
11193 ++it)
11194 {
11195 MediumAttachment *pAttach = *it;
11196 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11197 if (pMediumThis->i_getId() == id)
11198 return pAttach;
11199 }
11200
11201 return NULL;
11202}
11203
11204/**
11205 * Main implementation for Machine::DetachDevice. This also gets called
11206 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11207 *
11208 * @param pAttach Medium attachment to detach.
11209 * @param writeLock Machine write lock which the caller must have locked once.
11210 * This may be released temporarily in here.
11211 * @param pSnapshot If NULL, then the detachment is for the current machine.
11212 * Otherwise this is for a SnapshotMachine, and this must be
11213 * its snapshot.
11214 * @return
11215 */
11216HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11217 AutoWriteLock &writeLock,
11218 Snapshot *pSnapshot)
11219{
11220 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11221 DeviceType_T mediumType = pAttach->i_getType();
11222
11223 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11224
11225 if (pAttach->i_isImplicit())
11226 {
11227 /* attempt to implicitly delete the implicitly created diff */
11228
11229 /// @todo move the implicit flag from MediumAttachment to Medium
11230 /// and forbid any hard disk operation when it is implicit. Or maybe
11231 /// a special media state for it to make it even more simple.
11232
11233 Assert(mMediumAttachments.isBackedUp());
11234
11235 /* will release the lock before the potentially lengthy operation, so
11236 * protect with the special state */
11237 MachineState_T oldState = mData->mMachineState;
11238 i_setMachineState(MachineState_SettingUp);
11239
11240 writeLock.release();
11241
11242 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11243 true /*aWait*/,
11244 false /*aNotify*/);
11245
11246 writeLock.acquire();
11247
11248 i_setMachineState(oldState);
11249
11250 if (FAILED(rc)) return rc;
11251 }
11252
11253 i_setModified(IsModified_Storage);
11254 mMediumAttachments.backup();
11255 mMediumAttachments->remove(pAttach);
11256
11257 if (!oldmedium.isNull())
11258 {
11259 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11260 if (pSnapshot)
11261 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11262 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11263 else if (mediumType != DeviceType_HardDisk)
11264 oldmedium->i_removeBackReference(mData->mUuid);
11265 }
11266
11267 return S_OK;
11268}
11269
11270/**
11271 * Goes thru all media of the given list and
11272 *
11273 * 1) calls i_detachDevice() on each of them for this machine and
11274 * 2) adds all Medium objects found in the process to the given list,
11275 * depending on cleanupMode.
11276 *
11277 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11278 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11279 * media to the list.
11280 *
11281 * This gets called from Machine::Unregister, both for the actual Machine and
11282 * the SnapshotMachine objects that might be found in the snapshots.
11283 *
11284 * Requires caller and locking. The machine lock must be passed in because it
11285 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11286 *
11287 * @param writeLock Machine lock from top-level caller; this gets passed to
11288 * i_detachDevice.
11289 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11290 * object if called for a SnapshotMachine.
11291 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11292 * added to llMedia; if Full, then all media get added;
11293 * otherwise no media get added.
11294 * @param llMedia Caller's list to receive Medium objects which got detached so
11295 * caller can close() them, depending on cleanupMode.
11296 * @return
11297 */
11298HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11299 Snapshot *pSnapshot,
11300 CleanupMode_T cleanupMode,
11301 MediaList &llMedia)
11302{
11303 Assert(isWriteLockOnCurrentThread());
11304
11305 HRESULT rc;
11306
11307 // make a temporary list because i_detachDevice invalidates iterators into
11308 // mMediumAttachments
11309 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11310
11311 for (MediumAttachmentList::iterator
11312 it = llAttachments2.begin();
11313 it != llAttachments2.end();
11314 ++it)
11315 {
11316 ComObjPtr<MediumAttachment> &pAttach = *it;
11317 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11318
11319 if (!pMedium.isNull())
11320 {
11321 AutoCaller mac(pMedium);
11322 if (FAILED(mac.rc())) return mac.rc();
11323 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11324 DeviceType_T devType = pMedium->i_getDeviceType();
11325 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11326 && devType == DeviceType_HardDisk)
11327 || (cleanupMode == CleanupMode_Full)
11328 )
11329 {
11330 llMedia.push_back(pMedium);
11331 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11332 /* Not allowed to keep this lock as below we need the parent
11333 * medium lock, and the lock order is parent to child. */
11334 lock.release();
11335 /*
11336 * Search for medias which are not attached to any machine, but
11337 * in the chain to an attached disk. Mediums are only consided
11338 * if they are:
11339 * - have only one child
11340 * - no references to any machines
11341 * - are of normal medium type
11342 */
11343 while (!pParent.isNull())
11344 {
11345 AutoCaller mac1(pParent);
11346 if (FAILED(mac1.rc())) return mac1.rc();
11347 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11348 if (pParent->i_getChildren().size() == 1)
11349 {
11350 if ( pParent->i_getMachineBackRefCount() == 0
11351 && pParent->i_getType() == MediumType_Normal
11352 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11353 llMedia.push_back(pParent);
11354 }
11355 else
11356 break;
11357 pParent = pParent->i_getParent();
11358 }
11359 }
11360 }
11361
11362 // real machine: then we need to use the proper method
11363 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11364
11365 if (FAILED(rc))
11366 return rc;
11367 }
11368
11369 return S_OK;
11370}
11371
11372/**
11373 * Perform deferred hard disk detachments.
11374 *
11375 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11376 * changed (not backed up).
11377 *
11378 * If @a aOnline is @c true then this method will also unlock the old hard
11379 * disks for which the new implicit diffs were created and will lock these new
11380 * diffs for writing.
11381 *
11382 * @param aOnline Whether the VM was online prior to this operation.
11383 *
11384 * @note Locks this object for writing!
11385 */
11386void Machine::i_commitMedia(bool aOnline /*= false*/)
11387{
11388 AutoCaller autoCaller(this);
11389 AssertComRCReturnVoid(autoCaller.rc());
11390
11391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11392
11393 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11394
11395 HRESULT rc = S_OK;
11396
11397 /* no attach/detach operations -- nothing to do */
11398 if (!mMediumAttachments.isBackedUp())
11399 return;
11400
11401 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11402 bool fMediaNeedsLocking = false;
11403
11404 /* enumerate new attachments */
11405 for (MediumAttachmentList::const_iterator
11406 it = mMediumAttachments->begin();
11407 it != mMediumAttachments->end();
11408 ++it)
11409 {
11410 MediumAttachment *pAttach = *it;
11411
11412 pAttach->i_commit();
11413
11414 Medium *pMedium = pAttach->i_getMedium();
11415 bool fImplicit = pAttach->i_isImplicit();
11416
11417 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11418 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11419 fImplicit));
11420
11421 /** @todo convert all this Machine-based voodoo to MediumAttachment
11422 * based commit logic. */
11423 if (fImplicit)
11424 {
11425 /* convert implicit attachment to normal */
11426 pAttach->i_setImplicit(false);
11427
11428 if ( aOnline
11429 && pMedium
11430 && pAttach->i_getType() == DeviceType_HardDisk
11431 )
11432 {
11433 /* update the appropriate lock list */
11434 MediumLockList *pMediumLockList;
11435 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11436 AssertComRC(rc);
11437 if (pMediumLockList)
11438 {
11439 /* unlock if there's a need to change the locking */
11440 if (!fMediaNeedsLocking)
11441 {
11442 rc = mData->mSession.mLockedMedia.Unlock();
11443 AssertComRC(rc);
11444 fMediaNeedsLocking = true;
11445 }
11446 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11447 AssertComRC(rc);
11448 rc = pMediumLockList->Append(pMedium, true);
11449 AssertComRC(rc);
11450 }
11451 }
11452
11453 continue;
11454 }
11455
11456 if (pMedium)
11457 {
11458 /* was this medium attached before? */
11459 for (MediumAttachmentList::iterator
11460 oldIt = oldAtts.begin();
11461 oldIt != oldAtts.end();
11462 ++oldIt)
11463 {
11464 MediumAttachment *pOldAttach = *oldIt;
11465 if (pOldAttach->i_getMedium() == pMedium)
11466 {
11467 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11468
11469 /* yes: remove from old to avoid de-association */
11470 oldAtts.erase(oldIt);
11471 break;
11472 }
11473 }
11474 }
11475 }
11476
11477 /* enumerate remaining old attachments and de-associate from the
11478 * current machine state */
11479 for (MediumAttachmentList::const_iterator
11480 it = oldAtts.begin();
11481 it != oldAtts.end();
11482 ++it)
11483 {
11484 MediumAttachment *pAttach = *it;
11485 Medium *pMedium = pAttach->i_getMedium();
11486
11487 /* Detach only hard disks, since DVD/floppy media is detached
11488 * instantly in MountMedium. */
11489 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11490 {
11491 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11492
11493 /* now de-associate from the current machine state */
11494 rc = pMedium->i_removeBackReference(mData->mUuid);
11495 AssertComRC(rc);
11496
11497 if (aOnline)
11498 {
11499 /* unlock since medium is not used anymore */
11500 MediumLockList *pMediumLockList;
11501 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11502 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11503 {
11504 /* this happens for online snapshots, there the attachment
11505 * is changing, but only to a diff image created under
11506 * the old one, so there is no separate lock list */
11507 Assert(!pMediumLockList);
11508 }
11509 else
11510 {
11511 AssertComRC(rc);
11512 if (pMediumLockList)
11513 {
11514 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11515 AssertComRC(rc);
11516 }
11517 }
11518 }
11519 }
11520 }
11521
11522 /* take media locks again so that the locking state is consistent */
11523 if (fMediaNeedsLocking)
11524 {
11525 Assert(aOnline);
11526 rc = mData->mSession.mLockedMedia.Lock();
11527 AssertComRC(rc);
11528 }
11529
11530 /* commit the hard disk changes */
11531 mMediumAttachments.commit();
11532
11533 if (i_isSessionMachine())
11534 {
11535 /*
11536 * Update the parent machine to point to the new owner.
11537 * This is necessary because the stored parent will point to the
11538 * session machine otherwise and cause crashes or errors later
11539 * when the session machine gets invalid.
11540 */
11541 /** @todo Change the MediumAttachment class to behave like any other
11542 * class in this regard by creating peer MediumAttachment
11543 * objects for session machines and share the data with the peer
11544 * machine.
11545 */
11546 for (MediumAttachmentList::const_iterator
11547 it = mMediumAttachments->begin();
11548 it != mMediumAttachments->end();
11549 ++it)
11550 (*it)->i_updateParentMachine(mPeer);
11551
11552 /* attach new data to the primary machine and reshare it */
11553 mPeer->mMediumAttachments.attach(mMediumAttachments);
11554 }
11555
11556 return;
11557}
11558
11559/**
11560 * Perform deferred deletion of implicitly created diffs.
11561 *
11562 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11563 * changed (not backed up).
11564 *
11565 * @note Locks this object for writing!
11566 */
11567void Machine::i_rollbackMedia()
11568{
11569 AutoCaller autoCaller(this);
11570 AssertComRCReturnVoid(autoCaller.rc());
11571
11572 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11573 LogFlowThisFunc(("Entering rollbackMedia\n"));
11574
11575 HRESULT rc = S_OK;
11576
11577 /* no attach/detach operations -- nothing to do */
11578 if (!mMediumAttachments.isBackedUp())
11579 return;
11580
11581 /* enumerate new attachments */
11582 for (MediumAttachmentList::const_iterator
11583 it = mMediumAttachments->begin();
11584 it != mMediumAttachments->end();
11585 ++it)
11586 {
11587 MediumAttachment *pAttach = *it;
11588 /* Fix up the backrefs for DVD/floppy media. */
11589 if (pAttach->i_getType() != DeviceType_HardDisk)
11590 {
11591 Medium *pMedium = pAttach->i_getMedium();
11592 if (pMedium)
11593 {
11594 rc = pMedium->i_removeBackReference(mData->mUuid);
11595 AssertComRC(rc);
11596 }
11597 }
11598
11599 (*it)->i_rollback();
11600
11601 pAttach = *it;
11602 /* Fix up the backrefs for DVD/floppy media. */
11603 if (pAttach->i_getType() != DeviceType_HardDisk)
11604 {
11605 Medium *pMedium = pAttach->i_getMedium();
11606 if (pMedium)
11607 {
11608 rc = pMedium->i_addBackReference(mData->mUuid);
11609 AssertComRC(rc);
11610 }
11611 }
11612 }
11613
11614 /** @todo convert all this Machine-based voodoo to MediumAttachment
11615 * based rollback logic. */
11616 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11617
11618 return;
11619}
11620
11621/**
11622 * Returns true if the settings file is located in the directory named exactly
11623 * as the machine; this means, among other things, that the machine directory
11624 * should be auto-renamed.
11625 *
11626 * @param aSettingsDir if not NULL, the full machine settings file directory
11627 * name will be assigned there.
11628 *
11629 * @note Doesn't lock anything.
11630 * @note Not thread safe (must be called from this object's lock).
11631 */
11632bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11633{
11634 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11635 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11636 if (aSettingsDir)
11637 *aSettingsDir = strMachineDirName;
11638 strMachineDirName.stripPath(); // vmname
11639 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11640 strConfigFileOnly.stripPath() // vmname.vbox
11641 .stripSuffix(); // vmname
11642 /** @todo hack, make somehow use of ComposeMachineFilename */
11643 if (mUserData->s.fDirectoryIncludesUUID)
11644 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11645
11646 AssertReturn(!strMachineDirName.isEmpty(), false);
11647 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11648
11649 return strMachineDirName == strConfigFileOnly;
11650}
11651
11652/**
11653 * Discards all changes to machine settings.
11654 *
11655 * @param aNotify Whether to notify the direct session about changes or not.
11656 *
11657 * @note Locks objects for writing!
11658 */
11659void Machine::i_rollback(bool aNotify)
11660{
11661 AutoCaller autoCaller(this);
11662 AssertComRCReturn(autoCaller.rc(), (void)0);
11663
11664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11665
11666 if (!mStorageControllers.isNull())
11667 {
11668 if (mStorageControllers.isBackedUp())
11669 {
11670 /* unitialize all new devices (absent in the backed up list). */
11671 StorageControllerList *backedList = mStorageControllers.backedUpData();
11672 for (StorageControllerList::const_iterator
11673 it = mStorageControllers->begin();
11674 it != mStorageControllers->end();
11675 ++it)
11676 {
11677 if ( std::find(backedList->begin(), backedList->end(), *it)
11678 == backedList->end()
11679 )
11680 {
11681 (*it)->uninit();
11682 }
11683 }
11684
11685 /* restore the list */
11686 mStorageControllers.rollback();
11687 }
11688
11689 /* rollback any changes to devices after restoring the list */
11690 if (mData->flModifications & IsModified_Storage)
11691 {
11692 for (StorageControllerList::const_iterator
11693 it = mStorageControllers->begin();
11694 it != mStorageControllers->end();
11695 ++it)
11696 {
11697 (*it)->i_rollback();
11698 }
11699 }
11700 }
11701
11702 if (!mUSBControllers.isNull())
11703 {
11704 if (mUSBControllers.isBackedUp())
11705 {
11706 /* unitialize all new devices (absent in the backed up list). */
11707 USBControllerList *backedList = mUSBControllers.backedUpData();
11708 for (USBControllerList::const_iterator
11709 it = mUSBControllers->begin();
11710 it != mUSBControllers->end();
11711 ++it)
11712 {
11713 if ( std::find(backedList->begin(), backedList->end(), *it)
11714 == backedList->end()
11715 )
11716 {
11717 (*it)->uninit();
11718 }
11719 }
11720
11721 /* restore the list */
11722 mUSBControllers.rollback();
11723 }
11724
11725 /* rollback any changes to devices after restoring the list */
11726 if (mData->flModifications & IsModified_USB)
11727 {
11728 for (USBControllerList::const_iterator
11729 it = mUSBControllers->begin();
11730 it != mUSBControllers->end();
11731 ++it)
11732 {
11733 (*it)->i_rollback();
11734 }
11735 }
11736 }
11737
11738 mUserData.rollback();
11739
11740 mHWData.rollback();
11741
11742 if (mData->flModifications & IsModified_Storage)
11743 i_rollbackMedia();
11744
11745 if (mBIOSSettings)
11746 mBIOSSettings->i_rollback();
11747
11748 if (mTrustedPlatformModule)
11749 mTrustedPlatformModule->i_rollback();
11750
11751 if (mNvramStore)
11752 mNvramStore->i_rollback();
11753
11754 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11755 mRecordingSettings->i_rollback();
11756
11757 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11758 mGraphicsAdapter->i_rollback();
11759
11760 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11761 mVRDEServer->i_rollback();
11762
11763 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11764 mAudioAdapter->i_rollback();
11765
11766 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11767 mUSBDeviceFilters->i_rollback();
11768
11769 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11770 mBandwidthControl->i_rollback();
11771
11772 if (!mHWData.isNull())
11773 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11774 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11775 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11776 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11777
11778 if (mData->flModifications & IsModified_NetworkAdapters)
11779 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11780 if ( mNetworkAdapters[slot]
11781 && mNetworkAdapters[slot]->i_isModified())
11782 {
11783 mNetworkAdapters[slot]->i_rollback();
11784 networkAdapters[slot] = mNetworkAdapters[slot];
11785 }
11786
11787 if (mData->flModifications & IsModified_SerialPorts)
11788 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11789 if ( mSerialPorts[slot]
11790 && mSerialPorts[slot]->i_isModified())
11791 {
11792 mSerialPorts[slot]->i_rollback();
11793 serialPorts[slot] = mSerialPorts[slot];
11794 }
11795
11796 if (mData->flModifications & IsModified_ParallelPorts)
11797 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11798 if ( mParallelPorts[slot]
11799 && mParallelPorts[slot]->i_isModified())
11800 {
11801 mParallelPorts[slot]->i_rollback();
11802 parallelPorts[slot] = mParallelPorts[slot];
11803 }
11804
11805 if (aNotify)
11806 {
11807 /* inform the direct session about changes */
11808
11809 ComObjPtr<Machine> that = this;
11810 uint32_t flModifications = mData->flModifications;
11811 alock.release();
11812
11813 if (flModifications & IsModified_SharedFolders)
11814 that->i_onSharedFolderChange();
11815
11816 if (flModifications & IsModified_VRDEServer)
11817 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11818 if (flModifications & IsModified_USB)
11819 that->i_onUSBControllerChange();
11820
11821 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11822 if (networkAdapters[slot])
11823 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11824 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11825 if (serialPorts[slot])
11826 that->i_onSerialPortChange(serialPorts[slot]);
11827 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11828 if (parallelPorts[slot])
11829 that->i_onParallelPortChange(parallelPorts[slot]);
11830
11831 if (flModifications & IsModified_Storage)
11832 {
11833 for (StorageControllerList::const_iterator
11834 it = mStorageControllers->begin();
11835 it != mStorageControllers->end();
11836 ++it)
11837 {
11838 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11839 }
11840 }
11841
11842
11843#if 0
11844 if (flModifications & IsModified_BandwidthControl)
11845 that->onBandwidthControlChange();
11846#endif
11847 }
11848}
11849
11850/**
11851 * Commits all the changes to machine settings.
11852 *
11853 * Note that this operation is supposed to never fail.
11854 *
11855 * @note Locks this object and children for writing.
11856 */
11857void Machine::i_commit()
11858{
11859 AutoCaller autoCaller(this);
11860 AssertComRCReturnVoid(autoCaller.rc());
11861
11862 AutoCaller peerCaller(mPeer);
11863 AssertComRCReturnVoid(peerCaller.rc());
11864
11865 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11866
11867 /*
11868 * use safe commit to ensure Snapshot machines (that share mUserData)
11869 * will still refer to a valid memory location
11870 */
11871 mUserData.commitCopy();
11872
11873 mHWData.commit();
11874
11875 if (mMediumAttachments.isBackedUp())
11876 i_commitMedia(Global::IsOnline(mData->mMachineState));
11877
11878 mBIOSSettings->i_commit();
11879 mTrustedPlatformModule->i_commit();
11880 mNvramStore->i_commit();
11881 mRecordingSettings->i_commit();
11882 mGraphicsAdapter->i_commit();
11883 mVRDEServer->i_commit();
11884 mAudioAdapter->i_commit();
11885 mUSBDeviceFilters->i_commit();
11886 mBandwidthControl->i_commit();
11887
11888 /* Since mNetworkAdapters is a list which might have been changed (resized)
11889 * without using the Backupable<> template we need to handle the copying
11890 * of the list entries manually, including the creation of peers for the
11891 * new objects. */
11892 bool commitNetworkAdapters = false;
11893 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11894 if (mPeer)
11895 {
11896 /* commit everything, even the ones which will go away */
11897 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11898 mNetworkAdapters[slot]->i_commit();
11899 /* copy over the new entries, creating a peer and uninit the original */
11900 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11901 for (size_t slot = 0; slot < newSize; slot++)
11902 {
11903 /* look if this adapter has a peer device */
11904 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11905 if (!peer)
11906 {
11907 /* no peer means the adapter is a newly created one;
11908 * create a peer owning data this data share it with */
11909 peer.createObject();
11910 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11911 }
11912 mPeer->mNetworkAdapters[slot] = peer;
11913 }
11914 /* uninit any no longer needed network adapters */
11915 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11916 mNetworkAdapters[slot]->uninit();
11917 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11918 {
11919 if (mPeer->mNetworkAdapters[slot])
11920 mPeer->mNetworkAdapters[slot]->uninit();
11921 }
11922 /* Keep the original network adapter count until this point, so that
11923 * discarding a chipset type change will not lose settings. */
11924 mNetworkAdapters.resize(newSize);
11925 mPeer->mNetworkAdapters.resize(newSize);
11926 }
11927 else
11928 {
11929 /* we have no peer (our parent is the newly created machine);
11930 * just commit changes to the network adapters */
11931 commitNetworkAdapters = true;
11932 }
11933 if (commitNetworkAdapters)
11934 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11935 mNetworkAdapters[slot]->i_commit();
11936
11937 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11938 mSerialPorts[slot]->i_commit();
11939 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11940 mParallelPorts[slot]->i_commit();
11941
11942 bool commitStorageControllers = false;
11943
11944 if (mStorageControllers.isBackedUp())
11945 {
11946 mStorageControllers.commit();
11947
11948 if (mPeer)
11949 {
11950 /* Commit all changes to new controllers (this will reshare data with
11951 * peers for those who have peers) */
11952 StorageControllerList *newList = new StorageControllerList();
11953 for (StorageControllerList::const_iterator
11954 it = mStorageControllers->begin();
11955 it != mStorageControllers->end();
11956 ++it)
11957 {
11958 (*it)->i_commit();
11959
11960 /* look if this controller has a peer device */
11961 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11962 if (!peer)
11963 {
11964 /* no peer means the device is a newly created one;
11965 * create a peer owning data this device share it with */
11966 peer.createObject();
11967 peer->init(mPeer, *it, true /* aReshare */);
11968 }
11969 else
11970 {
11971 /* remove peer from the old list */
11972 mPeer->mStorageControllers->remove(peer);
11973 }
11974 /* and add it to the new list */
11975 newList->push_back(peer);
11976 }
11977
11978 /* uninit old peer's controllers that are left */
11979 for (StorageControllerList::const_iterator
11980 it = mPeer->mStorageControllers->begin();
11981 it != mPeer->mStorageControllers->end();
11982 ++it)
11983 {
11984 (*it)->uninit();
11985 }
11986
11987 /* attach new list of controllers to our peer */
11988 mPeer->mStorageControllers.attach(newList);
11989 }
11990 else
11991 {
11992 /* we have no peer (our parent is the newly created machine);
11993 * just commit changes to devices */
11994 commitStorageControllers = true;
11995 }
11996 }
11997 else
11998 {
11999 /* the list of controllers itself is not changed,
12000 * just commit changes to controllers themselves */
12001 commitStorageControllers = true;
12002 }
12003
12004 if (commitStorageControllers)
12005 {
12006 for (StorageControllerList::const_iterator
12007 it = mStorageControllers->begin();
12008 it != mStorageControllers->end();
12009 ++it)
12010 {
12011 (*it)->i_commit();
12012 }
12013 }
12014
12015 bool commitUSBControllers = false;
12016
12017 if (mUSBControllers.isBackedUp())
12018 {
12019 mUSBControllers.commit();
12020
12021 if (mPeer)
12022 {
12023 /* Commit all changes to new controllers (this will reshare data with
12024 * peers for those who have peers) */
12025 USBControllerList *newList = new USBControllerList();
12026 for (USBControllerList::const_iterator
12027 it = mUSBControllers->begin();
12028 it != mUSBControllers->end();
12029 ++it)
12030 {
12031 (*it)->i_commit();
12032
12033 /* look if this controller has a peer device */
12034 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12035 if (!peer)
12036 {
12037 /* no peer means the device is a newly created one;
12038 * create a peer owning data this device share it with */
12039 peer.createObject();
12040 peer->init(mPeer, *it, true /* aReshare */);
12041 }
12042 else
12043 {
12044 /* remove peer from the old list */
12045 mPeer->mUSBControllers->remove(peer);
12046 }
12047 /* and add it to the new list */
12048 newList->push_back(peer);
12049 }
12050
12051 /* uninit old peer's controllers that are left */
12052 for (USBControllerList::const_iterator
12053 it = mPeer->mUSBControllers->begin();
12054 it != mPeer->mUSBControllers->end();
12055 ++it)
12056 {
12057 (*it)->uninit();
12058 }
12059
12060 /* attach new list of controllers to our peer */
12061 mPeer->mUSBControllers.attach(newList);
12062 }
12063 else
12064 {
12065 /* we have no peer (our parent is the newly created machine);
12066 * just commit changes to devices */
12067 commitUSBControllers = true;
12068 }
12069 }
12070 else
12071 {
12072 /* the list of controllers itself is not changed,
12073 * just commit changes to controllers themselves */
12074 commitUSBControllers = true;
12075 }
12076
12077 if (commitUSBControllers)
12078 {
12079 for (USBControllerList::const_iterator
12080 it = mUSBControllers->begin();
12081 it != mUSBControllers->end();
12082 ++it)
12083 {
12084 (*it)->i_commit();
12085 }
12086 }
12087
12088 if (i_isSessionMachine())
12089 {
12090 /* attach new data to the primary machine and reshare it */
12091 mPeer->mUserData.attach(mUserData);
12092 mPeer->mHWData.attach(mHWData);
12093 /* mmMediumAttachments is reshared by fixupMedia */
12094 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12095 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12096 }
12097}
12098
12099/**
12100 * Copies all the hardware data from the given machine.
12101 *
12102 * Currently, only called when the VM is being restored from a snapshot. In
12103 * particular, this implies that the VM is not running during this method's
12104 * call.
12105 *
12106 * @note This method must be called from under this object's lock.
12107 *
12108 * @note This method doesn't call #i_commit(), so all data remains backed up and
12109 * unsaved.
12110 */
12111void Machine::i_copyFrom(Machine *aThat)
12112{
12113 AssertReturnVoid(!i_isSnapshotMachine());
12114 AssertReturnVoid(aThat->i_isSnapshotMachine());
12115
12116 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12117
12118 mHWData.assignCopy(aThat->mHWData);
12119
12120 // create copies of all shared folders (mHWData after attaching a copy
12121 // contains just references to original objects)
12122 for (HWData::SharedFolderList::iterator
12123 it = mHWData->mSharedFolders.begin();
12124 it != mHWData->mSharedFolders.end();
12125 ++it)
12126 {
12127 ComObjPtr<SharedFolder> folder;
12128 folder.createObject();
12129 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12130 AssertComRC(rc);
12131 *it = folder;
12132 }
12133
12134 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12135 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12136 mNvramStore->i_copyFrom(aThat->mNvramStore);
12137 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12138 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12139 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12140 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12141 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12142 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12143
12144 /* create private copies of all controllers */
12145 mStorageControllers.backup();
12146 mStorageControllers->clear();
12147 for (StorageControllerList::const_iterator
12148 it = aThat->mStorageControllers->begin();
12149 it != aThat->mStorageControllers->end();
12150 ++it)
12151 {
12152 ComObjPtr<StorageController> ctrl;
12153 ctrl.createObject();
12154 ctrl->initCopy(this, *it);
12155 mStorageControllers->push_back(ctrl);
12156 }
12157
12158 /* create private copies of all USB controllers */
12159 mUSBControllers.backup();
12160 mUSBControllers->clear();
12161 for (USBControllerList::const_iterator
12162 it = aThat->mUSBControllers->begin();
12163 it != aThat->mUSBControllers->end();
12164 ++it)
12165 {
12166 ComObjPtr<USBController> ctrl;
12167 ctrl.createObject();
12168 ctrl->initCopy(this, *it);
12169 mUSBControllers->push_back(ctrl);
12170 }
12171
12172 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12173 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12174 {
12175 if (mNetworkAdapters[slot].isNotNull())
12176 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12177 else
12178 {
12179 unconst(mNetworkAdapters[slot]).createObject();
12180 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12181 }
12182 }
12183 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12184 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12185 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12186 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12187}
12188
12189/**
12190 * Returns whether the given storage controller is hotplug capable.
12191 *
12192 * @returns true if the controller supports hotplugging
12193 * false otherwise.
12194 * @param enmCtrlType The controller type to check for.
12195 */
12196bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12197{
12198 ComPtr<ISystemProperties> systemProperties;
12199 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12200 if (FAILED(rc))
12201 return false;
12202
12203 BOOL aHotplugCapable = FALSE;
12204 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12205
12206 return RT_BOOL(aHotplugCapable);
12207}
12208
12209#ifdef VBOX_WITH_RESOURCE_USAGE_API
12210
12211void Machine::i_getDiskList(MediaList &list)
12212{
12213 for (MediumAttachmentList::const_iterator
12214 it = mMediumAttachments->begin();
12215 it != mMediumAttachments->end();
12216 ++it)
12217 {
12218 MediumAttachment *pAttach = *it;
12219 /* just in case */
12220 AssertContinue(pAttach);
12221
12222 AutoCaller localAutoCallerA(pAttach);
12223 if (FAILED(localAutoCallerA.rc())) continue;
12224
12225 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12226
12227 if (pAttach->i_getType() == DeviceType_HardDisk)
12228 list.push_back(pAttach->i_getMedium());
12229 }
12230}
12231
12232void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12233{
12234 AssertReturnVoid(isWriteLockOnCurrentThread());
12235 AssertPtrReturnVoid(aCollector);
12236
12237 pm::CollectorHAL *hal = aCollector->getHAL();
12238 /* Create sub metrics */
12239 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12240 "Percentage of processor time spent in user mode by the VM process.");
12241 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12242 "Percentage of processor time spent in kernel mode by the VM process.");
12243 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12244 "Size of resident portion of VM process in memory.");
12245 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12246 "Actual size of all VM disks combined.");
12247 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12248 "Network receive rate.");
12249 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12250 "Network transmit rate.");
12251 /* Create and register base metrics */
12252 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12253 cpuLoadUser, cpuLoadKernel);
12254 aCollector->registerBaseMetric(cpuLoad);
12255 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12256 ramUsageUsed);
12257 aCollector->registerBaseMetric(ramUsage);
12258 MediaList disks;
12259 i_getDiskList(disks);
12260 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12261 diskUsageUsed);
12262 aCollector->registerBaseMetric(diskUsage);
12263
12264 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12265 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12266 new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12268 new pm::AggregateMin()));
12269 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12270 new pm::AggregateMax()));
12271 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12272 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12273 new pm::AggregateAvg()));
12274 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12275 new pm::AggregateMin()));
12276 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12277 new pm::AggregateMax()));
12278
12279 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12280 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12281 new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12283 new pm::AggregateMin()));
12284 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12285 new pm::AggregateMax()));
12286
12287 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12288 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12289 new pm::AggregateAvg()));
12290 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12291 new pm::AggregateMin()));
12292 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12293 new pm::AggregateMax()));
12294
12295
12296 /* Guest metrics collector */
12297 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12298 aCollector->registerGuest(mCollectorGuest);
12299 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12300
12301 /* Create sub metrics */
12302 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12303 "Percentage of processor time spent in user mode as seen by the guest.");
12304 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12305 "Percentage of processor time spent in kernel mode as seen by the guest.");
12306 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12307 "Percentage of processor time spent idling as seen by the guest.");
12308
12309 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12310 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12311 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12312 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12313 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12314 pm::SubMetric *guestMemCache = new pm::SubMetric(
12315 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12316
12317 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12318 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12319
12320 /* Create and register base metrics */
12321 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12322 machineNetRx, machineNetTx);
12323 aCollector->registerBaseMetric(machineNetRate);
12324
12325 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12326 guestLoadUser, guestLoadKernel, guestLoadIdle);
12327 aCollector->registerBaseMetric(guestCpuLoad);
12328
12329 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12330 guestMemTotal, guestMemFree,
12331 guestMemBalloon, guestMemShared,
12332 guestMemCache, guestPagedTotal);
12333 aCollector->registerBaseMetric(guestCpuMem);
12334
12335 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12336 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12341 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12346 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12349
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12351 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12354
12355 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12356 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12357 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12358 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12359
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12364
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12369
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12374
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12379
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12384
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12389}
12390
12391void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12392{
12393 AssertReturnVoid(isWriteLockOnCurrentThread());
12394
12395 if (aCollector)
12396 {
12397 aCollector->unregisterMetricsFor(aMachine);
12398 aCollector->unregisterBaseMetricsFor(aMachine);
12399 }
12400}
12401
12402#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12403
12404
12405////////////////////////////////////////////////////////////////////////////////
12406
12407DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12408
12409HRESULT SessionMachine::FinalConstruct()
12410{
12411 LogFlowThisFunc(("\n"));
12412
12413 mClientToken = NULL;
12414
12415 return BaseFinalConstruct();
12416}
12417
12418void SessionMachine::FinalRelease()
12419{
12420 LogFlowThisFunc(("\n"));
12421
12422 Assert(!mClientToken);
12423 /* paranoia, should not hang around any more */
12424 if (mClientToken)
12425 {
12426 delete mClientToken;
12427 mClientToken = NULL;
12428 }
12429
12430 uninit(Uninit::Unexpected);
12431
12432 BaseFinalRelease();
12433}
12434
12435/**
12436 * @note Must be called only by Machine::LockMachine() from its own write lock.
12437 */
12438HRESULT SessionMachine::init(Machine *aMachine)
12439{
12440 LogFlowThisFuncEnter();
12441 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12442
12443 AssertReturn(aMachine, E_INVALIDARG);
12444
12445 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12446
12447 /* Enclose the state transition NotReady->InInit->Ready */
12448 AutoInitSpan autoInitSpan(this);
12449 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12450
12451 HRESULT rc = S_OK;
12452
12453 RT_ZERO(mAuthLibCtx);
12454
12455 /* create the machine client token */
12456 try
12457 {
12458 mClientToken = new ClientToken(aMachine, this);
12459 if (!mClientToken->isReady())
12460 {
12461 delete mClientToken;
12462 mClientToken = NULL;
12463 rc = E_FAIL;
12464 }
12465 }
12466 catch (std::bad_alloc &)
12467 {
12468 rc = E_OUTOFMEMORY;
12469 }
12470 if (FAILED(rc))
12471 return rc;
12472
12473 /* memorize the peer Machine */
12474 unconst(mPeer) = aMachine;
12475 /* share the parent pointer */
12476 unconst(mParent) = aMachine->mParent;
12477
12478 /* take the pointers to data to share */
12479 mData.share(aMachine->mData);
12480 mSSData.share(aMachine->mSSData);
12481
12482 mUserData.share(aMachine->mUserData);
12483 mHWData.share(aMachine->mHWData);
12484 mMediumAttachments.share(aMachine->mMediumAttachments);
12485
12486 mStorageControllers.allocate();
12487 for (StorageControllerList::const_iterator
12488 it = aMachine->mStorageControllers->begin();
12489 it != aMachine->mStorageControllers->end();
12490 ++it)
12491 {
12492 ComObjPtr<StorageController> ctl;
12493 ctl.createObject();
12494 ctl->init(this, *it);
12495 mStorageControllers->push_back(ctl);
12496 }
12497
12498 mUSBControllers.allocate();
12499 for (USBControllerList::const_iterator
12500 it = aMachine->mUSBControllers->begin();
12501 it != aMachine->mUSBControllers->end();
12502 ++it)
12503 {
12504 ComObjPtr<USBController> ctl;
12505 ctl.createObject();
12506 ctl->init(this, *it);
12507 mUSBControllers->push_back(ctl);
12508 }
12509
12510 unconst(mBIOSSettings).createObject();
12511 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12512
12513 unconst(mTrustedPlatformModule).createObject();
12514 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12515
12516 unconst(mNvramStore).createObject();
12517 mNvramStore->init(this, aMachine->mNvramStore);
12518
12519 unconst(mRecordingSettings).createObject();
12520 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12521 /* create another GraphicsAdapter object that will be mutable */
12522 unconst(mGraphicsAdapter).createObject();
12523 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12524 /* create another VRDEServer object that will be mutable */
12525 unconst(mVRDEServer).createObject();
12526 mVRDEServer->init(this, aMachine->mVRDEServer);
12527 /* create another audio adapter object that will be mutable */
12528 unconst(mAudioAdapter).createObject();
12529 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12530 /* create a list of serial ports that will be mutable */
12531 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12532 {
12533 unconst(mSerialPorts[slot]).createObject();
12534 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12535 }
12536 /* create a list of parallel ports that will be mutable */
12537 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12538 {
12539 unconst(mParallelPorts[slot]).createObject();
12540 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12541 }
12542
12543 /* create another USB device filters object that will be mutable */
12544 unconst(mUSBDeviceFilters).createObject();
12545 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12546
12547 /* create a list of network adapters that will be mutable */
12548 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12549 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12550 {
12551 unconst(mNetworkAdapters[slot]).createObject();
12552 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12553 }
12554
12555 /* create another bandwidth control object that will be mutable */
12556 unconst(mBandwidthControl).createObject();
12557 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12558
12559 /* default is to delete saved state on Saved -> PoweredOff transition */
12560 mRemoveSavedState = true;
12561
12562 /* Confirm a successful initialization when it's the case */
12563 autoInitSpan.setSucceeded();
12564
12565 miNATNetworksStarted = 0;
12566
12567 LogFlowThisFuncLeave();
12568 return rc;
12569}
12570
12571/**
12572 * Uninitializes this session object. If the reason is other than
12573 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12574 * or the client watcher code.
12575 *
12576 * @param aReason uninitialization reason
12577 *
12578 * @note Locks mParent + this object for writing.
12579 */
12580void SessionMachine::uninit(Uninit::Reason aReason)
12581{
12582 LogFlowThisFuncEnter();
12583 LogFlowThisFunc(("reason=%d\n", aReason));
12584
12585 /*
12586 * Strongly reference ourselves to prevent this object deletion after
12587 * mData->mSession.mMachine.setNull() below (which can release the last
12588 * reference and call the destructor). Important: this must be done before
12589 * accessing any members (and before AutoUninitSpan that does it as well).
12590 * This self reference will be released as the very last step on return.
12591 */
12592 ComObjPtr<SessionMachine> selfRef;
12593 if (aReason != Uninit::Unexpected)
12594 selfRef = this;
12595
12596 /* Enclose the state transition Ready->InUninit->NotReady */
12597 AutoUninitSpan autoUninitSpan(this);
12598 if (autoUninitSpan.uninitDone())
12599 {
12600 LogFlowThisFunc(("Already uninitialized\n"));
12601 LogFlowThisFuncLeave();
12602 return;
12603 }
12604
12605 if (autoUninitSpan.initFailed())
12606 {
12607 /* We've been called by init() because it's failed. It's not really
12608 * necessary (nor it's safe) to perform the regular uninit sequence
12609 * below, the following is enough.
12610 */
12611 LogFlowThisFunc(("Initialization failed.\n"));
12612 /* destroy the machine client token */
12613 if (mClientToken)
12614 {
12615 delete mClientToken;
12616 mClientToken = NULL;
12617 }
12618 uninitDataAndChildObjects();
12619 mData.free();
12620 unconst(mParent) = NULL;
12621 unconst(mPeer) = NULL;
12622 LogFlowThisFuncLeave();
12623 return;
12624 }
12625
12626 MachineState_T lastState;
12627 {
12628 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12629 lastState = mData->mMachineState;
12630 }
12631 NOREF(lastState);
12632
12633#ifdef VBOX_WITH_USB
12634 // release all captured USB devices, but do this before requesting the locks below
12635 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12636 {
12637 /* Console::captureUSBDevices() is called in the VM process only after
12638 * setting the machine state to Starting or Restoring.
12639 * Console::detachAllUSBDevices() will be called upon successful
12640 * termination. So, we need to release USB devices only if there was
12641 * an abnormal termination of a running VM.
12642 *
12643 * This is identical to SessionMachine::DetachAllUSBDevices except
12644 * for the aAbnormal argument. */
12645 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12646 AssertComRC(rc);
12647 NOREF(rc);
12648
12649 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12650 if (service)
12651 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12652 }
12653#endif /* VBOX_WITH_USB */
12654
12655 // we need to lock this object in uninit() because the lock is shared
12656 // with mPeer (as well as data we modify below). mParent lock is needed
12657 // by several calls to it.
12658 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12659
12660#ifdef VBOX_WITH_RESOURCE_USAGE_API
12661 /*
12662 * It is safe to call Machine::i_unregisterMetrics() here because
12663 * PerformanceCollector::samplerCallback no longer accesses guest methods
12664 * holding the lock.
12665 */
12666 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12667 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12668 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12669 if (mCollectorGuest)
12670 {
12671 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12672 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12673 mCollectorGuest = NULL;
12674 }
12675#endif
12676
12677 if (aReason == Uninit::Abnormal)
12678 {
12679 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12680
12681 /*
12682 * Move the VM to the 'Aborted' machine state unless we are restoring a
12683 * VM that was in the 'Saved' machine state. In that case, if the VM
12684 * fails before reaching either the 'Restoring' machine state or the
12685 * 'Running' machine state then we set the machine state to
12686 * 'AbortedSaved' in order to preserve the saved state file so that the
12687 * VM can be restored in the future.
12688 */
12689 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12690 i_setMachineState(MachineState_AbortedSaved);
12691 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12692 i_setMachineState(MachineState_Aborted);
12693 }
12694
12695 // any machine settings modified?
12696 if (mData->flModifications)
12697 {
12698 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12699 i_rollback(false /* aNotify */);
12700 }
12701
12702 mData->mSession.mPID = NIL_RTPROCESS;
12703
12704 if (aReason == Uninit::Unexpected)
12705 {
12706 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12707 * client watcher thread to update the set of machines that have open
12708 * sessions. */
12709 mParent->i_updateClientWatcher();
12710 }
12711
12712 /* uninitialize all remote controls */
12713 if (mData->mSession.mRemoteControls.size())
12714 {
12715 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12716 mData->mSession.mRemoteControls.size()));
12717
12718 /* Always restart a the beginning, since the iterator is invalidated
12719 * by using erase(). */
12720 for (Data::Session::RemoteControlList::iterator
12721 it = mData->mSession.mRemoteControls.begin();
12722 it != mData->mSession.mRemoteControls.end();
12723 it = mData->mSession.mRemoteControls.begin())
12724 {
12725 ComPtr<IInternalSessionControl> pControl = *it;
12726 mData->mSession.mRemoteControls.erase(it);
12727 multilock.release();
12728 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12729 HRESULT rc = pControl->Uninitialize();
12730 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12731 if (FAILED(rc))
12732 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12733 multilock.acquire();
12734 }
12735 mData->mSession.mRemoteControls.clear();
12736 }
12737
12738 /* Remove all references to the NAT network service. The service will stop
12739 * if all references (also from other VMs) are removed. */
12740 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12741 {
12742 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12743 {
12744 BOOL enabled;
12745 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12746 if ( FAILED(hrc)
12747 || !enabled)
12748 continue;
12749
12750 NetworkAttachmentType_T type;
12751 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12752 if ( SUCCEEDED(hrc)
12753 && type == NetworkAttachmentType_NATNetwork)
12754 {
12755 Bstr name;
12756 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12757 if (SUCCEEDED(hrc))
12758 {
12759 multilock.release();
12760 Utf8Str strName(name);
12761 LogRel(("VM '%s' stops using NAT network '%s'\n",
12762 mUserData->s.strName.c_str(), strName.c_str()));
12763 mParent->i_natNetworkRefDec(strName);
12764 multilock.acquire();
12765 }
12766 }
12767 }
12768 }
12769
12770 /*
12771 * An expected uninitialization can come only from #i_checkForDeath().
12772 * Otherwise it means that something's gone really wrong (for example,
12773 * the Session implementation has released the VirtualBox reference
12774 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12775 * etc). However, it's also possible, that the client releases the IPC
12776 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12777 * but the VirtualBox release event comes first to the server process.
12778 * This case is practically possible, so we should not assert on an
12779 * unexpected uninit, just log a warning.
12780 */
12781
12782 if (aReason == Uninit::Unexpected)
12783 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12784
12785 if (aReason != Uninit::Normal)
12786 {
12787 mData->mSession.mDirectControl.setNull();
12788 }
12789 else
12790 {
12791 /* this must be null here (see #OnSessionEnd()) */
12792 Assert(mData->mSession.mDirectControl.isNull());
12793 Assert(mData->mSession.mState == SessionState_Unlocking);
12794 Assert(!mData->mSession.mProgress.isNull());
12795 }
12796 if (mData->mSession.mProgress)
12797 {
12798 if (aReason == Uninit::Normal)
12799 mData->mSession.mProgress->i_notifyComplete(S_OK);
12800 else
12801 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12802 COM_IIDOF(ISession),
12803 getComponentName(),
12804 tr("The VM session was aborted"));
12805 mData->mSession.mProgress.setNull();
12806 }
12807
12808 if (mConsoleTaskData.mProgress)
12809 {
12810 Assert(aReason == Uninit::Abnormal);
12811 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12812 COM_IIDOF(ISession),
12813 getComponentName(),
12814 tr("The VM session was aborted"));
12815 mConsoleTaskData.mProgress.setNull();
12816 }
12817
12818 /* remove the association between the peer machine and this session machine */
12819 Assert( (SessionMachine*)mData->mSession.mMachine == this
12820 || aReason == Uninit::Unexpected);
12821
12822 /* reset the rest of session data */
12823 mData->mSession.mLockType = LockType_Null;
12824 mData->mSession.mMachine.setNull();
12825 mData->mSession.mState = SessionState_Unlocked;
12826 mData->mSession.mName.setNull();
12827
12828 /* destroy the machine client token before leaving the exclusive lock */
12829 if (mClientToken)
12830 {
12831 delete mClientToken;
12832 mClientToken = NULL;
12833 }
12834
12835 /* fire an event */
12836 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12837
12838 uninitDataAndChildObjects();
12839
12840 /* free the essential data structure last */
12841 mData.free();
12842
12843 /* release the exclusive lock before setting the below two to NULL */
12844 multilock.release();
12845
12846 unconst(mParent) = NULL;
12847 unconst(mPeer) = NULL;
12848
12849 AuthLibUnload(&mAuthLibCtx);
12850
12851 LogFlowThisFuncLeave();
12852}
12853
12854// util::Lockable interface
12855////////////////////////////////////////////////////////////////////////////////
12856
12857/**
12858 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12859 * with the primary Machine instance (mPeer).
12860 */
12861RWLockHandle *SessionMachine::lockHandle() const
12862{
12863 AssertReturn(mPeer != NULL, NULL);
12864 return mPeer->lockHandle();
12865}
12866
12867// IInternalMachineControl methods
12868////////////////////////////////////////////////////////////////////////////////
12869
12870/**
12871 * Passes collected guest statistics to performance collector object
12872 */
12873HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12874 ULONG aCpuKernel, ULONG aCpuIdle,
12875 ULONG aMemTotal, ULONG aMemFree,
12876 ULONG aMemBalloon, ULONG aMemShared,
12877 ULONG aMemCache, ULONG aPageTotal,
12878 ULONG aAllocVMM, ULONG aFreeVMM,
12879 ULONG aBalloonedVMM, ULONG aSharedVMM,
12880 ULONG aVmNetRx, ULONG aVmNetTx)
12881{
12882#ifdef VBOX_WITH_RESOURCE_USAGE_API
12883 if (mCollectorGuest)
12884 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12885 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12886 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12887 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12888
12889 return S_OK;
12890#else
12891 NOREF(aValidStats);
12892 NOREF(aCpuUser);
12893 NOREF(aCpuKernel);
12894 NOREF(aCpuIdle);
12895 NOREF(aMemTotal);
12896 NOREF(aMemFree);
12897 NOREF(aMemBalloon);
12898 NOREF(aMemShared);
12899 NOREF(aMemCache);
12900 NOREF(aPageTotal);
12901 NOREF(aAllocVMM);
12902 NOREF(aFreeVMM);
12903 NOREF(aBalloonedVMM);
12904 NOREF(aSharedVMM);
12905 NOREF(aVmNetRx);
12906 NOREF(aVmNetTx);
12907 return E_NOTIMPL;
12908#endif
12909}
12910
12911////////////////////////////////////////////////////////////////////////////////
12912//
12913// SessionMachine task records
12914//
12915////////////////////////////////////////////////////////////////////////////////
12916
12917/**
12918 * Task record for saving the machine state.
12919 */
12920class SessionMachine::SaveStateTask
12921 : public Machine::Task
12922{
12923public:
12924 SaveStateTask(SessionMachine *m,
12925 Progress *p,
12926 const Utf8Str &t,
12927 Reason_T enmReason,
12928 const Utf8Str &strStateFilePath)
12929 : Task(m, p, t),
12930 m_enmReason(enmReason),
12931 m_strStateFilePath(strStateFilePath)
12932 {}
12933
12934private:
12935 void handler()
12936 {
12937 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12938 }
12939
12940 Reason_T m_enmReason;
12941 Utf8Str m_strStateFilePath;
12942
12943 friend class SessionMachine;
12944};
12945
12946/**
12947 * Task thread implementation for SessionMachine::SaveState(), called from
12948 * SessionMachine::taskHandler().
12949 *
12950 * @note Locks this object for writing.
12951 *
12952 * @param task
12953 * @return
12954 */
12955void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12956{
12957 LogFlowThisFuncEnter();
12958
12959 AutoCaller autoCaller(this);
12960 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12961 if (FAILED(autoCaller.rc()))
12962 {
12963 /* we might have been uninitialized because the session was accidentally
12964 * closed by the client, so don't assert */
12965 HRESULT rc = setError(E_FAIL,
12966 tr("The session has been accidentally closed"));
12967 task.m_pProgress->i_notifyComplete(rc);
12968 LogFlowThisFuncLeave();
12969 return;
12970 }
12971
12972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12973
12974 HRESULT rc = S_OK;
12975
12976 try
12977 {
12978 ComPtr<IInternalSessionControl> directControl;
12979 if (mData->mSession.mLockType == LockType_VM)
12980 directControl = mData->mSession.mDirectControl;
12981 if (directControl.isNull())
12982 throw setError(VBOX_E_INVALID_VM_STATE,
12983 tr("Trying to save state without a running VM"));
12984 alock.release();
12985 BOOL fSuspendedBySave;
12986 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12987 Assert(!fSuspendedBySave);
12988 alock.acquire();
12989
12990 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12991 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12992 throw E_FAIL);
12993
12994 if (SUCCEEDED(rc))
12995 {
12996 mSSData->strStateFilePath = task.m_strStateFilePath;
12997
12998 /* save all VM settings */
12999 rc = i_saveSettings(NULL);
13000 // no need to check whether VirtualBox.xml needs saving also since
13001 // we can't have a name change pending at this point
13002 }
13003 else
13004 {
13005 // On failure, set the state to the state we had at the beginning.
13006 i_setMachineState(task.m_machineStateBackup);
13007 i_updateMachineStateOnClient();
13008
13009 // Delete the saved state file (might have been already created).
13010 // No need to check whether this is shared with a snapshot here
13011 // because we certainly created a fresh saved state file here.
13012 RTFileDelete(task.m_strStateFilePath.c_str());
13013 }
13014 }
13015 catch (HRESULT aRC) { rc = aRC; }
13016
13017 task.m_pProgress->i_notifyComplete(rc);
13018
13019 LogFlowThisFuncLeave();
13020}
13021
13022/**
13023 * @note Locks this object for writing.
13024 */
13025HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13026{
13027 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13028}
13029
13030HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13031{
13032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13033
13034 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13035 if (FAILED(rc)) return rc;
13036
13037 if ( mData->mMachineState != MachineState_Running
13038 && mData->mMachineState != MachineState_Paused
13039 )
13040 return setError(VBOX_E_INVALID_VM_STATE,
13041 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13042 Global::stringifyMachineState(mData->mMachineState));
13043
13044 ComObjPtr<Progress> pProgress;
13045 pProgress.createObject();
13046 rc = pProgress->init(i_getVirtualBox(),
13047 static_cast<IMachine *>(this) /* aInitiator */,
13048 tr("Saving the execution state of the virtual machine"),
13049 FALSE /* aCancelable */);
13050 if (FAILED(rc))
13051 return rc;
13052
13053 Utf8Str strStateFilePath;
13054 i_composeSavedStateFilename(strStateFilePath);
13055
13056 /* create and start the task on a separate thread (note that it will not
13057 * start working until we release alock) */
13058 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13059 rc = pTask->createThread();
13060 if (FAILED(rc))
13061 return rc;
13062
13063 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13064 i_setMachineState(MachineState_Saving);
13065 i_updateMachineStateOnClient();
13066
13067 pProgress.queryInterfaceTo(aProgress.asOutParam());
13068
13069 return S_OK;
13070}
13071
13072/**
13073 * @note Locks this object for writing.
13074 */
13075HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13076{
13077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13078
13079 HRESULT rc = i_checkStateDependency(MutableStateDep);
13080 if (FAILED(rc)) return rc;
13081
13082 if ( mData->mMachineState != MachineState_PoweredOff
13083 && mData->mMachineState != MachineState_Teleported
13084 && mData->mMachineState != MachineState_Aborted
13085 )
13086 return setError(VBOX_E_INVALID_VM_STATE,
13087 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13088 Global::stringifyMachineState(mData->mMachineState));
13089
13090 com::Utf8Str stateFilePathFull;
13091 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13092 if (RT_FAILURE(vrc))
13093 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13094 tr("Invalid saved state file path '%s' (%Rrc)"),
13095 aSavedStateFile.c_str(),
13096 vrc);
13097
13098 mSSData->strStateFilePath = stateFilePathFull;
13099
13100 /* The below i_setMachineState() will detect the state transition and will
13101 * update the settings file */
13102
13103 return i_setMachineState(MachineState_Saved);
13104}
13105
13106/**
13107 * @note Locks this object for writing.
13108 */
13109HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13110{
13111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13112
13113 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13114 if (FAILED(rc)) return rc;
13115
13116 if ( mData->mMachineState != MachineState_Saved
13117 && mData->mMachineState != MachineState_AbortedSaved)
13118 return setError(VBOX_E_INVALID_VM_STATE,
13119 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13120 Global::stringifyMachineState(mData->mMachineState));
13121
13122 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13123
13124 /*
13125 * Saved -> PoweredOff transition will be detected in the SessionMachine
13126 * and properly handled.
13127 */
13128 rc = i_setMachineState(MachineState_PoweredOff);
13129 return rc;
13130}
13131
13132
13133/**
13134 * @note Locks the same as #i_setMachineState() does.
13135 */
13136HRESULT SessionMachine::updateState(MachineState_T aState)
13137{
13138 return i_setMachineState(aState);
13139}
13140
13141/**
13142 * @note Locks this object for writing.
13143 */
13144HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13145{
13146 IProgress *pProgress(aProgress);
13147
13148 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13149
13150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13151
13152 if (mData->mSession.mState != SessionState_Locked)
13153 return VBOX_E_INVALID_OBJECT_STATE;
13154
13155 if (!mData->mSession.mProgress.isNull())
13156 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13157
13158 /* If we didn't reference the NAT network service yet, add a reference to
13159 * force a start */
13160 if (miNATNetworksStarted < 1)
13161 {
13162 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13163 {
13164 BOOL enabled;
13165 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13166 if ( FAILED(hrc)
13167 || !enabled)
13168 continue;
13169
13170 NetworkAttachmentType_T type;
13171 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13172 if ( SUCCEEDED(hrc)
13173 && type == NetworkAttachmentType_NATNetwork)
13174 {
13175 Bstr name;
13176 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13177 if (SUCCEEDED(hrc))
13178 {
13179 Utf8Str strName(name);
13180 LogRel(("VM '%s' starts using NAT network '%s'\n",
13181 mUserData->s.strName.c_str(), strName.c_str()));
13182 mPeer->lockHandle()->unlockWrite();
13183 mParent->i_natNetworkRefInc(strName);
13184#ifdef RT_LOCK_STRICT
13185 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13186#else
13187 mPeer->lockHandle()->lockWrite();
13188#endif
13189 }
13190 }
13191 }
13192 miNATNetworksStarted++;
13193 }
13194
13195 LogFlowThisFunc(("returns S_OK.\n"));
13196 return S_OK;
13197}
13198
13199/**
13200 * @note Locks this object for writing.
13201 */
13202HRESULT SessionMachine::endPowerUp(LONG aResult)
13203{
13204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13205
13206 if (mData->mSession.mState != SessionState_Locked)
13207 return VBOX_E_INVALID_OBJECT_STATE;
13208
13209 /* Finalize the LaunchVMProcess progress object. */
13210 if (mData->mSession.mProgress)
13211 {
13212 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13213 mData->mSession.mProgress.setNull();
13214 }
13215
13216 if (SUCCEEDED((HRESULT)aResult))
13217 {
13218#ifdef VBOX_WITH_RESOURCE_USAGE_API
13219 /* The VM has been powered up successfully, so it makes sense
13220 * now to offer the performance metrics for a running machine
13221 * object. Doing it earlier wouldn't be safe. */
13222 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13223 mData->mSession.mPID);
13224#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13225 }
13226
13227 return S_OK;
13228}
13229
13230/**
13231 * @note Locks this object for writing.
13232 */
13233HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13234{
13235 LogFlowThisFuncEnter();
13236
13237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13238
13239 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13240 E_FAIL);
13241
13242 /* create a progress object to track operation completion */
13243 ComObjPtr<Progress> pProgress;
13244 pProgress.createObject();
13245 pProgress->init(i_getVirtualBox(),
13246 static_cast<IMachine *>(this) /* aInitiator */,
13247 tr("Stopping the virtual machine"),
13248 FALSE /* aCancelable */);
13249
13250 /* fill in the console task data */
13251 mConsoleTaskData.mLastState = mData->mMachineState;
13252 mConsoleTaskData.mProgress = pProgress;
13253
13254 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13255 i_setMachineState(MachineState_Stopping);
13256
13257 pProgress.queryInterfaceTo(aProgress.asOutParam());
13258
13259 return S_OK;
13260}
13261
13262/**
13263 * @note Locks this object for writing.
13264 */
13265HRESULT SessionMachine::endPoweringDown(LONG aResult,
13266 const com::Utf8Str &aErrMsg)
13267{
13268 HRESULT const hrcResult = (HRESULT)aResult;
13269 LogFlowThisFuncEnter();
13270
13271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13272
13273 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13274 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13275 && mConsoleTaskData.mLastState != MachineState_Null,
13276 E_FAIL);
13277
13278 /*
13279 * On failure, set the state to the state we had when BeginPoweringDown()
13280 * was called (this is expected by Console::PowerDown() and the associated
13281 * task). On success the VM process already changed the state to
13282 * MachineState_PoweredOff, so no need to do anything.
13283 */
13284 if (FAILED(hrcResult))
13285 i_setMachineState(mConsoleTaskData.mLastState);
13286
13287 /* notify the progress object about operation completion */
13288 Assert(mConsoleTaskData.mProgress);
13289 if (SUCCEEDED(hrcResult))
13290 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13291 else
13292 {
13293 if (aErrMsg.length())
13294 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13295 COM_IIDOF(ISession),
13296 getComponentName(),
13297 aErrMsg.c_str());
13298 else
13299 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13300 }
13301
13302 /* clear out the temporary saved state data */
13303 mConsoleTaskData.mLastState = MachineState_Null;
13304 mConsoleTaskData.mProgress.setNull();
13305
13306 LogFlowThisFuncLeave();
13307 return S_OK;
13308}
13309
13310
13311/**
13312 * Goes through the USB filters of the given machine to see if the given
13313 * device matches any filter or not.
13314 *
13315 * @note Locks the same as USBController::hasMatchingFilter() does.
13316 */
13317HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13318 BOOL *aMatched,
13319 ULONG *aMaskedInterfaces)
13320{
13321 LogFlowThisFunc(("\n"));
13322
13323#ifdef VBOX_WITH_USB
13324 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13325#else
13326 NOREF(aDevice);
13327 NOREF(aMaskedInterfaces);
13328 *aMatched = FALSE;
13329#endif
13330
13331 return S_OK;
13332}
13333
13334/**
13335 * @note Locks the same as Host::captureUSBDevice() does.
13336 */
13337HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13338{
13339 LogFlowThisFunc(("\n"));
13340
13341#ifdef VBOX_WITH_USB
13342 /* if captureDeviceForVM() fails, it must have set extended error info */
13343 clearError();
13344 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13345 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13346 return rc;
13347
13348 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13349 AssertReturn(service, E_FAIL);
13350 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13351#else
13352 RT_NOREF(aId, aCaptureFilename);
13353 return E_NOTIMPL;
13354#endif
13355}
13356
13357/**
13358 * @note Locks the same as Host::detachUSBDevice() does.
13359 */
13360HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13361 BOOL aDone)
13362{
13363 LogFlowThisFunc(("\n"));
13364
13365#ifdef VBOX_WITH_USB
13366 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13367 AssertReturn(service, E_FAIL);
13368 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13369#else
13370 NOREF(aId);
13371 NOREF(aDone);
13372 return E_NOTIMPL;
13373#endif
13374}
13375
13376/**
13377 * Inserts all machine filters to the USB proxy service and then calls
13378 * Host::autoCaptureUSBDevices().
13379 *
13380 * Called by Console from the VM process upon VM startup.
13381 *
13382 * @note Locks what called methods lock.
13383 */
13384HRESULT SessionMachine::autoCaptureUSBDevices()
13385{
13386 LogFlowThisFunc(("\n"));
13387
13388#ifdef VBOX_WITH_USB
13389 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13390 AssertComRC(rc);
13391 NOREF(rc);
13392
13393 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13394 AssertReturn(service, E_FAIL);
13395 return service->autoCaptureDevicesForVM(this);
13396#else
13397 return S_OK;
13398#endif
13399}
13400
13401/**
13402 * Removes all machine filters from the USB proxy service and then calls
13403 * Host::detachAllUSBDevices().
13404 *
13405 * Called by Console from the VM process upon normal VM termination or by
13406 * SessionMachine::uninit() upon abnormal VM termination (from under the
13407 * Machine/SessionMachine lock).
13408 *
13409 * @note Locks what called methods lock.
13410 */
13411HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13412{
13413 LogFlowThisFunc(("\n"));
13414
13415#ifdef VBOX_WITH_USB
13416 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13417 AssertComRC(rc);
13418 NOREF(rc);
13419
13420 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13421 AssertReturn(service, E_FAIL);
13422 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13423#else
13424 NOREF(aDone);
13425 return S_OK;
13426#endif
13427}
13428
13429/**
13430 * @note Locks this object for writing.
13431 */
13432HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13433 ComPtr<IProgress> &aProgress)
13434{
13435 LogFlowThisFuncEnter();
13436
13437 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13438 /*
13439 * We don't assert below because it might happen that a non-direct session
13440 * informs us it is closed right after we've been uninitialized -- it's ok.
13441 */
13442
13443 /* get IInternalSessionControl interface */
13444 ComPtr<IInternalSessionControl> control(aSession);
13445
13446 ComAssertRet(!control.isNull(), E_INVALIDARG);
13447
13448 /* Creating a Progress object requires the VirtualBox lock, and
13449 * thus locking it here is required by the lock order rules. */
13450 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13451
13452 if (control == mData->mSession.mDirectControl)
13453 {
13454 /* The direct session is being normally closed by the client process
13455 * ----------------------------------------------------------------- */
13456
13457 /* go to the closing state (essential for all open*Session() calls and
13458 * for #i_checkForDeath()) */
13459 Assert(mData->mSession.mState == SessionState_Locked);
13460 mData->mSession.mState = SessionState_Unlocking;
13461
13462 /* set direct control to NULL to release the remote instance */
13463 mData->mSession.mDirectControl.setNull();
13464 LogFlowThisFunc(("Direct control is set to NULL\n"));
13465
13466 if (mData->mSession.mProgress)
13467 {
13468 /* finalize the progress, someone might wait if a frontend
13469 * closes the session before powering on the VM. */
13470 mData->mSession.mProgress->notifyComplete(E_FAIL,
13471 COM_IIDOF(ISession),
13472 getComponentName(),
13473 tr("The VM session was closed before any attempt to power it on"));
13474 mData->mSession.mProgress.setNull();
13475 }
13476
13477 /* Create the progress object the client will use to wait until
13478 * #i_checkForDeath() is called to uninitialize this session object after
13479 * it releases the IPC semaphore.
13480 * Note! Because we're "reusing" mProgress here, this must be a proxy
13481 * object just like for LaunchVMProcess. */
13482 Assert(mData->mSession.mProgress.isNull());
13483 ComObjPtr<ProgressProxy> progress;
13484 progress.createObject();
13485 ComPtr<IUnknown> pPeer(mPeer);
13486 progress->init(mParent, pPeer,
13487 Bstr(tr("Closing session")).raw(),
13488 FALSE /* aCancelable */);
13489 progress.queryInterfaceTo(aProgress.asOutParam());
13490 mData->mSession.mProgress = progress;
13491 }
13492 else
13493 {
13494 /* the remote session is being normally closed */
13495 bool found = false;
13496 for (Data::Session::RemoteControlList::iterator
13497 it = mData->mSession.mRemoteControls.begin();
13498 it != mData->mSession.mRemoteControls.end();
13499 ++it)
13500 {
13501 if (control == *it)
13502 {
13503 found = true;
13504 // This MUST be erase(it), not remove(*it) as the latter
13505 // triggers a very nasty use after free due to the place where
13506 // the value "lives".
13507 mData->mSession.mRemoteControls.erase(it);
13508 break;
13509 }
13510 }
13511 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13512 E_INVALIDARG);
13513 }
13514
13515 /* signal the client watcher thread, because the client is going away */
13516 mParent->i_updateClientWatcher();
13517
13518 LogFlowThisFuncLeave();
13519 return S_OK;
13520}
13521
13522HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13523 std::vector<com::Utf8Str> &aValues,
13524 std::vector<LONG64> &aTimestamps,
13525 std::vector<com::Utf8Str> &aFlags)
13526{
13527 LogFlowThisFunc(("\n"));
13528
13529#ifdef VBOX_WITH_GUEST_PROPS
13530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13531
13532 size_t cEntries = mHWData->mGuestProperties.size();
13533 aNames.resize(cEntries);
13534 aValues.resize(cEntries);
13535 aTimestamps.resize(cEntries);
13536 aFlags.resize(cEntries);
13537
13538 size_t i = 0;
13539 for (HWData::GuestPropertyMap::const_iterator
13540 it = mHWData->mGuestProperties.begin();
13541 it != mHWData->mGuestProperties.end();
13542 ++it, ++i)
13543 {
13544 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13545 aNames[i] = it->first;
13546 aValues[i] = it->second.strValue;
13547 aTimestamps[i] = it->second.mTimestamp;
13548
13549 /* If it is NULL, keep it NULL. */
13550 if (it->second.mFlags)
13551 {
13552 GuestPropWriteFlags(it->second.mFlags, szFlags);
13553 aFlags[i] = szFlags;
13554 }
13555 else
13556 aFlags[i] = "";
13557 }
13558 return S_OK;
13559#else
13560 ReturnComNotImplemented();
13561#endif
13562}
13563
13564HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13565 const com::Utf8Str &aValue,
13566 LONG64 aTimestamp,
13567 const com::Utf8Str &aFlags)
13568{
13569 LogFlowThisFunc(("\n"));
13570
13571#ifdef VBOX_WITH_GUEST_PROPS
13572 try
13573 {
13574 /*
13575 * Convert input up front.
13576 */
13577 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13578 if (aFlags.length())
13579 {
13580 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13581 AssertRCReturn(vrc, E_INVALIDARG);
13582 }
13583
13584 /*
13585 * Now grab the object lock, validate the state and do the update.
13586 */
13587
13588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13589
13590 if (!Global::IsOnline(mData->mMachineState))
13591 {
13592 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13593 VBOX_E_INVALID_VM_STATE);
13594 }
13595
13596 i_setModified(IsModified_MachineData);
13597 mHWData.backup();
13598
13599 bool fDelete = !aValue.length();
13600 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13601 if (it != mHWData->mGuestProperties.end())
13602 {
13603 if (!fDelete)
13604 {
13605 it->second.strValue = aValue;
13606 it->second.mTimestamp = aTimestamp;
13607 it->second.mFlags = fFlags;
13608 }
13609 else
13610 mHWData->mGuestProperties.erase(it);
13611
13612 mData->mGuestPropertiesModified = TRUE;
13613 }
13614 else if (!fDelete)
13615 {
13616 HWData::GuestProperty prop;
13617 prop.strValue = aValue;
13618 prop.mTimestamp = aTimestamp;
13619 prop.mFlags = fFlags;
13620
13621 mHWData->mGuestProperties[aName] = prop;
13622 mData->mGuestPropertiesModified = TRUE;
13623 }
13624
13625 alock.release();
13626
13627 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13628 }
13629 catch (...)
13630 {
13631 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13632 }
13633 return S_OK;
13634#else
13635 ReturnComNotImplemented();
13636#endif
13637}
13638
13639
13640HRESULT SessionMachine::lockMedia()
13641{
13642 AutoMultiWriteLock2 alock(this->lockHandle(),
13643 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13644
13645 AssertReturn( mData->mMachineState == MachineState_Starting
13646 || mData->mMachineState == MachineState_Restoring
13647 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13648
13649 clearError();
13650 alock.release();
13651 return i_lockMedia();
13652}
13653
13654HRESULT SessionMachine::unlockMedia()
13655{
13656 HRESULT hrc = i_unlockMedia();
13657 return hrc;
13658}
13659
13660HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13661 ComPtr<IMediumAttachment> &aNewAttachment)
13662{
13663 // request the host lock first, since might be calling Host methods for getting host drives;
13664 // next, protect the media tree all the while we're in here, as well as our member variables
13665 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13666 this->lockHandle(),
13667 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13668
13669 IMediumAttachment *iAttach = aAttachment;
13670 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13671
13672 Utf8Str ctrlName;
13673 LONG lPort;
13674 LONG lDevice;
13675 bool fTempEject;
13676 {
13677 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13678
13679 /* Need to query the details first, as the IMediumAttachment reference
13680 * might be to the original settings, which we are going to change. */
13681 ctrlName = pAttach->i_getControllerName();
13682 lPort = pAttach->i_getPort();
13683 lDevice = pAttach->i_getDevice();
13684 fTempEject = pAttach->i_getTempEject();
13685 }
13686
13687 if (!fTempEject)
13688 {
13689 /* Remember previously mounted medium. The medium before taking the
13690 * backup is not necessarily the same thing. */
13691 ComObjPtr<Medium> oldmedium;
13692 oldmedium = pAttach->i_getMedium();
13693
13694 i_setModified(IsModified_Storage);
13695 mMediumAttachments.backup();
13696
13697 // The backup operation makes the pAttach reference point to the
13698 // old settings. Re-get the correct reference.
13699 pAttach = i_findAttachment(*mMediumAttachments.data(),
13700 ctrlName,
13701 lPort,
13702 lDevice);
13703
13704 {
13705 AutoCaller autoAttachCaller(this);
13706 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13707
13708 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13709 if (!oldmedium.isNull())
13710 oldmedium->i_removeBackReference(mData->mUuid);
13711
13712 pAttach->i_updateMedium(NULL);
13713 pAttach->i_updateEjected();
13714 }
13715
13716 i_setModified(IsModified_Storage);
13717 }
13718 else
13719 {
13720 {
13721 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13722 pAttach->i_updateEjected();
13723 }
13724 }
13725
13726 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13727
13728 return S_OK;
13729}
13730
13731HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13732 com::Utf8Str &aResult)
13733{
13734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13735
13736 HRESULT hr = S_OK;
13737
13738 if (!mAuthLibCtx.hAuthLibrary)
13739 {
13740 /* Load the external authentication library. */
13741 Bstr authLibrary;
13742 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13743
13744 Utf8Str filename = authLibrary;
13745
13746 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13747 if (RT_FAILURE(vrc))
13748 hr = setErrorBoth(E_FAIL, vrc,
13749 tr("Could not load the external authentication library '%s' (%Rrc)"),
13750 filename.c_str(), vrc);
13751 }
13752
13753 /* The auth library might need the machine lock. */
13754 alock.release();
13755
13756 if (FAILED(hr))
13757 return hr;
13758
13759 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13760 {
13761 enum VRDEAuthParams
13762 {
13763 parmUuid = 1,
13764 parmGuestJudgement,
13765 parmUser,
13766 parmPassword,
13767 parmDomain,
13768 parmClientId
13769 };
13770
13771 AuthResult result = AuthResultAccessDenied;
13772
13773 Guid uuid(aAuthParams[parmUuid]);
13774 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13775 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13776
13777 result = AuthLibAuthenticate(&mAuthLibCtx,
13778 uuid.raw(), guestJudgement,
13779 aAuthParams[parmUser].c_str(),
13780 aAuthParams[parmPassword].c_str(),
13781 aAuthParams[parmDomain].c_str(),
13782 u32ClientId);
13783
13784 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13785 size_t cbPassword = aAuthParams[parmPassword].length();
13786 if (cbPassword)
13787 {
13788 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13789 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13790 }
13791
13792 if (result == AuthResultAccessGranted)
13793 aResult = "granted";
13794 else
13795 aResult = "denied";
13796
13797 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13798 aAuthParams[parmUser].c_str(), aResult.c_str()));
13799 }
13800 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13801 {
13802 enum VRDEAuthDisconnectParams
13803 {
13804 parmUuid = 1,
13805 parmClientId
13806 };
13807
13808 Guid uuid(aAuthParams[parmUuid]);
13809 uint32_t u32ClientId = 0;
13810 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13811 }
13812 else
13813 {
13814 hr = E_INVALIDARG;
13815 }
13816
13817 return hr;
13818}
13819
13820// public methods only for internal purposes
13821/////////////////////////////////////////////////////////////////////////////
13822
13823#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13824/**
13825 * Called from the client watcher thread to check for expected or unexpected
13826 * death of the client process that has a direct session to this machine.
13827 *
13828 * On Win32 and on OS/2, this method is called only when we've got the
13829 * mutex (i.e. the client has either died or terminated normally) so it always
13830 * returns @c true (the client is terminated, the session machine is
13831 * uninitialized).
13832 *
13833 * On other platforms, the method returns @c true if the client process has
13834 * terminated normally or abnormally and the session machine was uninitialized,
13835 * and @c false if the client process is still alive.
13836 *
13837 * @note Locks this object for writing.
13838 */
13839bool SessionMachine::i_checkForDeath()
13840{
13841 Uninit::Reason reason;
13842 bool terminated = false;
13843
13844 /* Enclose autoCaller with a block because calling uninit() from under it
13845 * will deadlock. */
13846 {
13847 AutoCaller autoCaller(this);
13848 if (!autoCaller.isOk())
13849 {
13850 /* return true if not ready, to cause the client watcher to exclude
13851 * the corresponding session from watching */
13852 LogFlowThisFunc(("Already uninitialized!\n"));
13853 return true;
13854 }
13855
13856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13857
13858 /* Determine the reason of death: if the session state is Closing here,
13859 * everything is fine. Otherwise it means that the client did not call
13860 * OnSessionEnd() before it released the IPC semaphore. This may happen
13861 * either because the client process has abnormally terminated, or
13862 * because it simply forgot to call ISession::Close() before exiting. We
13863 * threat the latter also as an abnormal termination (see
13864 * Session::uninit() for details). */
13865 reason = mData->mSession.mState == SessionState_Unlocking ?
13866 Uninit::Normal :
13867 Uninit::Abnormal;
13868
13869 if (mClientToken)
13870 terminated = mClientToken->release();
13871 } /* AutoCaller block */
13872
13873 if (terminated)
13874 uninit(reason);
13875
13876 return terminated;
13877}
13878
13879void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13880{
13881 LogFlowThisFunc(("\n"));
13882
13883 strTokenId.setNull();
13884
13885 AutoCaller autoCaller(this);
13886 AssertComRCReturnVoid(autoCaller.rc());
13887
13888 Assert(mClientToken);
13889 if (mClientToken)
13890 mClientToken->getId(strTokenId);
13891}
13892#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13893IToken *SessionMachine::i_getToken()
13894{
13895 LogFlowThisFunc(("\n"));
13896
13897 AutoCaller autoCaller(this);
13898 AssertComRCReturn(autoCaller.rc(), NULL);
13899
13900 Assert(mClientToken);
13901 if (mClientToken)
13902 return mClientToken->getToken();
13903 else
13904 return NULL;
13905}
13906#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13907
13908Machine::ClientToken *SessionMachine::i_getClientToken()
13909{
13910 LogFlowThisFunc(("\n"));
13911
13912 AutoCaller autoCaller(this);
13913 AssertComRCReturn(autoCaller.rc(), NULL);
13914
13915 return mClientToken;
13916}
13917
13918
13919/**
13920 * @note Locks this object for reading.
13921 */
13922HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926 AutoCaller autoCaller(this);
13927 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13928
13929 ComPtr<IInternalSessionControl> directControl;
13930 {
13931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13932 if (mData->mSession.mLockType == LockType_VM)
13933 directControl = mData->mSession.mDirectControl;
13934 }
13935
13936 /* ignore notifications sent after #OnSessionEnd() is called */
13937 if (!directControl)
13938 return S_OK;
13939
13940 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13941}
13942
13943/**
13944 * @note Locks this object for reading.
13945 */
13946HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13947 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13948 const Utf8Str &aGuestIp, LONG aGuestPort)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 AutoCaller autoCaller(this);
13953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13954
13955 ComPtr<IInternalSessionControl> directControl;
13956 {
13957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13958 if (mData->mSession.mLockType == LockType_VM)
13959 directControl = mData->mSession.mDirectControl;
13960 }
13961
13962 /* ignore notifications sent after #OnSessionEnd() is called */
13963 if (!directControl)
13964 return S_OK;
13965 /*
13966 * instead acting like callback we ask IVirtualBox deliver corresponding event
13967 */
13968
13969 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13970 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13971 return S_OK;
13972}
13973
13974/**
13975 * @note Locks this object for reading.
13976 */
13977HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13978{
13979 LogFlowThisFunc(("\n"));
13980
13981 AutoCaller autoCaller(this);
13982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13983
13984 ComPtr<IInternalSessionControl> directControl;
13985 {
13986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13987 if (mData->mSession.mLockType == LockType_VM)
13988 directControl = mData->mSession.mDirectControl;
13989 }
13990
13991 /* ignore notifications sent after #OnSessionEnd() is called */
13992 if (!directControl)
13993 return S_OK;
13994
13995 return directControl->OnAudioAdapterChange(audioAdapter);
13996}
13997
13998/**
13999 * @note Locks this object for reading.
14000 */
14001HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005 AutoCaller autoCaller(this);
14006 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14007
14008 ComPtr<IInternalSessionControl> directControl;
14009 {
14010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14011 if (mData->mSession.mLockType == LockType_VM)
14012 directControl = mData->mSession.mDirectControl;
14013 }
14014
14015 /* ignore notifications sent after #OnSessionEnd() is called */
14016 if (!directControl)
14017 return S_OK;
14018
14019 return directControl->OnSerialPortChange(serialPort);
14020}
14021
14022/**
14023 * @note Locks this object for reading.
14024 */
14025HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14026{
14027 LogFlowThisFunc(("\n"));
14028
14029 AutoCaller autoCaller(this);
14030 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14031
14032 ComPtr<IInternalSessionControl> directControl;
14033 {
14034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14035 if (mData->mSession.mLockType == LockType_VM)
14036 directControl = mData->mSession.mDirectControl;
14037 }
14038
14039 /* ignore notifications sent after #OnSessionEnd() is called */
14040 if (!directControl)
14041 return S_OK;
14042
14043 return directControl->OnParallelPortChange(parallelPort);
14044}
14045
14046/**
14047 * @note Locks this object for reading.
14048 */
14049HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14050{
14051 LogFlowThisFunc(("\n"));
14052
14053 AutoCaller autoCaller(this);
14054 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14055
14056 ComPtr<IInternalSessionControl> directControl;
14057 {
14058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14059 if (mData->mSession.mLockType == LockType_VM)
14060 directControl = mData->mSession.mDirectControl;
14061 }
14062
14063 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14064
14065 /* ignore notifications sent after #OnSessionEnd() is called */
14066 if (!directControl)
14067 return S_OK;
14068
14069 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14070}
14071
14072/**
14073 * @note Locks this object for reading.
14074 */
14075HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14076{
14077 LogFlowThisFunc(("\n"));
14078
14079 AutoCaller autoCaller(this);
14080 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14081
14082 ComPtr<IInternalSessionControl> directControl;
14083 {
14084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14085 if (mData->mSession.mLockType == LockType_VM)
14086 directControl = mData->mSession.mDirectControl;
14087 }
14088
14089 mParent->i_onMediumChanged(aAttachment);
14090
14091 /* ignore notifications sent after #OnSessionEnd() is called */
14092 if (!directControl)
14093 return S_OK;
14094
14095 return directControl->OnMediumChange(aAttachment, aForce);
14096}
14097
14098HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14099{
14100 LogFlowThisFunc(("\n"));
14101
14102 AutoCaller autoCaller(this);
14103 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14104
14105 ComPtr<IInternalSessionControl> directControl;
14106 {
14107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14108 if (mData->mSession.mLockType == LockType_VM)
14109 directControl = mData->mSession.mDirectControl;
14110 }
14111
14112 /* ignore notifications sent after #OnSessionEnd() is called */
14113 if (!directControl)
14114 return S_OK;
14115
14116 return directControl->OnVMProcessPriorityChange(aPriority);
14117}
14118
14119/**
14120 * @note Locks this object for reading.
14121 */
14122HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14123{
14124 LogFlowThisFunc(("\n"));
14125
14126 AutoCaller autoCaller(this);
14127 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14128
14129 ComPtr<IInternalSessionControl> directControl;
14130 {
14131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14132 if (mData->mSession.mLockType == LockType_VM)
14133 directControl = mData->mSession.mDirectControl;
14134 }
14135
14136 /* ignore notifications sent after #OnSessionEnd() is called */
14137 if (!directControl)
14138 return S_OK;
14139
14140 return directControl->OnCPUChange(aCPU, aRemove);
14141}
14142
14143HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14144{
14145 LogFlowThisFunc(("\n"));
14146
14147 AutoCaller autoCaller(this);
14148 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14149
14150 ComPtr<IInternalSessionControl> directControl;
14151 {
14152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14153 if (mData->mSession.mLockType == LockType_VM)
14154 directControl = mData->mSession.mDirectControl;
14155 }
14156
14157 /* ignore notifications sent after #OnSessionEnd() is called */
14158 if (!directControl)
14159 return S_OK;
14160
14161 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14162}
14163
14164/**
14165 * @note Locks this object for reading.
14166 */
14167HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14168{
14169 LogFlowThisFunc(("\n"));
14170
14171 AutoCaller autoCaller(this);
14172 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14173
14174 ComPtr<IInternalSessionControl> directControl;
14175 {
14176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14177 if (mData->mSession.mLockType == LockType_VM)
14178 directControl = mData->mSession.mDirectControl;
14179 }
14180
14181 /* ignore notifications sent after #OnSessionEnd() is called */
14182 if (!directControl)
14183 return S_OK;
14184
14185 return directControl->OnVRDEServerChange(aRestart);
14186}
14187
14188/**
14189 * @note Locks this object for reading.
14190 */
14191HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14192{
14193 LogFlowThisFunc(("\n"));
14194
14195 AutoCaller autoCaller(this);
14196 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14197
14198 ComPtr<IInternalSessionControl> directControl;
14199 {
14200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14201 if (mData->mSession.mLockType == LockType_VM)
14202 directControl = mData->mSession.mDirectControl;
14203 }
14204
14205 /* ignore notifications sent after #OnSessionEnd() is called */
14206 if (!directControl)
14207 return S_OK;
14208
14209 return directControl->OnRecordingChange(aEnable);
14210}
14211
14212/**
14213 * @note Locks this object for reading.
14214 */
14215HRESULT SessionMachine::i_onUSBControllerChange()
14216{
14217 LogFlowThisFunc(("\n"));
14218
14219 AutoCaller autoCaller(this);
14220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14221
14222 ComPtr<IInternalSessionControl> directControl;
14223 {
14224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14225 if (mData->mSession.mLockType == LockType_VM)
14226 directControl = mData->mSession.mDirectControl;
14227 }
14228
14229 /* ignore notifications sent after #OnSessionEnd() is called */
14230 if (!directControl)
14231 return S_OK;
14232
14233 return directControl->OnUSBControllerChange();
14234}
14235
14236/**
14237 * @note Locks this object for reading.
14238 */
14239HRESULT SessionMachine::i_onSharedFolderChange()
14240{
14241 LogFlowThisFunc(("\n"));
14242
14243 AutoCaller autoCaller(this);
14244 AssertComRCReturnRC(autoCaller.rc());
14245
14246 ComPtr<IInternalSessionControl> directControl;
14247 {
14248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14249 if (mData->mSession.mLockType == LockType_VM)
14250 directControl = mData->mSession.mDirectControl;
14251 }
14252
14253 /* ignore notifications sent after #OnSessionEnd() is called */
14254 if (!directControl)
14255 return S_OK;
14256
14257 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14258}
14259
14260/**
14261 * @note Locks this object for reading.
14262 */
14263HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14264{
14265 LogFlowThisFunc(("\n"));
14266
14267 AutoCaller autoCaller(this);
14268 AssertComRCReturnRC(autoCaller.rc());
14269
14270 ComPtr<IInternalSessionControl> directControl;
14271 {
14272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14273 if (mData->mSession.mLockType == LockType_VM)
14274 directControl = mData->mSession.mDirectControl;
14275 }
14276
14277 /* ignore notifications sent after #OnSessionEnd() is called */
14278 if (!directControl)
14279 return S_OK;
14280
14281 return directControl->OnClipboardModeChange(aClipboardMode);
14282}
14283
14284/**
14285 * @note Locks this object for reading.
14286 */
14287HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14288{
14289 LogFlowThisFunc(("\n"));
14290
14291 AutoCaller autoCaller(this);
14292 AssertComRCReturnRC(autoCaller.rc());
14293
14294 ComPtr<IInternalSessionControl> directControl;
14295 {
14296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14297 if (mData->mSession.mLockType == LockType_VM)
14298 directControl = mData->mSession.mDirectControl;
14299 }
14300
14301 /* ignore notifications sent after #OnSessionEnd() is called */
14302 if (!directControl)
14303 return S_OK;
14304
14305 return directControl->OnClipboardFileTransferModeChange(aEnable);
14306}
14307
14308/**
14309 * @note Locks this object for reading.
14310 */
14311HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14312{
14313 LogFlowThisFunc(("\n"));
14314
14315 AutoCaller autoCaller(this);
14316 AssertComRCReturnRC(autoCaller.rc());
14317
14318 ComPtr<IInternalSessionControl> directControl;
14319 {
14320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14321 if (mData->mSession.mLockType == LockType_VM)
14322 directControl = mData->mSession.mDirectControl;
14323 }
14324
14325 /* ignore notifications sent after #OnSessionEnd() is called */
14326 if (!directControl)
14327 return S_OK;
14328
14329 return directControl->OnDnDModeChange(aDnDMode);
14330}
14331
14332/**
14333 * @note Locks this object for reading.
14334 */
14335HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14336{
14337 LogFlowThisFunc(("\n"));
14338
14339 AutoCaller autoCaller(this);
14340 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14341
14342 ComPtr<IInternalSessionControl> directControl;
14343 {
14344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14345 if (mData->mSession.mLockType == LockType_VM)
14346 directControl = mData->mSession.mDirectControl;
14347 }
14348
14349 /* ignore notifications sent after #OnSessionEnd() is called */
14350 if (!directControl)
14351 return S_OK;
14352
14353 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14354}
14355
14356/**
14357 * @note Locks this object for reading.
14358 */
14359HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14360{
14361 LogFlowThisFunc(("\n"));
14362
14363 AutoCaller autoCaller(this);
14364 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14365
14366 ComPtr<IInternalSessionControl> directControl;
14367 {
14368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14369 if (mData->mSession.mLockType == LockType_VM)
14370 directControl = mData->mSession.mDirectControl;
14371 }
14372
14373 /* ignore notifications sent after #OnSessionEnd() is called */
14374 if (!directControl)
14375 return S_OK;
14376
14377 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14378}
14379
14380/**
14381 * Returns @c true if this machine's USB controller reports it has a matching
14382 * filter for the given USB device and @c false otherwise.
14383 *
14384 * @note locks this object for reading.
14385 */
14386bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14387{
14388 AutoCaller autoCaller(this);
14389 /* silently return if not ready -- this method may be called after the
14390 * direct machine session has been called */
14391 if (!autoCaller.isOk())
14392 return false;
14393
14394#ifdef VBOX_WITH_USB
14395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14396
14397 switch (mData->mMachineState)
14398 {
14399 case MachineState_Starting:
14400 case MachineState_Restoring:
14401 case MachineState_TeleportingIn:
14402 case MachineState_Paused:
14403 case MachineState_Running:
14404 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14405 * elsewhere... */
14406 alock.release();
14407 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14408 default: break;
14409 }
14410#else
14411 NOREF(aDevice);
14412 NOREF(aMaskedIfs);
14413#endif
14414 return false;
14415}
14416
14417/**
14418 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14419 */
14420HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14421 IVirtualBoxErrorInfo *aError,
14422 ULONG aMaskedIfs,
14423 const com::Utf8Str &aCaptureFilename)
14424{
14425 LogFlowThisFunc(("\n"));
14426
14427 AutoCaller autoCaller(this);
14428
14429 /* This notification may happen after the machine object has been
14430 * uninitialized (the session was closed), so don't assert. */
14431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14432
14433 ComPtr<IInternalSessionControl> directControl;
14434 {
14435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14436 if (mData->mSession.mLockType == LockType_VM)
14437 directControl = mData->mSession.mDirectControl;
14438 }
14439
14440 /* fail on notifications sent after #OnSessionEnd() is called, it is
14441 * expected by the caller */
14442 if (!directControl)
14443 return E_FAIL;
14444
14445 /* No locks should be held at this point. */
14446 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14447 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14448
14449 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14450}
14451
14452/**
14453 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14454 */
14455HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14456 IVirtualBoxErrorInfo *aError)
14457{
14458 LogFlowThisFunc(("\n"));
14459
14460 AutoCaller autoCaller(this);
14461
14462 /* This notification may happen after the machine object has been
14463 * uninitialized (the session was closed), so don't assert. */
14464 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14465
14466 ComPtr<IInternalSessionControl> directControl;
14467 {
14468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14469 if (mData->mSession.mLockType == LockType_VM)
14470 directControl = mData->mSession.mDirectControl;
14471 }
14472
14473 /* fail on notifications sent after #OnSessionEnd() is called, it is
14474 * expected by the caller */
14475 if (!directControl)
14476 return E_FAIL;
14477
14478 /* No locks should be held at this point. */
14479 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14480 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14481
14482 return directControl->OnUSBDeviceDetach(aId, aError);
14483}
14484
14485// protected methods
14486/////////////////////////////////////////////////////////////////////////////
14487
14488/**
14489 * Deletes the given file if it is no longer in use by either the current machine state
14490 * (if the machine is "saved") or any of the machine's snapshots.
14491 *
14492 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14493 * but is different for each SnapshotMachine. When calling this, the order of calling this
14494 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14495 * is therefore critical. I know, it's all rather messy.
14496 *
14497 * @param strStateFile
14498 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14499 * the test for whether the saved state file is in use.
14500 */
14501void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14502 Snapshot *pSnapshotToIgnore)
14503{
14504 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14505 if ( (strStateFile.isNotEmpty())
14506 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14507 )
14508 // ... and it must also not be shared with other snapshots
14509 if ( !mData->mFirstSnapshot
14510 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14511 // this checks the SnapshotMachine's state file paths
14512 )
14513 RTFileDelete(strStateFile.c_str());
14514}
14515
14516/**
14517 * Locks the attached media.
14518 *
14519 * All attached hard disks are locked for writing and DVD/floppy are locked for
14520 * reading. Parents of attached hard disks (if any) are locked for reading.
14521 *
14522 * This method also performs accessibility check of all media it locks: if some
14523 * media is inaccessible, the method will return a failure and a bunch of
14524 * extended error info objects per each inaccessible medium.
14525 *
14526 * Note that this method is atomic: if it returns a success, all media are
14527 * locked as described above; on failure no media is locked at all (all
14528 * succeeded individual locks will be undone).
14529 *
14530 * The caller is responsible for doing the necessary state sanity checks.
14531 *
14532 * The locks made by this method must be undone by calling #unlockMedia() when
14533 * no more needed.
14534 */
14535HRESULT SessionMachine::i_lockMedia()
14536{
14537 AutoCaller autoCaller(this);
14538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14539
14540 AutoMultiWriteLock2 alock(this->lockHandle(),
14541 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14542
14543 /* bail out if trying to lock things with already set up locking */
14544 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14545
14546 MultiResult mrc(S_OK);
14547
14548 /* Collect locking information for all medium objects attached to the VM. */
14549 for (MediumAttachmentList::const_iterator
14550 it = mMediumAttachments->begin();
14551 it != mMediumAttachments->end();
14552 ++it)
14553 {
14554 MediumAttachment *pAtt = *it;
14555 DeviceType_T devType = pAtt->i_getType();
14556 Medium *pMedium = pAtt->i_getMedium();
14557
14558 MediumLockList *pMediumLockList(new MediumLockList());
14559 // There can be attachments without a medium (floppy/dvd), and thus
14560 // it's impossible to create a medium lock list. It still makes sense
14561 // to have the empty medium lock list in the map in case a medium is
14562 // attached later.
14563 if (pMedium != NULL)
14564 {
14565 MediumType_T mediumType = pMedium->i_getType();
14566 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14567 || mediumType == MediumType_Shareable;
14568 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14569
14570 alock.release();
14571 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14572 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14573 false /* fMediumLockWriteAll */,
14574 NULL,
14575 *pMediumLockList);
14576 alock.acquire();
14577 if (FAILED(mrc))
14578 {
14579 delete pMediumLockList;
14580 mData->mSession.mLockedMedia.Clear();
14581 break;
14582 }
14583 }
14584
14585 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14586 if (FAILED(rc))
14587 {
14588 mData->mSession.mLockedMedia.Clear();
14589 mrc = setError(rc,
14590 tr("Collecting locking information for all attached media failed"));
14591 break;
14592 }
14593 }
14594
14595 if (SUCCEEDED(mrc))
14596 {
14597 /* Now lock all media. If this fails, nothing is locked. */
14598 alock.release();
14599 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14600 alock.acquire();
14601 if (FAILED(rc))
14602 {
14603 mrc = setError(rc,
14604 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14605 }
14606 }
14607
14608 return mrc;
14609}
14610
14611/**
14612 * Undoes the locks made by by #lockMedia().
14613 */
14614HRESULT SessionMachine::i_unlockMedia()
14615{
14616 AutoCaller autoCaller(this);
14617 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14618
14619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14620
14621 /* we may be holding important error info on the current thread;
14622 * preserve it */
14623 ErrorInfoKeeper eik;
14624
14625 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14626 AssertComRC(rc);
14627 return rc;
14628}
14629
14630/**
14631 * Helper to change the machine state (reimplementation).
14632 *
14633 * @note Locks this object for writing.
14634 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14635 * it can cause crashes in random places due to unexpectedly committing
14636 * the current settings. The caller is responsible for that. The call
14637 * to saveStateSettings is fine, because this method does not commit.
14638 */
14639HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14640{
14641 LogFlowThisFuncEnter();
14642
14643 AutoCaller autoCaller(this);
14644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14645
14646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14647
14648 MachineState_T oldMachineState = mData->mMachineState;
14649
14650 AssertMsgReturn(oldMachineState != aMachineState,
14651 ("oldMachineState=%s, aMachineState=%s\n",
14652 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14653 E_FAIL);
14654
14655 HRESULT rc = S_OK;
14656
14657 int stsFlags = 0;
14658 bool deleteSavedState = false;
14659
14660 /* detect some state transitions */
14661
14662 if ( ( ( oldMachineState == MachineState_Saved
14663 || oldMachineState == MachineState_AbortedSaved
14664 )
14665 && aMachineState == MachineState_Restoring
14666 )
14667 || ( ( oldMachineState == MachineState_PoweredOff
14668 || oldMachineState == MachineState_Teleported
14669 || oldMachineState == MachineState_Aborted
14670 )
14671 && ( aMachineState == MachineState_TeleportingIn
14672 || aMachineState == MachineState_Starting
14673 )
14674 )
14675 )
14676 {
14677 /* The EMT thread is about to start */
14678
14679 /* Nothing to do here for now... */
14680
14681 /// @todo NEWMEDIA don't let mDVDDrive and other children
14682 /// change anything when in the Starting/Restoring state
14683 }
14684 else if ( ( oldMachineState == MachineState_Running
14685 || oldMachineState == MachineState_Paused
14686 || oldMachineState == MachineState_Teleporting
14687 || oldMachineState == MachineState_OnlineSnapshotting
14688 || oldMachineState == MachineState_LiveSnapshotting
14689 || oldMachineState == MachineState_Stuck
14690 || oldMachineState == MachineState_Starting
14691 || oldMachineState == MachineState_Stopping
14692 || oldMachineState == MachineState_Saving
14693 || oldMachineState == MachineState_Restoring
14694 || oldMachineState == MachineState_TeleportingPausedVM
14695 || oldMachineState == MachineState_TeleportingIn
14696 )
14697 && ( aMachineState == MachineState_PoweredOff
14698 || aMachineState == MachineState_Saved
14699 || aMachineState == MachineState_Teleported
14700 || aMachineState == MachineState_Aborted
14701 || aMachineState == MachineState_AbortedSaved
14702 )
14703 )
14704 {
14705 /* The EMT thread has just stopped, unlock attached media. Note that as
14706 * opposed to locking that is done from Console, we do unlocking here
14707 * because the VM process may have aborted before having a chance to
14708 * properly unlock all media it locked. */
14709
14710 unlockMedia();
14711 }
14712
14713 if (oldMachineState == MachineState_Restoring)
14714 {
14715 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14716 {
14717 /*
14718 * delete the saved state file once the machine has finished
14719 * restoring from it (note that Console sets the state from
14720 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14721 * to give the user an ability to fix an error and retry --
14722 * we keep the saved state file in this case)
14723 */
14724 deleteSavedState = true;
14725 }
14726 }
14727 else if ( oldMachineState == MachineState_Saved
14728 && ( aMachineState == MachineState_PoweredOff
14729 || aMachineState == MachineState_Teleported
14730 )
14731 )
14732 {
14733 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14734 deleteSavedState = true;
14735 mData->mCurrentStateModified = TRUE;
14736 stsFlags |= SaveSTS_CurStateModified;
14737 }
14738 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14739 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14740
14741 if ( aMachineState == MachineState_Starting
14742 || aMachineState == MachineState_Restoring
14743 || aMachineState == MachineState_TeleportingIn
14744 )
14745 {
14746 /* set the current state modified flag to indicate that the current
14747 * state is no more identical to the state in the
14748 * current snapshot */
14749 if (!mData->mCurrentSnapshot.isNull())
14750 {
14751 mData->mCurrentStateModified = TRUE;
14752 stsFlags |= SaveSTS_CurStateModified;
14753 }
14754 }
14755
14756 if (deleteSavedState)
14757 {
14758 if (mRemoveSavedState)
14759 {
14760 Assert(!mSSData->strStateFilePath.isEmpty());
14761
14762 // it is safe to delete the saved state file if ...
14763 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14764 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14765 // ... none of the snapshots share the saved state file
14766 )
14767 RTFileDelete(mSSData->strStateFilePath.c_str());
14768 }
14769
14770 mSSData->strStateFilePath.setNull();
14771 stsFlags |= SaveSTS_StateFilePath;
14772 }
14773
14774 /* redirect to the underlying peer machine */
14775 mPeer->i_setMachineState(aMachineState);
14776
14777 if ( oldMachineState != MachineState_RestoringSnapshot
14778 && ( aMachineState == MachineState_PoweredOff
14779 || aMachineState == MachineState_Teleported
14780 || aMachineState == MachineState_Aborted
14781 || aMachineState == MachineState_AbortedSaved
14782 || aMachineState == MachineState_Saved))
14783 {
14784 /* the machine has stopped execution
14785 * (or the saved state file was adopted) */
14786 stsFlags |= SaveSTS_StateTimeStamp;
14787 }
14788
14789 if ( ( oldMachineState == MachineState_PoweredOff
14790 || oldMachineState == MachineState_Aborted
14791 || oldMachineState == MachineState_Teleported
14792 )
14793 && aMachineState == MachineState_Saved)
14794 {
14795 /* the saved state file was adopted */
14796 Assert(!mSSData->strStateFilePath.isEmpty());
14797 stsFlags |= SaveSTS_StateFilePath;
14798 }
14799
14800#ifdef VBOX_WITH_GUEST_PROPS
14801 if ( aMachineState == MachineState_PoweredOff
14802 || aMachineState == MachineState_Aborted
14803 || aMachineState == MachineState_Teleported)
14804 {
14805 /* Make sure any transient guest properties get removed from the
14806 * property store on shutdown. */
14807 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14808
14809 /* remove it from the settings representation */
14810 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14811 for (settings::GuestPropertiesList::iterator
14812 it = llGuestProperties.begin();
14813 it != llGuestProperties.end();
14814 /*nothing*/)
14815 {
14816 const settings::GuestProperty &prop = *it;
14817 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14818 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14819 {
14820 it = llGuestProperties.erase(it);
14821 fNeedsSaving = true;
14822 }
14823 else
14824 {
14825 ++it;
14826 }
14827 }
14828
14829 /* Additionally remove it from the HWData representation. Required to
14830 * keep everything in sync, as this is what the API keeps using. */
14831 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14832 for (HWData::GuestPropertyMap::iterator
14833 it = llHWGuestProperties.begin();
14834 it != llHWGuestProperties.end();
14835 /*nothing*/)
14836 {
14837 uint32_t fFlags = it->second.mFlags;
14838 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14839 {
14840 /* iterator where we need to continue after the erase call
14841 * (C++03 is a fact still, and it doesn't return the iterator
14842 * which would allow continuing) */
14843 HWData::GuestPropertyMap::iterator it2 = it;
14844 ++it2;
14845 llHWGuestProperties.erase(it);
14846 it = it2;
14847 fNeedsSaving = true;
14848 }
14849 else
14850 {
14851 ++it;
14852 }
14853 }
14854
14855 if (fNeedsSaving)
14856 {
14857 mData->mCurrentStateModified = TRUE;
14858 stsFlags |= SaveSTS_CurStateModified;
14859 }
14860 }
14861#endif /* VBOX_WITH_GUEST_PROPS */
14862
14863 rc = i_saveStateSettings(stsFlags);
14864
14865 if ( ( oldMachineState != MachineState_PoweredOff
14866 && oldMachineState != MachineState_Aborted
14867 && oldMachineState != MachineState_Teleported
14868 )
14869 && ( aMachineState == MachineState_PoweredOff
14870 || aMachineState == MachineState_Aborted
14871 || aMachineState == MachineState_Teleported
14872 )
14873 )
14874 {
14875 /* we've been shut down for any reason */
14876 /* no special action so far */
14877 }
14878
14879 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14880 LogFlowThisFuncLeave();
14881 return rc;
14882}
14883
14884/**
14885 * Sends the current machine state value to the VM process.
14886 *
14887 * @note Locks this object for reading, then calls a client process.
14888 */
14889HRESULT SessionMachine::i_updateMachineStateOnClient()
14890{
14891 AutoCaller autoCaller(this);
14892 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14893
14894 ComPtr<IInternalSessionControl> directControl;
14895 {
14896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14897 AssertReturn(!!mData, E_FAIL);
14898 if (mData->mSession.mLockType == LockType_VM)
14899 directControl = mData->mSession.mDirectControl;
14900
14901 /* directControl may be already set to NULL here in #OnSessionEnd()
14902 * called too early by the direct session process while there is still
14903 * some operation (like deleting the snapshot) in progress. The client
14904 * process in this case is waiting inside Session::close() for the
14905 * "end session" process object to complete, while #uninit() called by
14906 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14907 * operation to complete. For now, we accept this inconsistent behavior
14908 * and simply do nothing here. */
14909
14910 if (mData->mSession.mState == SessionState_Unlocking)
14911 return S_OK;
14912 }
14913
14914 /* ignore notifications sent after #OnSessionEnd() is called */
14915 if (!directControl)
14916 return S_OK;
14917
14918 return directControl->UpdateMachineState(mData->mMachineState);
14919}
14920
14921
14922/*static*/
14923HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14924{
14925 va_list args;
14926 va_start(args, pcszMsg);
14927 HRESULT rc = setErrorInternalV(aResultCode,
14928 getStaticClassIID(),
14929 getStaticComponentName(),
14930 pcszMsg, args,
14931 false /* aWarning */,
14932 true /* aLogIt */);
14933 va_end(args);
14934 return rc;
14935}
14936
14937
14938HRESULT Machine::updateState(MachineState_T aState)
14939{
14940 NOREF(aState);
14941 ReturnComNotImplemented();
14942}
14943
14944HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14945{
14946 NOREF(aProgress);
14947 ReturnComNotImplemented();
14948}
14949
14950HRESULT Machine::endPowerUp(LONG aResult)
14951{
14952 NOREF(aResult);
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14957{
14958 NOREF(aProgress);
14959 ReturnComNotImplemented();
14960}
14961
14962HRESULT Machine::endPoweringDown(LONG aResult,
14963 const com::Utf8Str &aErrMsg)
14964{
14965 NOREF(aResult);
14966 NOREF(aErrMsg);
14967 ReturnComNotImplemented();
14968}
14969
14970HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14971 BOOL *aMatched,
14972 ULONG *aMaskedInterfaces)
14973{
14974 NOREF(aDevice);
14975 NOREF(aMatched);
14976 NOREF(aMaskedInterfaces);
14977 ReturnComNotImplemented();
14978
14979}
14980
14981HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14982{
14983 NOREF(aId); NOREF(aCaptureFilename);
14984 ReturnComNotImplemented();
14985}
14986
14987HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14988 BOOL aDone)
14989{
14990 NOREF(aId);
14991 NOREF(aDone);
14992 ReturnComNotImplemented();
14993}
14994
14995HRESULT Machine::autoCaptureUSBDevices()
14996{
14997 ReturnComNotImplemented();
14998}
14999
15000HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15001{
15002 NOREF(aDone);
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15007 ComPtr<IProgress> &aProgress)
15008{
15009 NOREF(aSession);
15010 NOREF(aProgress);
15011 ReturnComNotImplemented();
15012}
15013
15014HRESULT Machine::finishOnlineMergeMedium()
15015{
15016 ReturnComNotImplemented();
15017}
15018
15019HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15020 std::vector<com::Utf8Str> &aValues,
15021 std::vector<LONG64> &aTimestamps,
15022 std::vector<com::Utf8Str> &aFlags)
15023{
15024 NOREF(aNames);
15025 NOREF(aValues);
15026 NOREF(aTimestamps);
15027 NOREF(aFlags);
15028 ReturnComNotImplemented();
15029}
15030
15031HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15032 const com::Utf8Str &aValue,
15033 LONG64 aTimestamp,
15034 const com::Utf8Str &aFlags)
15035{
15036 NOREF(aName);
15037 NOREF(aValue);
15038 NOREF(aTimestamp);
15039 NOREF(aFlags);
15040 ReturnComNotImplemented();
15041}
15042
15043HRESULT Machine::lockMedia()
15044{
15045 ReturnComNotImplemented();
15046}
15047
15048HRESULT Machine::unlockMedia()
15049{
15050 ReturnComNotImplemented();
15051}
15052
15053HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15054 ComPtr<IMediumAttachment> &aNewAttachment)
15055{
15056 NOREF(aAttachment);
15057 NOREF(aNewAttachment);
15058 ReturnComNotImplemented();
15059}
15060
15061HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15062 ULONG aCpuUser,
15063 ULONG aCpuKernel,
15064 ULONG aCpuIdle,
15065 ULONG aMemTotal,
15066 ULONG aMemFree,
15067 ULONG aMemBalloon,
15068 ULONG aMemShared,
15069 ULONG aMemCache,
15070 ULONG aPagedTotal,
15071 ULONG aMemAllocTotal,
15072 ULONG aMemFreeTotal,
15073 ULONG aMemBalloonTotal,
15074 ULONG aMemSharedTotal,
15075 ULONG aVmNetRx,
15076 ULONG aVmNetTx)
15077{
15078 NOREF(aValidStats);
15079 NOREF(aCpuUser);
15080 NOREF(aCpuKernel);
15081 NOREF(aCpuIdle);
15082 NOREF(aMemTotal);
15083 NOREF(aMemFree);
15084 NOREF(aMemBalloon);
15085 NOREF(aMemShared);
15086 NOREF(aMemCache);
15087 NOREF(aPagedTotal);
15088 NOREF(aMemAllocTotal);
15089 NOREF(aMemFreeTotal);
15090 NOREF(aMemBalloonTotal);
15091 NOREF(aMemSharedTotal);
15092 NOREF(aVmNetRx);
15093 NOREF(aVmNetTx);
15094 ReturnComNotImplemented();
15095}
15096
15097HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15098 com::Utf8Str &aResult)
15099{
15100 NOREF(aAuthParams);
15101 NOREF(aResult);
15102 ReturnComNotImplemented();
15103}
15104
15105com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15106{
15107 com::Utf8Str strControllerName = "Unknown";
15108 switch (aBusType)
15109 {
15110 case StorageBus_IDE:
15111 {
15112 strControllerName = "IDE";
15113 break;
15114 }
15115 case StorageBus_SATA:
15116 {
15117 strControllerName = "SATA";
15118 break;
15119 }
15120 case StorageBus_SCSI:
15121 {
15122 strControllerName = "SCSI";
15123 break;
15124 }
15125 case StorageBus_Floppy:
15126 {
15127 strControllerName = "Floppy";
15128 break;
15129 }
15130 case StorageBus_SAS:
15131 {
15132 strControllerName = "SAS";
15133 break;
15134 }
15135 case StorageBus_USB:
15136 {
15137 strControllerName = "USB";
15138 break;
15139 }
15140 default:
15141 break;
15142 }
15143 return strControllerName;
15144}
15145
15146HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15147{
15148 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15149
15150 AutoCaller autoCaller(this);
15151 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15152
15153 HRESULT rc = S_OK;
15154
15155 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15156 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15157 rc = getUSBDeviceFilters(usbDeviceFilters);
15158 if (FAILED(rc)) return rc;
15159
15160 NOREF(aFlags);
15161 com::Utf8Str osTypeId;
15162 ComObjPtr<GuestOSType> osType = NULL;
15163
15164 /* Get the guest os type as a string from the VB. */
15165 rc = getOSTypeId(osTypeId);
15166 if (FAILED(rc)) return rc;
15167
15168 /* Get the os type obj that coresponds, can be used to get
15169 * the defaults for this guest OS. */
15170 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15171 if (FAILED(rc)) return rc;
15172
15173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15174
15175 /* Let the OS type select 64-bit ness. */
15176 mHWData->mLongMode = osType->i_is64Bit()
15177 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15178
15179 /* Let the OS type enable the X2APIC */
15180 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15181
15182 /* This one covers IOAPICEnabled. */
15183 mBIOSSettings->i_applyDefaults(osType);
15184
15185 /* Initialize default record settings. */
15186 mRecordingSettings->i_applyDefaults();
15187
15188 /* Initialize default BIOS settings here */
15189 /* Hardware virtualization must be ON by default */
15190 mHWData->mAPIC = true;
15191 mHWData->mHWVirtExEnabled = true;
15192
15193 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15194 if (FAILED(rc)) return rc;
15195
15196 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15197 if (FAILED(rc)) return rc;
15198
15199 /* Graphics stuff. */
15200 GraphicsControllerType_T graphicsController;
15201 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15202 if (FAILED(rc)) return rc;
15203
15204 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15205 if (FAILED(rc)) return rc;
15206
15207 ULONG vramSize;
15208 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15212 if (FAILED(rc)) return rc;
15213
15214 BOOL fAccelerate2DVideoEnabled;
15215 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15216 if (FAILED(rc)) return rc;
15217
15218 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15219 if (FAILED(rc)) return rc;
15220
15221 BOOL fAccelerate3DEnabled;
15222 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15223 if (FAILED(rc)) return rc;
15224
15225 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15226 if (FAILED(rc)) return rc;
15227
15228 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15229 if (FAILED(rc)) return rc;
15230
15231 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15232 if (FAILED(rc)) return rc;
15233
15234 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15235 if (FAILED(rc)) return rc;
15236
15237 BOOL mRTCUseUTC;
15238 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15239 if (FAILED(rc)) return rc;
15240
15241 setRTCUseUTC(mRTCUseUTC);
15242 if (FAILED(rc)) return rc;
15243
15244 /* the setter does more than just the assignment, so use it */
15245 ChipsetType_T enmChipsetType;
15246 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15247 if (FAILED(rc)) return rc;
15248
15249 rc = COMSETTER(ChipsetType)(enmChipsetType);
15250 if (FAILED(rc)) return rc;
15251
15252 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15253 if (FAILED(rc)) return rc;
15254
15255 /* Apply IOMMU defaults. */
15256 IommuType_T enmIommuType;
15257 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15258 if (FAILED(rc)) return rc;
15259
15260 rc = COMSETTER(IommuType)(enmIommuType);
15261 if (FAILED(rc)) return rc;
15262
15263 /* Apply network adapters defaults */
15264 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15265 mNetworkAdapters[slot]->i_applyDefaults(osType);
15266
15267 /* Apply serial port defaults */
15268 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15269 mSerialPorts[slot]->i_applyDefaults(osType);
15270
15271 /* Apply parallel port defaults - not OS dependent*/
15272 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15273 mParallelPorts[slot]->i_applyDefaults();
15274
15275 /* This one covers the TPM type. */
15276 mTrustedPlatformModule->i_applyDefaults(osType);
15277
15278 /* This one covers secure boot. */
15279 rc = mNvramStore->i_applyDefaults(osType);
15280 if (FAILED(rc)) return rc;
15281
15282 /* Audio stuff. */
15283 AudioControllerType_T audioController;
15284 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15285 if (FAILED(rc)) return rc;
15286
15287 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15288 if (FAILED(rc)) return rc;
15289
15290 AudioCodecType_T audioCodec;
15291 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15292 if (FAILED(rc)) return rc;
15293
15294 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15295 if (FAILED(rc)) return rc;
15296
15297 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15298 if (FAILED(rc)) return rc;
15299
15300 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15301 if (FAILED(rc)) return rc;
15302
15303 /* Storage Controllers */
15304 StorageControllerType_T hdStorageControllerType;
15305 StorageBus_T hdStorageBusType;
15306 StorageControllerType_T dvdStorageControllerType;
15307 StorageBus_T dvdStorageBusType;
15308 BOOL recommendedFloppy;
15309 ComPtr<IStorageController> floppyController;
15310 ComPtr<IStorageController> hdController;
15311 ComPtr<IStorageController> dvdController;
15312 Utf8Str strFloppyName, strDVDName, strHDName;
15313
15314 /* GUI auto generates controller names using bus type. Do the same*/
15315 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15316
15317 /* Floppy recommended? add one. */
15318 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15319 if (FAILED(rc)) return rc;
15320 if (recommendedFloppy)
15321 {
15322 rc = addStorageController(strFloppyName,
15323 StorageBus_Floppy,
15324 floppyController);
15325 if (FAILED(rc)) return rc;
15326 }
15327
15328 /* Setup one DVD storage controller. */
15329 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15330 if (FAILED(rc)) return rc;
15331
15332 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15333 if (FAILED(rc)) return rc;
15334
15335 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15336
15337 rc = addStorageController(strDVDName,
15338 dvdStorageBusType,
15339 dvdController);
15340 if (FAILED(rc)) return rc;
15341
15342 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15343 if (FAILED(rc)) return rc;
15344
15345 /* Setup one HDD storage controller. */
15346 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15347 if (FAILED(rc)) return rc;
15348
15349 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15350 if (FAILED(rc)) return rc;
15351
15352 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15353
15354 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15355 {
15356 rc = addStorageController(strHDName,
15357 hdStorageBusType,
15358 hdController);
15359 if (FAILED(rc)) return rc;
15360
15361 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15362 if (FAILED(rc)) return rc;
15363 }
15364 else
15365 {
15366 /* The HD controller is the same as DVD: */
15367 hdController = dvdController;
15368 }
15369
15370 /* Limit the AHCI port count if it's used because windows has trouble with
15371 * too many ports and other guest (OS X in particular) may take extra long
15372 * boot: */
15373
15374 // pParent = static_cast<Medium*>(aP)
15375 IStorageController *temp = hdController;
15376 ComObjPtr<StorageController> storageController;
15377 storageController = static_cast<StorageController *>(temp);
15378
15379 // tempHDController = aHDController;
15380 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15381 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15382 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15383 storageController->COMSETTER(PortCount)(1);
15384
15385 /* USB stuff */
15386
15387 bool ohciEnabled = false;
15388
15389 ComPtr<IUSBController> usbController;
15390 BOOL recommendedUSB3;
15391 BOOL recommendedUSB;
15392 BOOL usbProxyAvailable;
15393
15394 getUSBProxyAvailable(&usbProxyAvailable);
15395 if (FAILED(rc)) return rc;
15396
15397 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15398 if (FAILED(rc)) return rc;
15399 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15400 if (FAILED(rc)) return rc;
15401
15402 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15403 {
15404#ifdef VBOX_WITH_EXTPACK
15405 /* USB 3.0 is only available if the proper ExtPack is installed. */
15406 ExtPackManager *aManager = mParent->i_getExtPackManager();
15407 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15408 {
15409 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15410 if (FAILED(rc)) return rc;
15411
15412 /* xHci includes OHCI */
15413 ohciEnabled = true;
15414 }
15415#endif
15416 }
15417 if ( !ohciEnabled
15418 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15419 {
15420 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15421 if (FAILED(rc)) return rc;
15422 ohciEnabled = true;
15423
15424#ifdef VBOX_WITH_EXTPACK
15425 /* USB 2.0 is only available if the proper ExtPack is installed.
15426 * Note. Configuring EHCI here and providing messages about
15427 * the missing extpack isn't exactly clean, but it is a
15428 * necessary evil to patch over legacy compatability issues
15429 * introduced by the new distribution model. */
15430 ExtPackManager *manager = mParent->i_getExtPackManager();
15431 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15432 {
15433 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15434 if (FAILED(rc)) return rc;
15435 }
15436#endif
15437 }
15438
15439 /* Set recommended human interface device types: */
15440 BOOL recommendedUSBHID;
15441 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15442 if (FAILED(rc)) return rc;
15443
15444 if (recommendedUSBHID)
15445 {
15446 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15447 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15448 if (!ohciEnabled && !usbDeviceFilters.isNull())
15449 {
15450 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15451 if (FAILED(rc)) return rc;
15452 }
15453 }
15454
15455 BOOL recommendedUSBTablet;
15456 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15457 if (FAILED(rc)) return rc;
15458
15459 if (recommendedUSBTablet)
15460 {
15461 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15462 if (!ohciEnabled && !usbDeviceFilters.isNull())
15463 {
15464 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15465 if (FAILED(rc)) return rc;
15466 }
15467 }
15468 return S_OK;
15469}
15470
15471/* This isn't handled entirely by the wrapper generator yet. */
15472#ifdef VBOX_WITH_XPCOM
15473NS_DECL_CLASSINFO(SessionMachine)
15474NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15475
15476NS_DECL_CLASSINFO(SnapshotMachine)
15477NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15478#endif
Note: See TracBrowser for help on using the repository browser.

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