VirtualBox

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

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

Main/NvramStore,FE/VBoxManage: Allow multiple NVRAM states (UEFI,TPM,etc.) to exist for a VM and and manage them in a central place. This allows to collect them in a single tar archive and provide a single interface to get access to the individual states (work in progress), bugref:10098

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 539.0 KB
Line 
1/* $Id: MachineImpl.cpp 91326 2021-09-22 15:10:38Z 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 /* Apply BIOS defaults. */
369 mBIOSSettings->i_applyDefaults(aOsType);
370
371 /* Apply record defaults. */
372 mRecordingSettings->i_applyDefaults();
373
374 /* Apply network adapters defaults */
375 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
376 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
377
378 /* Apply serial port defaults */
379 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
380 mSerialPorts[slot]->i_applyDefaults(aOsType);
381
382 /* Apply parallel port defaults */
383 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
384 mParallelPorts[slot]->i_applyDefaults();
385
386 /* At this point the changing of the current state modification
387 * flag is allowed. */
388 i_allowStateModification();
389
390 /* commit all changes made during the initialization */
391 i_commit();
392 }
393
394 /* Confirm a successful initialization when it's the case */
395 if (SUCCEEDED(rc))
396 {
397 if (mData->mAccessible)
398 autoInitSpan.setSucceeded();
399 else
400 autoInitSpan.setLimited();
401 }
402
403 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
404 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
405 mData->mRegistered,
406 mData->mAccessible,
407 rc));
408
409 LogFlowThisFuncLeave();
410
411 return rc;
412}
413
414/**
415 * Initializes a new instance with data from machine XML (formerly Init_Registered).
416 * Gets called in two modes:
417 *
418 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
419 * UUID is specified and we mark the machine as "registered";
420 *
421 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
422 * and the machine remains unregistered until RegisterMachine() is called.
423 *
424 * @param aParent Associated parent object
425 * @param strConfigFile Local file system path to the VM settings file (can
426 * be relative to the VirtualBox config directory).
427 * @param aId UUID of the machine or NULL (see above).
428 *
429 * @return Success indicator. if not S_OK, the machine object is invalid
430 */
431HRESULT Machine::initFromSettings(VirtualBox *aParent,
432 const Utf8Str &strConfigFile,
433 const Guid *aId)
434{
435 LogFlowThisFuncEnter();
436 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
437
438 /* Enclose the state transition NotReady->InInit->Ready */
439 AutoInitSpan autoInitSpan(this);
440 AssertReturn(autoInitSpan.isOk(), E_FAIL);
441
442 HRESULT rc = initImpl(aParent, strConfigFile);
443 if (FAILED(rc)) return rc;
444
445 if (aId)
446 {
447 // loading a registered VM:
448 unconst(mData->mUuid) = *aId;
449 mData->mRegistered = TRUE;
450 // now load the settings from XML:
451 rc = i_registeredInit();
452 // this calls initDataAndChildObjects() and loadSettings()
453 }
454 else
455 {
456 // opening an unregistered VM (VirtualBox::OpenMachine()):
457 rc = initDataAndChildObjects();
458
459 if (SUCCEEDED(rc))
460 {
461 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
462 mData->mAccessible = TRUE;
463
464 try
465 {
466 // load and parse machine XML; this will throw on XML or logic errors
467 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
468
469 // reject VM UUID duplicates, they can happen if someone
470 // tries to register an already known VM config again
471 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
472 true /* fPermitInaccessible */,
473 false /* aDoSetError */,
474 NULL) != VBOX_E_OBJECT_NOT_FOUND)
475 {
476 throw setError(E_FAIL,
477 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
478 mData->m_strConfigFile.c_str());
479 }
480
481 // use UUID from machine config
482 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
483
484 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
485 NULL /* puuidRegistry */);
486 if (FAILED(rc)) throw rc;
487
488 /* At this point the changing of the current state modification
489 * flag is allowed. */
490 i_allowStateModification();
491
492 i_commit();
493 }
494 catch (HRESULT err)
495 {
496 /* we assume that error info is set by the thrower */
497 rc = err;
498 }
499 catch (...)
500 {
501 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
502 }
503 }
504 }
505
506 /* Confirm a successful initialization when it's the case */
507 if (SUCCEEDED(rc))
508 {
509 if (mData->mAccessible)
510 autoInitSpan.setSucceeded();
511 else
512 {
513 autoInitSpan.setLimited();
514
515 // uninit media from this machine's media registry, or else
516 // reloading the settings will fail
517 mParent->i_unregisterMachineMedia(i_getId());
518 }
519 }
520
521 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
522 "rc=%08X\n",
523 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
524 mData->mRegistered, mData->mAccessible, rc));
525
526 LogFlowThisFuncLeave();
527
528 return rc;
529}
530
531/**
532 * Initializes a new instance from a machine config that is already in memory
533 * (import OVF case). Since we are importing, the UUID in the machine
534 * config is ignored and we always generate a fresh one.
535 *
536 * @param aParent Associated parent object.
537 * @param strName Name for the new machine; this overrides what is specified in config.
538 * @param strSettingsFilename File name of .vbox file.
539 * @param config Machine configuration loaded and parsed from XML.
540 *
541 * @return Success indicator. if not S_OK, the machine object is invalid
542 */
543HRESULT Machine::init(VirtualBox *aParent,
544 const Utf8Str &strName,
545 const Utf8Str &strSettingsFilename,
546 const settings::MachineConfigFile &config)
547{
548 LogFlowThisFuncEnter();
549
550 /* Enclose the state transition NotReady->InInit->Ready */
551 AutoInitSpan autoInitSpan(this);
552 AssertReturn(autoInitSpan.isOk(), E_FAIL);
553
554 HRESULT rc = initImpl(aParent, strSettingsFilename);
555 if (FAILED(rc)) return rc;
556
557 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
558 if (FAILED(rc)) return rc;
559
560 rc = initDataAndChildObjects();
561
562 if (SUCCEEDED(rc))
563 {
564 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
565 mData->mAccessible = TRUE;
566
567 // create empty machine config for instance data
568 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
569
570 // generate fresh UUID, ignore machine config
571 unconst(mData->mUuid).create();
572
573 rc = i_loadMachineDataFromSettings(config,
574 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
575
576 // override VM name as well, it may be different
577 mUserData->s.strName = strName;
578
579 if (SUCCEEDED(rc))
580 {
581 /* At this point the changing of the current state modification
582 * flag is allowed. */
583 i_allowStateModification();
584
585 /* commit all changes made during the initialization */
586 i_commit();
587 }
588 }
589
590 /* Confirm a successful initialization when it's the case */
591 if (SUCCEEDED(rc))
592 {
593 if (mData->mAccessible)
594 autoInitSpan.setSucceeded();
595 else
596 {
597 /* Ignore all errors from unregistering, they would destroy
598- * the more interesting error information we already have,
599- * pinpointing the issue with the VM config. */
600 ErrorInfoKeeper eik;
601
602 autoInitSpan.setLimited();
603
604 // uninit media from this machine's media registry, or else
605 // reloading the settings will fail
606 mParent->i_unregisterMachineMedia(i_getId());
607 }
608 }
609
610 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
611 "rc=%08X\n",
612 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
613 mData->mRegistered, mData->mAccessible, rc));
614
615 LogFlowThisFuncLeave();
616
617 return rc;
618}
619
620/**
621 * Shared code between the various init() implementations.
622 * @param aParent The VirtualBox object.
623 * @param strConfigFile Settings file.
624 * @return
625 */
626HRESULT Machine::initImpl(VirtualBox *aParent,
627 const Utf8Str &strConfigFile)
628{
629 LogFlowThisFuncEnter();
630
631 AssertReturn(aParent, E_INVALIDARG);
632 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
633
634 HRESULT rc = S_OK;
635
636 /* share the parent weakly */
637 unconst(mParent) = aParent;
638
639 /* allocate the essential machine data structure (the rest will be
640 * allocated later by initDataAndChildObjects() */
641 mData.allocate();
642
643 /* memorize the config file name (as provided) */
644 mData->m_strConfigFile = strConfigFile;
645
646 /* get the full file name */
647 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
648 if (RT_FAILURE(vrc1))
649 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
650 tr("Invalid machine settings file name '%s' (%Rrc)"),
651 strConfigFile.c_str(),
652 vrc1);
653
654 LogFlowThisFuncLeave();
655
656 return rc;
657}
658
659/**
660 * Tries to create a machine settings file in the path stored in the machine
661 * instance data. Used when a new machine is created to fail gracefully if
662 * the settings file could not be written (e.g. because machine dir is read-only).
663 * @return
664 */
665HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
666{
667 HRESULT rc = S_OK;
668
669 // when we create a new machine, we must be able to create the settings file
670 RTFILE f = NIL_RTFILE;
671 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
672 if ( RT_SUCCESS(vrc)
673 || vrc == VERR_SHARING_VIOLATION
674 )
675 {
676 if (RT_SUCCESS(vrc))
677 RTFileClose(f);
678 if (!fForceOverwrite)
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Machine settings file '%s' already exists"),
681 mData->m_strConfigFileFull.c_str());
682 else
683 {
684 /* try to delete the config file, as otherwise the creation
685 * of a new settings file will fail. */
686 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
687 if (RT_FAILURE(vrc2))
688 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
689 tr("Could not delete the existing settings file '%s' (%Rrc)"),
690 mData->m_strConfigFileFull.c_str(), vrc2);
691 }
692 }
693 else if ( vrc != VERR_FILE_NOT_FOUND
694 && vrc != VERR_PATH_NOT_FOUND
695 )
696 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
697 tr("Invalid machine settings file name '%s' (%Rrc)"),
698 mData->m_strConfigFileFull.c_str(),
699 vrc);
700 return rc;
701}
702
703/**
704 * Initializes the registered machine by loading the settings file.
705 * This method is separated from #init() in order to make it possible to
706 * retry the operation after VirtualBox startup instead of refusing to
707 * startup the whole VirtualBox server in case if the settings file of some
708 * registered VM is invalid or inaccessible.
709 *
710 * @note Must be always called from this object's write lock
711 * (unless called from #init() that doesn't need any locking).
712 * @note Locks the mUSBController method for writing.
713 * @note Subclasses must not call this method.
714 */
715HRESULT Machine::i_registeredInit()
716{
717 AssertReturn(!i_isSessionMachine(), E_FAIL);
718 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
719 AssertReturn(mData->mUuid.isValid(), E_FAIL);
720 AssertReturn(!mData->mAccessible, E_FAIL);
721
722 HRESULT rc = initDataAndChildObjects();
723
724 if (SUCCEEDED(rc))
725 {
726 /* Temporarily reset the registered flag in order to let setters
727 * potentially called from loadSettings() succeed (isMutable() used in
728 * all setters will return FALSE for a Machine instance if mRegistered
729 * is TRUE). */
730 mData->mRegistered = FALSE;
731
732 try
733 {
734 // load and parse machine XML; this will throw on XML or logic errors
735 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
736
737 if (mData->mUuid != mData->pMachineConfigFile->uuid)
738 throw setError(E_FAIL,
739 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
740 mData->pMachineConfigFile->uuid.raw(),
741 mData->m_strConfigFileFull.c_str(),
742 mData->mUuid.toString().c_str(),
743 mParent->i_settingsFilePath().c_str());
744
745 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
746 NULL /* const Guid *puuidRegistry */);
747 if (FAILED(rc)) throw rc;
748 }
749 catch (HRESULT err)
750 {
751 /* we assume that error info is set by the thrower */
752 rc = err;
753 }
754 catch (...)
755 {
756 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
757 }
758
759 /* Restore the registered flag (even on failure) */
760 mData->mRegistered = TRUE;
761 }
762
763 if (SUCCEEDED(rc))
764 {
765 /* Set mAccessible to TRUE only if we successfully locked and loaded
766 * the settings file */
767 mData->mAccessible = TRUE;
768
769 /* commit all changes made during loading the settings file */
770 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
771 /// @todo r=klaus for some reason the settings loading logic backs up
772 // the settings, and therefore a commit is needed. Should probably be changed.
773 }
774 else
775 {
776 /* If the machine is registered, then, instead of returning a
777 * failure, we mark it as inaccessible and set the result to
778 * success to give it a try later */
779
780 /* fetch the current error info */
781 mData->mAccessError = com::ErrorInfo();
782 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
783
784 /* rollback all changes */
785 i_rollback(false /* aNotify */);
786
787 // uninit media from this machine's media registry, or else
788 // reloading the settings will fail
789 mParent->i_unregisterMachineMedia(i_getId());
790
791 /* uninitialize the common part to make sure all data is reset to
792 * default (null) values */
793 uninitDataAndChildObjects();
794
795 rc = S_OK;
796 }
797
798 return rc;
799}
800
801/**
802 * Uninitializes the instance.
803 * Called either from FinalRelease() or by the parent when it gets destroyed.
804 *
805 * @note The caller of this method must make sure that this object
806 * a) doesn't have active callers on the current thread and b) is not locked
807 * by the current thread; otherwise uninit() will hang either a) due to
808 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
809 * a dead-lock caused by this thread waiting for all callers on the other
810 * threads are done but preventing them from doing so by holding a lock.
811 */
812void Machine::uninit()
813{
814 LogFlowThisFuncEnter();
815
816 Assert(!isWriteLockOnCurrentThread());
817
818 Assert(!uRegistryNeedsSaving);
819 if (uRegistryNeedsSaving)
820 {
821 AutoCaller autoCaller(this);
822 if (SUCCEEDED(autoCaller.rc()))
823 {
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825 i_saveSettings(NULL, Machine::SaveS_Force);
826 }
827 }
828
829 /* Enclose the state transition Ready->InUninit->NotReady */
830 AutoUninitSpan autoUninitSpan(this);
831 if (autoUninitSpan.uninitDone())
832 return;
833
834 Assert(!i_isSnapshotMachine());
835 Assert(!i_isSessionMachine());
836 Assert(!!mData);
837
838 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
839 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
840
841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
842
843 if (!mData->mSession.mMachine.isNull())
844 {
845 /* Theoretically, this can only happen if the VirtualBox server has been
846 * terminated while there were clients running that owned open direct
847 * sessions. Since in this case we are definitely called by
848 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
849 * won't happen on the client watcher thread (because it has a
850 * VirtualBox caller for the duration of the
851 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
852 * cannot happen until the VirtualBox caller is released). This is
853 * important, because SessionMachine::uninit() cannot correctly operate
854 * after we return from this method (it expects the Machine instance is
855 * still valid). We'll call it ourselves below.
856 */
857 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
858 (SessionMachine*)mData->mSession.mMachine));
859
860 if (Global::IsOnlineOrTransient(mData->mMachineState))
861 {
862 Log1WarningThisFunc(("Setting state to Aborted!\n"));
863 /* set machine state using SessionMachine reimplementation */
864 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
865 }
866
867 /*
868 * Uninitialize SessionMachine using public uninit() to indicate
869 * an unexpected uninitialization.
870 */
871 mData->mSession.mMachine->uninit();
872 /* SessionMachine::uninit() must set mSession.mMachine to null */
873 Assert(mData->mSession.mMachine.isNull());
874 }
875
876 // uninit media from this machine's media registry, if they're still there
877 Guid uuidMachine(i_getId());
878
879 /* the lock is no more necessary (SessionMachine is uninitialized) */
880 alock.release();
881
882 /* XXX This will fail with
883 * "cannot be closed because it is still attached to 1 virtual machines"
884 * because at this point we did not call uninitDataAndChildObjects() yet
885 * and therefore also removeBackReference() for all these mediums was not called! */
886
887 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
888 mParent->i_unregisterMachineMedia(uuidMachine);
889
890 // has machine been modified?
891 if (mData->flModifications)
892 {
893 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
894 i_rollback(false /* aNotify */);
895 }
896
897 if (mData->mAccessible)
898 uninitDataAndChildObjects();
899
900 /* free the essential data structure last */
901 mData.free();
902
903 LogFlowThisFuncLeave();
904}
905
906// Wrapped IMachine properties
907/////////////////////////////////////////////////////////////////////////////
908HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
909{
910 /* mParent is constant during life time, no need to lock */
911 ComObjPtr<VirtualBox> pVirtualBox(mParent);
912 aParent = pVirtualBox;
913
914 return S_OK;
915}
916
917
918HRESULT Machine::getAccessible(BOOL *aAccessible)
919{
920 /* In some cases (medium registry related), it is necessary to be able to
921 * go through the list of all machines. Happens when an inaccessible VM
922 * has a sensible medium registry. */
923 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
925
926 HRESULT rc = S_OK;
927
928 if (!mData->mAccessible)
929 {
930 /* try to initialize the VM once more if not accessible */
931
932 AutoReinitSpan autoReinitSpan(this);
933 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
934
935#ifdef DEBUG
936 LogFlowThisFunc(("Dumping media backreferences\n"));
937 mParent->i_dumpAllBackRefs();
938#endif
939
940 if (mData->pMachineConfigFile)
941 {
942 // reset the XML file to force loadSettings() (called from i_registeredInit())
943 // to parse it again; the file might have changed
944 delete mData->pMachineConfigFile;
945 mData->pMachineConfigFile = NULL;
946 }
947
948 rc = i_registeredInit();
949
950 if (SUCCEEDED(rc) && mData->mAccessible)
951 {
952 autoReinitSpan.setSucceeded();
953
954 /* make sure interesting parties will notice the accessibility
955 * state change */
956 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
957 mParent->i_onMachineDataChanged(mData->mUuid);
958 }
959 }
960
961 if (SUCCEEDED(rc))
962 *aAccessible = mData->mAccessible;
963
964 LogFlowThisFuncLeave();
965
966 return rc;
967}
968
969HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
970{
971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
972
973 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
974 {
975 /* return shortly */
976 aAccessError = NULL;
977 return S_OK;
978 }
979
980 HRESULT rc = S_OK;
981
982 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
983 rc = errorInfo.createObject();
984 if (SUCCEEDED(rc))
985 {
986 errorInfo->init(mData->mAccessError.getResultCode(),
987 mData->mAccessError.getInterfaceID().ref(),
988 Utf8Str(mData->mAccessError.getComponent()).c_str(),
989 Utf8Str(mData->mAccessError.getText()));
990 aAccessError = errorInfo;
991 }
992
993 return rc;
994}
995
996HRESULT Machine::getName(com::Utf8Str &aName)
997{
998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 aName = mUserData->s.strName;
1001
1002 return S_OK;
1003}
1004
1005HRESULT Machine::setName(const com::Utf8Str &aName)
1006{
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 i_setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1027{
1028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1029
1030 aDescription = mUserData->s.strDescription;
1031
1032 return S_OK;
1033}
1034
1035HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1036{
1037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1038
1039 // this can be done in principle in any state as it doesn't affect the VM
1040 // significantly, but play safe by not messing around while complex
1041 // activities are going on
1042 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1043 if (FAILED(rc)) return rc;
1044
1045 i_setModified(IsModified_MachineData);
1046 mUserData.backup();
1047 mUserData->s.strDescription = aDescription;
1048
1049 return S_OK;
1050}
1051
1052HRESULT Machine::getId(com::Guid &aId)
1053{
1054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1055
1056 aId = mData->mUuid;
1057
1058 return S_OK;
1059}
1060
1061HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1062{
1063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1064 aGroups.resize(mUserData->s.llGroups.size());
1065 size_t i = 0;
1066 for (StringsList::const_iterator
1067 it = mUserData->s.llGroups.begin();
1068 it != mUserData->s.llGroups.end();
1069 ++it, ++i)
1070 aGroups[i] = (*it);
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1076{
1077 StringsList llGroups;
1078 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1079 if (FAILED(rc))
1080 return rc;
1081
1082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 rc = i_checkStateDependency(MutableOrSavedStateDep);
1085 if (FAILED(rc)) return rc;
1086
1087 i_setModified(IsModified_MachineData);
1088 mUserData.backup();
1089 mUserData->s.llGroups = llGroups;
1090
1091 return S_OK;
1092}
1093
1094HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1095{
1096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 aOSTypeId = mUserData->s.strOsType;
1099
1100 return S_OK;
1101}
1102
1103HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1104{
1105 /* look up the object by Id to check it is valid */
1106 ComObjPtr<GuestOSType> pGuestOSType;
1107 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1108
1109 /* when setting, always use the "etalon" value for consistency -- lookup
1110 * by ID is case-insensitive and the input value may have different case */
1111 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1112
1113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 HRESULT rc = i_checkStateDependency(MutableStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 i_setModified(IsModified_MachineData);
1119 mUserData.backup();
1120 mUserData->s.strOsType = osTypeId;
1121
1122 return S_OK;
1123}
1124
1125HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1126{
1127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1128
1129 *aFirmwareType = mHWData->mFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1135{
1136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 HRESULT rc = i_checkStateDependency(MutableStateDep);
1139 if (FAILED(rc)) return rc;
1140
1141 i_setModified(IsModified_MachineData);
1142 mHWData.backup();
1143 mHWData->mFirmwareType = aFirmwareType;
1144 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1145 alock.release();
1146
1147 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aPointingHIDType = mHWData->mPointingHIDType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 i_setModified(IsModified_MachineData);
1192 mHWData.backup();
1193 mHWData->mPointingHIDType = aPointingHIDType;
1194
1195 return S_OK;
1196}
1197
1198HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1199{
1200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 *aChipsetType = mHWData->mChipsetType;
1203
1204 return S_OK;
1205}
1206
1207HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1208{
1209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1210
1211 HRESULT rc = i_checkStateDependency(MutableStateDep);
1212 if (FAILED(rc)) return rc;
1213
1214 if (aChipsetType != mHWData->mChipsetType)
1215 {
1216 i_setModified(IsModified_MachineData);
1217 mHWData.backup();
1218 mHWData->mChipsetType = aChipsetType;
1219
1220 // Resize network adapter array, to be finalized on commit/rollback.
1221 // We must not throw away entries yet, otherwise settings are lost
1222 // without a way to roll back.
1223 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1224 size_t oldCount = mNetworkAdapters.size();
1225 if (newCount > oldCount)
1226 {
1227 mNetworkAdapters.resize(newCount);
1228 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1229 {
1230 unconst(mNetworkAdapters[slot]).createObject();
1231 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1232 }
1233 }
1234 }
1235
1236 return S_OK;
1237}
1238
1239HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1240{
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 *aIommuType = mHWData->mIommuType;
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::setIommuType(IommuType_T aIommuType)
1249{
1250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 HRESULT rc = i_checkStateDependency(MutableStateDep);
1253 if (FAILED(rc)) return rc;
1254
1255 if (aIommuType != mHWData->mIommuType)
1256 {
1257 if (aIommuType == IommuType_Intel)
1258 {
1259#ifndef VBOX_WITH_IOMMU_INTEL
1260 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1261 return E_UNEXPECTED;
1262#endif
1263 }
1264
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mIommuType = aIommuType;
1268 }
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1274{
1275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 aParavirtDebug = mHWData->mParavirtDebug;
1278 return S_OK;
1279}
1280
1281HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1282{
1283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 HRESULT rc = i_checkStateDependency(MutableStateDep);
1286 if (FAILED(rc)) return rc;
1287
1288 /** @todo Parse/validate options? */
1289 if (aParavirtDebug != mHWData->mParavirtDebug)
1290 {
1291 i_setModified(IsModified_MachineData);
1292 mHWData.backup();
1293 mHWData->mParavirtDebug = aParavirtDebug;
1294 }
1295
1296 return S_OK;
1297}
1298
1299HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1300{
1301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1302
1303 *aParavirtProvider = mHWData->mParavirtProvider;
1304
1305 return S_OK;
1306}
1307
1308HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1309{
1310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 HRESULT rc = i_checkStateDependency(MutableStateDep);
1313 if (FAILED(rc)) return rc;
1314
1315 if (aParavirtProvider != mHWData->mParavirtProvider)
1316 {
1317 i_setModified(IsModified_MachineData);
1318 mHWData.backup();
1319 mHWData->mParavirtProvider = aParavirtProvider;
1320 }
1321
1322 return S_OK;
1323}
1324
1325HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1326{
1327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1328
1329 *aParavirtProvider = mHWData->mParavirtProvider;
1330 switch (mHWData->mParavirtProvider)
1331 {
1332 case ParavirtProvider_None:
1333 case ParavirtProvider_HyperV:
1334 case ParavirtProvider_KVM:
1335 case ParavirtProvider_Minimal:
1336 break;
1337
1338 /* Resolve dynamic provider types to the effective types. */
1339 default:
1340 {
1341 ComObjPtr<GuestOSType> pGuestOSType;
1342 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1343 pGuestOSType);
1344 if (FAILED(hrc2) || pGuestOSType.isNull())
1345 {
1346 *aParavirtProvider = ParavirtProvider_None;
1347 break;
1348 }
1349
1350 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1351 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1352
1353 switch (mHWData->mParavirtProvider)
1354 {
1355 case ParavirtProvider_Legacy:
1356 {
1357 if (fOsXGuest)
1358 *aParavirtProvider = ParavirtProvider_Minimal;
1359 else
1360 *aParavirtProvider = ParavirtProvider_None;
1361 break;
1362 }
1363
1364 case ParavirtProvider_Default:
1365 {
1366 if (fOsXGuest)
1367 *aParavirtProvider = ParavirtProvider_Minimal;
1368 else if ( mUserData->s.strOsType == "Windows10"
1369 || mUserData->s.strOsType == "Windows10_64"
1370 || mUserData->s.strOsType == "Windows81"
1371 || mUserData->s.strOsType == "Windows81_64"
1372 || mUserData->s.strOsType == "Windows8"
1373 || mUserData->s.strOsType == "Windows8_64"
1374 || mUserData->s.strOsType == "Windows7"
1375 || mUserData->s.strOsType == "Windows7_64"
1376 || mUserData->s.strOsType == "WindowsVista"
1377 || mUserData->s.strOsType == "WindowsVista_64"
1378 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1379 || mUserData->s.strOsType.startsWith("Windows201"))
1380 && mUserData->s.strOsType.endsWith("_64"))
1381 || mUserData->s.strOsType == "Windows2012"
1382 || mUserData->s.strOsType == "Windows2012_64"
1383 || mUserData->s.strOsType == "Windows2008"
1384 || mUserData->s.strOsType == "Windows2008_64")
1385 {
1386 *aParavirtProvider = ParavirtProvider_HyperV;
1387 }
1388 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1389 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1390 || mUserData->s.strOsType == "Linux"
1391 || mUserData->s.strOsType == "Linux_64"
1392 || mUserData->s.strOsType == "ArchLinux"
1393 || mUserData->s.strOsType == "ArchLinux_64"
1394 || mUserData->s.strOsType == "Debian"
1395 || mUserData->s.strOsType == "Debian_64"
1396 || mUserData->s.strOsType == "Fedora"
1397 || mUserData->s.strOsType == "Fedora_64"
1398 || mUserData->s.strOsType == "Gentoo"
1399 || mUserData->s.strOsType == "Gentoo_64"
1400 || mUserData->s.strOsType == "Mandriva"
1401 || mUserData->s.strOsType == "Mandriva_64"
1402 || mUserData->s.strOsType == "OpenSUSE"
1403 || mUserData->s.strOsType == "OpenSUSE_64"
1404 || mUserData->s.strOsType == "Oracle"
1405 || mUserData->s.strOsType == "Oracle_64"
1406 || mUserData->s.strOsType == "RedHat"
1407 || mUserData->s.strOsType == "RedHat_64"
1408 || mUserData->s.strOsType == "Turbolinux"
1409 || mUserData->s.strOsType == "Turbolinux_64"
1410 || mUserData->s.strOsType == "Ubuntu"
1411 || mUserData->s.strOsType == "Ubuntu_64"
1412 || mUserData->s.strOsType == "Xandros"
1413 || mUserData->s.strOsType == "Xandros_64")
1414 {
1415 *aParavirtProvider = ParavirtProvider_KVM;
1416 }
1417 else
1418 *aParavirtProvider = ParavirtProvider_None;
1419 break;
1420 }
1421
1422 default: AssertFailedBreak(); /* Shut up MSC. */
1423 }
1424 break;
1425 }
1426 }
1427
1428 Assert( *aParavirtProvider == ParavirtProvider_None
1429 || *aParavirtProvider == ParavirtProvider_Minimal
1430 || *aParavirtProvider == ParavirtProvider_HyperV
1431 || *aParavirtProvider == ParavirtProvider_KVM);
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 aHardwareVersion = mHWData->mHWVersion;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1445{
1446 /* check known version */
1447 Utf8Str hwVersion = aHardwareVersion;
1448 if ( hwVersion.compare("1") != 0
1449 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1450 return setError(E_INVALIDARG,
1451 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1452
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 HRESULT rc = i_checkStateDependency(MutableStateDep);
1456 if (FAILED(rc)) return rc;
1457
1458 i_setModified(IsModified_MachineData);
1459 mHWData.backup();
1460 mHWData->mHWVersion = aHardwareVersion;
1461
1462 return S_OK;
1463}
1464
1465HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1466{
1467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1468
1469 if (!mHWData->mHardwareUUID.isZero())
1470 aHardwareUUID = mHWData->mHardwareUUID;
1471 else
1472 aHardwareUUID = mData->mUuid;
1473
1474 return S_OK;
1475}
1476
1477HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1478{
1479 if (!aHardwareUUID.isValid())
1480 return E_INVALIDARG;
1481
1482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1483
1484 HRESULT rc = i_checkStateDependency(MutableStateDep);
1485 if (FAILED(rc)) return rc;
1486
1487 i_setModified(IsModified_MachineData);
1488 mHWData.backup();
1489 if (aHardwareUUID == mData->mUuid)
1490 mHWData->mHardwareUUID.clear();
1491 else
1492 mHWData->mHardwareUUID = aHardwareUUID;
1493
1494 return S_OK;
1495}
1496
1497HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1498{
1499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1500
1501 *aMemorySize = mHWData->mMemorySize;
1502
1503 return S_OK;
1504}
1505
1506HRESULT Machine::setMemorySize(ULONG aMemorySize)
1507{
1508 /* check RAM limits */
1509 if ( aMemorySize < MM_RAM_MIN_IN_MB
1510 || aMemorySize > MM_RAM_MAX_IN_MB
1511 )
1512 return setError(E_INVALIDARG,
1513 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1514 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1515
1516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1517
1518 HRESULT rc = i_checkStateDependency(MutableStateDep);
1519 if (FAILED(rc)) return rc;
1520
1521 i_setModified(IsModified_MachineData);
1522 mHWData.backup();
1523 mHWData->mMemorySize = aMemorySize;
1524
1525 return S_OK;
1526}
1527
1528HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1529{
1530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 *aCPUCount = mHWData->mCPUCount;
1533
1534 return S_OK;
1535}
1536
1537HRESULT Machine::setCPUCount(ULONG aCPUCount)
1538{
1539 /* check CPU limits */
1540 if ( aCPUCount < SchemaDefs::MinCPUCount
1541 || aCPUCount > SchemaDefs::MaxCPUCount
1542 )
1543 return setError(E_INVALIDARG,
1544 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1545 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1550 if (mHWData->mCPUHotPlugEnabled)
1551 {
1552 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1553 {
1554 if (mHWData->mCPUAttached[idx])
1555 return setError(E_INVALIDARG,
1556 tr("There is still a CPU attached to socket %lu."
1557 "Detach the CPU before removing the socket"),
1558 aCPUCount, idx+1);
1559 }
1560 }
1561
1562 HRESULT rc = i_checkStateDependency(MutableStateDep);
1563 if (FAILED(rc)) return rc;
1564
1565 i_setModified(IsModified_MachineData);
1566 mHWData.backup();
1567 mHWData->mCPUCount = aCPUCount;
1568
1569 return S_OK;
1570}
1571
1572HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1573{
1574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1582{
1583 HRESULT rc = S_OK;
1584
1585 /* check throttle limits */
1586 if ( aCPUExecutionCap < 1
1587 || aCPUExecutionCap > 100
1588 )
1589 return setError(E_INVALIDARG,
1590 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1591 aCPUExecutionCap, 1, 100);
1592
1593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 alock.release();
1596 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1597 alock.acquire();
1598 if (FAILED(rc)) return rc;
1599
1600 i_setModified(IsModified_MachineData);
1601 mHWData.backup();
1602 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1603
1604 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1605 if (Global::IsOnline(mData->mMachineState))
1606 i_saveSettings(NULL);
1607
1608 return S_OK;
1609}
1610
1611HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1612{
1613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1621{
1622 HRESULT rc = S_OK;
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 rc = i_checkStateDependency(MutableStateDep);
1627 if (FAILED(rc)) return rc;
1628
1629 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1630 {
1631 if (aCPUHotPlugEnabled)
1632 {
1633 i_setModified(IsModified_MachineData);
1634 mHWData.backup();
1635
1636 /* Add the amount of CPUs currently attached */
1637 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1638 mHWData->mCPUAttached[i] = true;
1639 }
1640 else
1641 {
1642 /*
1643 * We can disable hotplug only if the amount of maximum CPUs is equal
1644 * to the amount of attached CPUs
1645 */
1646 unsigned cCpusAttached = 0;
1647 unsigned iHighestId = 0;
1648
1649 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1650 {
1651 if (mHWData->mCPUAttached[i])
1652 {
1653 cCpusAttached++;
1654 iHighestId = i;
1655 }
1656 }
1657
1658 if ( (cCpusAttached != mHWData->mCPUCount)
1659 || (iHighestId >= mHWData->mCPUCount))
1660 return setError(E_INVALIDARG,
1661 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1662
1663 i_setModified(IsModified_MachineData);
1664 mHWData.backup();
1665 }
1666 }
1667
1668 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1669
1670 return rc;
1671}
1672
1673HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1674{
1675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1678
1679 return S_OK;
1680}
1681
1682HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1683{
1684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1687 if (SUCCEEDED(hrc))
1688 {
1689 i_setModified(IsModified_MachineData);
1690 mHWData.backup();
1691 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1692 }
1693 return hrc;
1694}
1695
1696HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699 aCPUProfile = mHWData->mCpuProfile;
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1704{
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1707 if (SUCCEEDED(hrc))
1708 {
1709 i_setModified(IsModified_MachineData);
1710 mHWData.backup();
1711 /* Empty equals 'host'. */
1712 if (aCPUProfile.isNotEmpty())
1713 mHWData->mCpuProfile = aCPUProfile;
1714 else
1715 mHWData->mCpuProfile = "host";
1716 }
1717 return hrc;
1718}
1719
1720HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1721{
1722#ifdef VBOX_WITH_USB_CARDREADER
1723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1724
1725 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1726
1727 return S_OK;
1728#else
1729 NOREF(aEmulatedUSBCardReaderEnabled);
1730 return E_NOTIMPL;
1731#endif
1732}
1733
1734HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1735{
1736#ifdef VBOX_WITH_USB_CARDREADER
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1740 if (FAILED(rc)) return rc;
1741
1742 i_setModified(IsModified_MachineData);
1743 mHWData.backup();
1744 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1745
1746 return S_OK;
1747#else
1748 NOREF(aEmulatedUSBCardReaderEnabled);
1749 return E_NOTIMPL;
1750#endif
1751}
1752
1753HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1754{
1755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1756
1757 *aHPETEnabled = mHWData->mHPETEnabled;
1758
1759 return S_OK;
1760}
1761
1762HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1763{
1764 HRESULT rc = S_OK;
1765
1766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1767
1768 rc = i_checkStateDependency(MutableStateDep);
1769 if (FAILED(rc)) return rc;
1770
1771 i_setModified(IsModified_MachineData);
1772 mHWData.backup();
1773
1774 mHWData->mHPETEnabled = aHPETEnabled;
1775
1776 return rc;
1777}
1778
1779/** @todo this method should not be public */
1780HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1781{
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1785
1786 return S_OK;
1787}
1788
1789/**
1790 * Set the memory balloon size.
1791 *
1792 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1793 * we have to make sure that we never call IGuest from here.
1794 */
1795HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1796{
1797 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1798#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1799 /* check limits */
1800 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1801 return setError(E_INVALIDARG,
1802 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1803 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1804
1805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 i_setModified(IsModified_MachineData);
1808 mHWData.backup();
1809 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1810
1811 return S_OK;
1812#else
1813 NOREF(aMemoryBalloonSize);
1814 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1815#endif
1816}
1817
1818HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1819{
1820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1823 return S_OK;
1824}
1825
1826HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1827{
1828#ifdef VBOX_WITH_PAGE_SHARING
1829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1830
1831 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1832 i_setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1835 return S_OK;
1836#else
1837 NOREF(aPageFusionEnabled);
1838 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1839#endif
1840}
1841
1842HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1843{
1844 /* mBIOSSettings is constant during life time, no need to lock */
1845 aBIOSSettings = mBIOSSettings;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1851{
1852 /* mTrustedPlatformModule is constant during life time, no need to lock */
1853 aTrustedPlatformModule = mTrustedPlatformModule;
1854
1855 return S_OK;
1856}
1857
1858HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1859{
1860 /* mNvramStore is constant during life time, no need to lock */
1861 aNvramStore = mNvramStore;
1862
1863 return S_OK;
1864}
1865
1866HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1867{
1868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1869
1870 aRecordingSettings = mRecordingSettings;
1871
1872 return S_OK;
1873}
1874
1875HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1876{
1877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1878
1879 aGraphicsAdapter = mGraphicsAdapter;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 switch (aProperty)
1889 {
1890 case CPUPropertyType_PAE:
1891 *aValue = mHWData->mPAEEnabled;
1892 break;
1893
1894 case CPUPropertyType_LongMode:
1895 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1896 *aValue = TRUE;
1897 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1898 *aValue = FALSE;
1899#if HC_ARCH_BITS == 64
1900 else
1901 *aValue = TRUE;
1902#else
1903 else
1904 {
1905 *aValue = FALSE;
1906
1907 ComObjPtr<GuestOSType> pGuestOSType;
1908 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1909 pGuestOSType);
1910 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1911 {
1912 if (pGuestOSType->i_is64Bit())
1913 {
1914 ComObjPtr<Host> pHost = mParent->i_host();
1915 alock.release();
1916
1917 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1918 if (FAILED(hrc2))
1919 *aValue = FALSE;
1920 }
1921 }
1922 }
1923#endif
1924 break;
1925
1926 case CPUPropertyType_TripleFaultReset:
1927 *aValue = mHWData->mTripleFaultReset;
1928 break;
1929
1930 case CPUPropertyType_APIC:
1931 *aValue = mHWData->mAPIC;
1932 break;
1933
1934 case CPUPropertyType_X2APIC:
1935 *aValue = mHWData->mX2APIC;
1936 break;
1937
1938 case CPUPropertyType_IBPBOnVMExit:
1939 *aValue = mHWData->mIBPBOnVMExit;
1940 break;
1941
1942 case CPUPropertyType_IBPBOnVMEntry:
1943 *aValue = mHWData->mIBPBOnVMEntry;
1944 break;
1945
1946 case CPUPropertyType_SpecCtrl:
1947 *aValue = mHWData->mSpecCtrl;
1948 break;
1949
1950 case CPUPropertyType_SpecCtrlByHost:
1951 *aValue = mHWData->mSpecCtrlByHost;
1952 break;
1953
1954 case CPUPropertyType_HWVirt:
1955 *aValue = mHWData->mNestedHWVirt;
1956 break;
1957
1958 case CPUPropertyType_L1DFlushOnEMTScheduling:
1959 *aValue = mHWData->mL1DFlushOnSched;
1960 break;
1961
1962 case CPUPropertyType_L1DFlushOnVMEntry:
1963 *aValue = mHWData->mL1DFlushOnVMEntry;
1964 break;
1965
1966 case CPUPropertyType_MDSClearOnEMTScheduling:
1967 *aValue = mHWData->mMDSClearOnSched;
1968 break;
1969
1970 case CPUPropertyType_MDSClearOnVMEntry:
1971 *aValue = mHWData->mMDSClearOnVMEntry;
1972 break;
1973
1974 default:
1975 return E_INVALIDARG;
1976 }
1977 return S_OK;
1978}
1979
1980HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1981{
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 HRESULT rc = i_checkStateDependency(MutableStateDep);
1985 if (FAILED(rc)) return rc;
1986
1987 switch (aProperty)
1988 {
1989 case CPUPropertyType_PAE:
1990 i_setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mPAEEnabled = !!aValue;
1993 break;
1994
1995 case CPUPropertyType_LongMode:
1996 i_setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1999 break;
2000
2001 case CPUPropertyType_TripleFaultReset:
2002 i_setModified(IsModified_MachineData);
2003 mHWData.backup();
2004 mHWData->mTripleFaultReset = !!aValue;
2005 break;
2006
2007 case CPUPropertyType_APIC:
2008 if (mHWData->mX2APIC)
2009 aValue = TRUE;
2010 i_setModified(IsModified_MachineData);
2011 mHWData.backup();
2012 mHWData->mAPIC = !!aValue;
2013 break;
2014
2015 case CPUPropertyType_X2APIC:
2016 i_setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mX2APIC = !!aValue;
2019 if (aValue)
2020 mHWData->mAPIC = !!aValue;
2021 break;
2022
2023 case CPUPropertyType_IBPBOnVMExit:
2024 i_setModified(IsModified_MachineData);
2025 mHWData.backup();
2026 mHWData->mIBPBOnVMExit = !!aValue;
2027 break;
2028
2029 case CPUPropertyType_IBPBOnVMEntry:
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mIBPBOnVMEntry = !!aValue;
2033 break;
2034
2035 case CPUPropertyType_SpecCtrl:
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mSpecCtrl = !!aValue;
2039 break;
2040
2041 case CPUPropertyType_SpecCtrlByHost:
2042 i_setModified(IsModified_MachineData);
2043 mHWData.backup();
2044 mHWData->mSpecCtrlByHost = !!aValue;
2045 break;
2046
2047 case CPUPropertyType_HWVirt:
2048 i_setModified(IsModified_MachineData);
2049 mHWData.backup();
2050 mHWData->mNestedHWVirt = !!aValue;
2051 break;
2052
2053 case CPUPropertyType_L1DFlushOnEMTScheduling:
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mL1DFlushOnSched = !!aValue;
2057 break;
2058
2059 case CPUPropertyType_L1DFlushOnVMEntry:
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mL1DFlushOnVMEntry = !!aValue;
2063 break;
2064
2065 case CPUPropertyType_MDSClearOnEMTScheduling:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mMDSClearOnSched = !!aValue;
2069 break;
2070
2071 case CPUPropertyType_MDSClearOnVMEntry:
2072 i_setModified(IsModified_MachineData);
2073 mHWData.backup();
2074 mHWData->mMDSClearOnVMEntry = !!aValue;
2075 break;
2076
2077 default:
2078 return E_INVALIDARG;
2079 }
2080 return S_OK;
2081}
2082
2083HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2084 ULONG *aValEcx, ULONG *aValEdx)
2085{
2086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2087 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2088 {
2089 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2090 it != mHWData->mCpuIdLeafList.end();
2091 ++it)
2092 {
2093 if (aOrdinal == 0)
2094 {
2095 const settings::CpuIdLeaf &rLeaf= *it;
2096 *aIdx = rLeaf.idx;
2097 *aSubIdx = rLeaf.idxSub;
2098 *aValEax = rLeaf.uEax;
2099 *aValEbx = rLeaf.uEbx;
2100 *aValEcx = rLeaf.uEcx;
2101 *aValEdx = rLeaf.uEdx;
2102 return S_OK;
2103 }
2104 aOrdinal--;
2105 }
2106 }
2107 return E_INVALIDARG;
2108}
2109
2110HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2111{
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 /*
2115 * Search the list.
2116 */
2117 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2118 {
2119 const settings::CpuIdLeaf &rLeaf= *it;
2120 if ( rLeaf.idx == aIdx
2121 && ( aSubIdx == UINT32_MAX
2122 || rLeaf.idxSub == aSubIdx) )
2123 {
2124 *aValEax = rLeaf.uEax;
2125 *aValEbx = rLeaf.uEbx;
2126 *aValEcx = rLeaf.uEcx;
2127 *aValEdx = rLeaf.uEdx;
2128 return S_OK;
2129 }
2130 }
2131
2132 return E_INVALIDARG;
2133}
2134
2135
2136HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2137{
2138 /*
2139 * Validate input before taking locks and checking state.
2140 */
2141 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2142 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2143 if ( aIdx >= UINT32_C(0x20)
2144 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2145 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2146 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2147
2148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2149 HRESULT rc = i_checkStateDependency(MutableStateDep);
2150 if (FAILED(rc)) return rc;
2151
2152 /*
2153 * Impose a maximum number of leaves.
2154 */
2155 if (mHWData->mCpuIdLeafList.size() > 256)
2156 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2157
2158 /*
2159 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2160 */
2161 i_setModified(IsModified_MachineData);
2162 mHWData.backup();
2163
2164 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2165 {
2166 settings::CpuIdLeaf &rLeaf= *it;
2167 if ( rLeaf.idx == aIdx
2168 && ( aSubIdx == UINT32_MAX
2169 || rLeaf.idxSub == aSubIdx) )
2170 it = mHWData->mCpuIdLeafList.erase(it);
2171 else
2172 ++it;
2173 }
2174
2175 settings::CpuIdLeaf NewLeaf;
2176 NewLeaf.idx = aIdx;
2177 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2178 NewLeaf.uEax = aValEax;
2179 NewLeaf.uEbx = aValEbx;
2180 NewLeaf.uEcx = aValEcx;
2181 NewLeaf.uEdx = aValEdx;
2182 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2183 return S_OK;
2184}
2185
2186HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2187{
2188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 HRESULT rc = i_checkStateDependency(MutableStateDep);
2191 if (FAILED(rc)) return rc;
2192
2193 /*
2194 * Do the removal.
2195 */
2196 bool fModified = mHWData.isBackedUp();
2197 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2198 {
2199 settings::CpuIdLeaf &rLeaf= *it;
2200 if ( rLeaf.idx == aIdx
2201 && ( aSubIdx == UINT32_MAX
2202 || rLeaf.idxSub == aSubIdx) )
2203 {
2204 if (!fModified)
2205 {
2206 fModified = true;
2207 i_setModified(IsModified_MachineData);
2208 mHWData.backup();
2209 // Start from the beginning, since mHWData.backup() creates
2210 // a new list, causing iterator mixup. This makes sure that
2211 // the settings are not unnecessarily marked as modified,
2212 // at the price of extra list walking.
2213 it = mHWData->mCpuIdLeafList.begin();
2214 }
2215 else
2216 it = mHWData->mCpuIdLeafList.erase(it);
2217 }
2218 else
2219 ++it;
2220 }
2221
2222 return S_OK;
2223}
2224
2225HRESULT Machine::removeAllCPUIDLeaves()
2226{
2227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2228
2229 HRESULT rc = i_checkStateDependency(MutableStateDep);
2230 if (FAILED(rc)) return rc;
2231
2232 if (mHWData->mCpuIdLeafList.size() > 0)
2233 {
2234 i_setModified(IsModified_MachineData);
2235 mHWData.backup();
2236
2237 mHWData->mCpuIdLeafList.clear();
2238 }
2239
2240 return S_OK;
2241}
2242HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2243{
2244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2245
2246 switch(aProperty)
2247 {
2248 case HWVirtExPropertyType_Enabled:
2249 *aValue = mHWData->mHWVirtExEnabled;
2250 break;
2251
2252 case HWVirtExPropertyType_VPID:
2253 *aValue = mHWData->mHWVirtExVPIDEnabled;
2254 break;
2255
2256 case HWVirtExPropertyType_NestedPaging:
2257 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2258 break;
2259
2260 case HWVirtExPropertyType_UnrestrictedExecution:
2261 *aValue = mHWData->mHWVirtExUXEnabled;
2262 break;
2263
2264 case HWVirtExPropertyType_LargePages:
2265 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2266#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2267 *aValue = FALSE;
2268#endif
2269 break;
2270
2271 case HWVirtExPropertyType_Force:
2272 *aValue = mHWData->mHWVirtExForceEnabled;
2273 break;
2274
2275 case HWVirtExPropertyType_UseNativeApi:
2276 *aValue = mHWData->mHWVirtExUseNativeApi;
2277 break;
2278
2279 case HWVirtExPropertyType_VirtVmsaveVmload:
2280 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2281 break;
2282
2283 default:
2284 return E_INVALIDARG;
2285 }
2286 return S_OK;
2287}
2288
2289HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2290{
2291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2292
2293 HRESULT rc = i_checkStateDependency(MutableStateDep);
2294 if (FAILED(rc)) return rc;
2295
2296 switch (aProperty)
2297 {
2298 case HWVirtExPropertyType_Enabled:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mHWVirtExEnabled = !!aValue;
2302 break;
2303
2304 case HWVirtExPropertyType_VPID:
2305 i_setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2308 break;
2309
2310 case HWVirtExPropertyType_NestedPaging:
2311 i_setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2314 break;
2315
2316 case HWVirtExPropertyType_UnrestrictedExecution:
2317 i_setModified(IsModified_MachineData);
2318 mHWData.backup();
2319 mHWData->mHWVirtExUXEnabled = !!aValue;
2320 break;
2321
2322 case HWVirtExPropertyType_LargePages:
2323 i_setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2326 break;
2327
2328 case HWVirtExPropertyType_Force:
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mHWVirtExForceEnabled = !!aValue;
2332 break;
2333
2334 case HWVirtExPropertyType_UseNativeApi:
2335 i_setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mHWVirtExUseNativeApi = !!aValue;
2338 break;
2339
2340 case HWVirtExPropertyType_VirtVmsaveVmload:
2341 i_setModified(IsModified_MachineData);
2342 mHWData.backup();
2343 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2344 break;
2345
2346 default:
2347 return E_INVALIDARG;
2348 }
2349
2350 return S_OK;
2351}
2352
2353HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2354{
2355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2356
2357 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2358
2359 return S_OK;
2360}
2361
2362HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2363{
2364 /** @todo (r=dmik):
2365 * 1. Allow to change the name of the snapshot folder containing snapshots
2366 * 2. Rename the folder on disk instead of just changing the property
2367 * value (to be smart and not to leave garbage). Note that it cannot be
2368 * done here because the change may be rolled back. Thus, the right
2369 * place is #saveSettings().
2370 */
2371
2372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 HRESULT rc = i_checkStateDependency(MutableStateDep);
2375 if (FAILED(rc)) return rc;
2376
2377 if (!mData->mCurrentSnapshot.isNull())
2378 return setError(E_FAIL,
2379 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2380
2381 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2382
2383 if (strSnapshotFolder.isEmpty())
2384 strSnapshotFolder = "Snapshots";
2385 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2386 if (RT_FAILURE(vrc))
2387 return setErrorBoth(E_FAIL, vrc,
2388 tr("Invalid snapshot folder '%s' (%Rrc)"),
2389 strSnapshotFolder.c_str(), vrc);
2390
2391 i_setModified(IsModified_MachineData);
2392 mUserData.backup();
2393
2394 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2395
2396 return S_OK;
2397}
2398
2399HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2400{
2401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2402
2403 aMediumAttachments.resize(mMediumAttachments->size());
2404 size_t i = 0;
2405 for (MediumAttachmentList::const_iterator
2406 it = mMediumAttachments->begin();
2407 it != mMediumAttachments->end();
2408 ++it, ++i)
2409 aMediumAttachments[i] = *it;
2410
2411 return S_OK;
2412}
2413
2414HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2415{
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 Assert(!!mVRDEServer);
2419
2420 aVRDEServer = mVRDEServer;
2421
2422 return S_OK;
2423}
2424
2425HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 aAudioAdapter = mAudioAdapter;
2430
2431 return S_OK;
2432}
2433
2434HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2435{
2436#ifdef VBOX_WITH_VUSB
2437 clearError();
2438 MultiResult rc(S_OK);
2439
2440# ifdef VBOX_WITH_USB
2441 rc = mParent->i_host()->i_checkUSBProxyService();
2442 if (FAILED(rc)) return rc;
2443# endif
2444
2445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2446
2447 aUSBControllers.resize(mUSBControllers->size());
2448 size_t i = 0;
2449 for (USBControllerList::const_iterator
2450 it = mUSBControllers->begin();
2451 it != mUSBControllers->end();
2452 ++it, ++i)
2453 aUSBControllers[i] = *it;
2454
2455 return S_OK;
2456#else
2457 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2458 * extended error info to indicate that USB is simply not available
2459 * (w/o treating it as a failure), for example, as in OSE */
2460 NOREF(aUSBControllers);
2461 ReturnComNotImplemented();
2462#endif /* VBOX_WITH_VUSB */
2463}
2464
2465HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2466{
2467#ifdef VBOX_WITH_VUSB
2468 clearError();
2469 MultiResult rc(S_OK);
2470
2471# ifdef VBOX_WITH_USB
2472 rc = mParent->i_host()->i_checkUSBProxyService();
2473 if (FAILED(rc)) return rc;
2474# endif
2475
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 aUSBDeviceFilters = mUSBDeviceFilters;
2479 return rc;
2480#else
2481 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2482 * extended error info to indicate that USB is simply not available
2483 * (w/o treating it as a failure), for example, as in OSE */
2484 NOREF(aUSBDeviceFilters);
2485 ReturnComNotImplemented();
2486#endif /* VBOX_WITH_VUSB */
2487}
2488
2489HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2490{
2491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 aSettingsFilePath = mData->m_strConfigFileFull;
2494
2495 return S_OK;
2496}
2497
2498HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2499{
2500 RT_NOREF(aSettingsFilePath);
2501 ReturnComNotImplemented();
2502}
2503
2504HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2505{
2506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2509 if (FAILED(rc)) return rc;
2510
2511 if (!mData->pMachineConfigFile->fileExists())
2512 // this is a new machine, and no config file exists yet:
2513 *aSettingsModified = TRUE;
2514 else
2515 *aSettingsModified = (mData->flModifications != 0);
2516
2517 return S_OK;
2518}
2519
2520HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2521{
2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2523
2524 *aSessionState = mData->mSession.mState;
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 aSessionName = mData->mSession.mName;
2534
2535 return S_OK;
2536}
2537
2538HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2539{
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 *aSessionPID = mData->mSession.mPID;
2543
2544 return S_OK;
2545}
2546
2547HRESULT Machine::getState(MachineState_T *aState)
2548{
2549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2550
2551 *aState = mData->mMachineState;
2552 Assert(mData->mMachineState != MachineState_Null);
2553
2554 return S_OK;
2555}
2556
2557HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2558{
2559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2562
2563 return S_OK;
2564}
2565
2566HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2567{
2568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2569
2570 aStateFilePath = mSSData->strStateFilePath;
2571
2572 return S_OK;
2573}
2574
2575HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2576{
2577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 i_getLogFolder(aLogFolder);
2580
2581 return S_OK;
2582}
2583
2584HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2585{
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 aCurrentSnapshot = mData->mCurrentSnapshot;
2589
2590 return S_OK;
2591}
2592
2593HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2594{
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2598 ? 0
2599 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 /* Note: for machines with no snapshots, we always return FALSE
2609 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2610 * reasons :) */
2611
2612 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2613 ? FALSE
2614 : mData->mCurrentStateModified;
2615
2616 return S_OK;
2617}
2618
2619HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2620{
2621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 aSharedFolders.resize(mHWData->mSharedFolders.size());
2624 size_t i = 0;
2625 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2626 it = mHWData->mSharedFolders.begin();
2627 it != mHWData->mSharedFolders.end();
2628 ++it, ++i)
2629 aSharedFolders[i] = *it;
2630
2631 return S_OK;
2632}
2633
2634HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2635{
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 *aClipboardMode = mHWData->mClipboardMode;
2639
2640 return S_OK;
2641}
2642
2643HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2644{
2645 HRESULT rc = S_OK;
2646
2647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 alock.release();
2650 rc = i_onClipboardModeChange(aClipboardMode);
2651 alock.acquire();
2652 if (FAILED(rc)) return rc;
2653
2654 i_setModified(IsModified_MachineData);
2655 mHWData.backup();
2656 mHWData->mClipboardMode = aClipboardMode;
2657
2658 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2659 if (Global::IsOnline(mData->mMachineState))
2660 i_saveSettings(NULL);
2661
2662 return S_OK;
2663}
2664
2665HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2666{
2667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2668
2669 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2675{
2676 HRESULT rc = S_OK;
2677
2678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 alock.release();
2681 rc = i_onClipboardFileTransferModeChange(aEnabled);
2682 alock.acquire();
2683 if (FAILED(rc)) return rc;
2684
2685 i_setModified(IsModified_MachineData);
2686 mHWData.backup();
2687 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2688
2689 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2690 if (Global::IsOnline(mData->mMachineState))
2691 i_saveSettings(NULL);
2692
2693 return S_OK;
2694}
2695
2696HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2697{
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 *aDnDMode = mHWData->mDnDMode;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2706{
2707 HRESULT rc = S_OK;
2708
2709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2710
2711 alock.release();
2712 rc = i_onDnDModeChange(aDnDMode);
2713
2714 alock.acquire();
2715 if (FAILED(rc)) return rc;
2716
2717 i_setModified(IsModified_MachineData);
2718 mHWData.backup();
2719 mHWData->mDnDMode = aDnDMode;
2720
2721 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2722 if (Global::IsOnline(mData->mMachineState))
2723 i_saveSettings(NULL);
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aStorageControllers.resize(mStorageControllers->size());
2733 size_t i = 0;
2734 for (StorageControllerList::const_iterator
2735 it = mStorageControllers->begin();
2736 it != mStorageControllers->end();
2737 ++it, ++i)
2738 aStorageControllers[i] = *it;
2739
2740 return S_OK;
2741}
2742
2743HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2744{
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 *aEnabled = mUserData->s.fTeleporterEnabled;
2748
2749 return S_OK;
2750}
2751
2752HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2753{
2754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 /* Only allow it to be set to true when PoweredOff or Aborted.
2757 (Clearing it is always permitted.) */
2758 if ( aTeleporterEnabled
2759 && mData->mRegistered
2760 && ( !i_isSessionMachine()
2761 || ( mData->mMachineState != MachineState_PoweredOff
2762 && mData->mMachineState != MachineState_Teleported
2763 && mData->mMachineState != MachineState_Aborted
2764 )
2765 )
2766 )
2767 return setError(VBOX_E_INVALID_VM_STATE,
2768 tr("The machine is not powered off (state is %s)"),
2769 Global::stringifyMachineState(mData->mMachineState));
2770
2771 i_setModified(IsModified_MachineData);
2772 mUserData.backup();
2773 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2774
2775 return S_OK;
2776}
2777
2778HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2779{
2780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2781
2782 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2788{
2789 if (aTeleporterPort >= _64K)
2790 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2791
2792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2795 if (FAILED(rc)) return rc;
2796
2797 i_setModified(IsModified_MachineData);
2798 mUserData.backup();
2799 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2814{
2815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2818 if (FAILED(rc)) return rc;
2819
2820 i_setModified(IsModified_MachineData);
2821 mUserData.backup();
2822 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2828{
2829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2830 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2831
2832 return S_OK;
2833}
2834
2835HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2836{
2837 /*
2838 * Hash the password first.
2839 */
2840 com::Utf8Str aT = aTeleporterPassword;
2841
2842 if (!aT.isEmpty())
2843 {
2844 if (VBoxIsPasswordHashed(&aT))
2845 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2846 VBoxHashPassword(&aT);
2847 }
2848
2849 /*
2850 * Do the update.
2851 */
2852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2854 if (SUCCEEDED(hrc))
2855 {
2856 i_setModified(IsModified_MachineData);
2857 mUserData.backup();
2858 mUserData->s.strTeleporterPassword = aT;
2859 }
2860
2861 return hrc;
2862}
2863
2864HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2874{
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 /* Only allow it to be set to true when PoweredOff or Aborted.
2878 (Clearing it is always permitted.) */
2879 if ( aRTCUseUTC
2880 && mData->mRegistered
2881 && ( !i_isSessionMachine()
2882 || ( mData->mMachineState != MachineState_PoweredOff
2883 && mData->mMachineState != MachineState_Teleported
2884 && mData->mMachineState != MachineState_Aborted
2885 )
2886 )
2887 )
2888 return setError(VBOX_E_INVALID_VM_STATE,
2889 tr("The machine is not powered off (state is %s)"),
2890 Global::stringifyMachineState(mData->mMachineState));
2891
2892 i_setModified(IsModified_MachineData);
2893 mUserData.backup();
2894 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2904
2905 return S_OK;
2906}
2907
2908HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2909{
2910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 HRESULT rc = i_checkStateDependency(MutableStateDep);
2913 if (FAILED(rc)) return rc;
2914
2915 i_setModified(IsModified_MachineData);
2916 mHWData.backup();
2917 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2923{
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aIOCacheSize = mHWData->mIOCacheSize;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2932{
2933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2934
2935 HRESULT rc = i_checkStateDependency(MutableStateDep);
2936 if (FAILED(rc)) return rc;
2937
2938 i_setModified(IsModified_MachineData);
2939 mHWData.backup();
2940 mHWData->mIOCacheSize = aIOCacheSize;
2941
2942 return S_OK;
2943}
2944
2945
2946/**
2947 * @note Locks objects!
2948 */
2949HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2950 LockType_T aLockType)
2951{
2952 /* check the session state */
2953 SessionState_T state;
2954 HRESULT rc = aSession->COMGETTER(State)(&state);
2955 if (FAILED(rc)) return rc;
2956
2957 if (state != SessionState_Unlocked)
2958 return setError(VBOX_E_INVALID_OBJECT_STATE,
2959 tr("The given session is busy"));
2960
2961 // get the client's IInternalSessionControl interface
2962 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2963 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2964 E_INVALIDARG);
2965
2966 // session name (only used in some code paths)
2967 Utf8Str strSessionName;
2968
2969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 if (!mData->mRegistered)
2972 return setError(E_UNEXPECTED,
2973 tr("The machine '%s' is not registered"),
2974 mUserData->s.strName.c_str());
2975
2976 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2977
2978 SessionState_T oldState = mData->mSession.mState;
2979 /* Hack: in case the session is closing and there is a progress object
2980 * which allows waiting for the session to be closed, take the opportunity
2981 * and do a limited wait (max. 1 second). This helps a lot when the system
2982 * is busy and thus session closing can take a little while. */
2983 if ( mData->mSession.mState == SessionState_Unlocking
2984 && mData->mSession.mProgress)
2985 {
2986 alock.release();
2987 mData->mSession.mProgress->WaitForCompletion(1000);
2988 alock.acquire();
2989 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2990 }
2991
2992 // try again now
2993 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2994 // (i.e. session machine exists)
2995 && (aLockType == LockType_Shared) // caller wants a shared link to the
2996 // existing session that holds the write lock:
2997 )
2998 {
2999 // OK, share the session... we are now dealing with three processes:
3000 // 1) VBoxSVC (where this code runs);
3001 // 2) process C: the caller's client process (who wants a shared session);
3002 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3003
3004 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3005 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3006 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3007 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3008 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3009
3010 /*
3011 * Release the lock before calling the client process. It's safe here
3012 * since the only thing to do after we get the lock again is to add
3013 * the remote control to the list (which doesn't directly influence
3014 * anything).
3015 */
3016 alock.release();
3017
3018 // get the console of the session holding the write lock (this is a remote call)
3019 ComPtr<IConsole> pConsoleW;
3020 if (mData->mSession.mLockType == LockType_VM)
3021 {
3022 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3023 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3024 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3025 if (FAILED(rc))
3026 // the failure may occur w/o any error info (from RPC), so provide one
3027 return setError(VBOX_E_VM_ERROR,
3028 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3029 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3030 }
3031
3032 // share the session machine and W's console with the caller's session
3033 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3034 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3035 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3036
3037 if (FAILED(rc))
3038 // the failure may occur w/o any error info (from RPC), so provide one
3039 return setError(VBOX_E_VM_ERROR,
3040 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3041 alock.acquire();
3042
3043 // need to revalidate the state after acquiring the lock again
3044 if (mData->mSession.mState != SessionState_Locked)
3045 {
3046 pSessionControl->Uninitialize();
3047 return setError(VBOX_E_INVALID_SESSION_STATE,
3048 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3049 mUserData->s.strName.c_str());
3050 }
3051
3052 // add the caller's session to the list
3053 mData->mSession.mRemoteControls.push_back(pSessionControl);
3054 }
3055 else if ( mData->mSession.mState == SessionState_Locked
3056 || mData->mSession.mState == SessionState_Unlocking
3057 )
3058 {
3059 // sharing not permitted, or machine still unlocking:
3060 return setError(VBOX_E_INVALID_OBJECT_STATE,
3061 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3062 mUserData->s.strName.c_str());
3063 }
3064 else
3065 {
3066 // machine is not locked: then write-lock the machine (create the session machine)
3067
3068 // must not be busy
3069 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3070
3071 // get the caller's session PID
3072 RTPROCESS pid = NIL_RTPROCESS;
3073 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3074 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3075 Assert(pid != NIL_RTPROCESS);
3076
3077 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3078
3079 if (fLaunchingVMProcess)
3080 {
3081 if (mData->mSession.mPID == NIL_RTPROCESS)
3082 {
3083 // two or more clients racing for a lock, the one which set the
3084 // session state to Spawning will win, the others will get an
3085 // error as we can't decide here if waiting a little would help
3086 // (only for shared locks this would avoid an error)
3087 return setError(VBOX_E_INVALID_OBJECT_STATE,
3088 tr("The machine '%s' already has a lock request pending"),
3089 mUserData->s.strName.c_str());
3090 }
3091
3092 // this machine is awaiting for a spawning session to be opened:
3093 // then the calling process must be the one that got started by
3094 // LaunchVMProcess()
3095
3096 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3097 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3098
3099#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3100 /* Hardened windows builds spawns three processes when a VM is
3101 launched, the 3rd one is the one that will end up here. */
3102 RTPROCESS pidParent;
3103 int vrc = RTProcQueryParent(pid, &pidParent);
3104 if (RT_SUCCESS(vrc))
3105 vrc = RTProcQueryParent(pidParent, &pidParent);
3106 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3107 || vrc == VERR_ACCESS_DENIED)
3108 {
3109 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3110 mData->mSession.mPID = pid;
3111 }
3112#endif
3113
3114 if (mData->mSession.mPID != pid)
3115 return setError(E_ACCESSDENIED,
3116 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3117 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3118 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3119 }
3120
3121 // create the mutable SessionMachine from the current machine
3122 ComObjPtr<SessionMachine> sessionMachine;
3123 sessionMachine.createObject();
3124 rc = sessionMachine->init(this);
3125 AssertComRC(rc);
3126
3127 /* NOTE: doing return from this function after this point but
3128 * before the end is forbidden since it may call SessionMachine::uninit()
3129 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3130 * lock while still holding the Machine lock in alock so that a deadlock
3131 * is possible due to the wrong lock order. */
3132
3133 if (SUCCEEDED(rc))
3134 {
3135 /*
3136 * Set the session state to Spawning to protect against subsequent
3137 * attempts to open a session and to unregister the machine after
3138 * we release the lock.
3139 */
3140 SessionState_T origState = mData->mSession.mState;
3141 mData->mSession.mState = SessionState_Spawning;
3142
3143#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3144 /* Get the client token ID to be passed to the client process */
3145 Utf8Str strTokenId;
3146 sessionMachine->i_getTokenId(strTokenId);
3147 Assert(!strTokenId.isEmpty());
3148#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3149 /* Get the client token to be passed to the client process */
3150 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3151 /* The token is now "owned" by pToken, fix refcount */
3152 if (!pToken.isNull())
3153 pToken->Release();
3154#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3155
3156 /*
3157 * Release the lock before calling the client process -- it will call
3158 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3159 * because the state is Spawning, so that LaunchVMProcess() and
3160 * LockMachine() calls will fail. This method, called before we
3161 * acquire the lock again, will fail because of the wrong PID.
3162 *
3163 * Note that mData->mSession.mRemoteControls accessed outside
3164 * the lock may not be modified when state is Spawning, so it's safe.
3165 */
3166 alock.release();
3167
3168 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3169#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3170 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3171#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3172 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3173 /* Now the token is owned by the client process. */
3174 pToken.setNull();
3175#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3176 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3177
3178 /* The failure may occur w/o any error info (from RPC), so provide one */
3179 if (FAILED(rc))
3180 setError(VBOX_E_VM_ERROR,
3181 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3182
3183 // get session name, either to remember or to compare against
3184 // the already known session name.
3185 {
3186 Bstr bstrSessionName;
3187 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3188 if (SUCCEEDED(rc2))
3189 strSessionName = bstrSessionName;
3190 }
3191
3192 if ( SUCCEEDED(rc)
3193 && fLaunchingVMProcess
3194 )
3195 {
3196 /* complete the remote session initialization */
3197
3198 /* get the console from the direct session */
3199 ComPtr<IConsole> console;
3200 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3201 ComAssertComRC(rc);
3202
3203 if (SUCCEEDED(rc) && !console)
3204 {
3205 ComAssert(!!console);
3206 rc = E_FAIL;
3207 }
3208
3209 /* assign machine & console to the remote session */
3210 if (SUCCEEDED(rc))
3211 {
3212 /*
3213 * after LaunchVMProcess(), the first and the only
3214 * entry in remoteControls is that remote session
3215 */
3216 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3217 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3218 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3219
3220 /* The failure may occur w/o any error info (from RPC), so provide one */
3221 if (FAILED(rc))
3222 setError(VBOX_E_VM_ERROR,
3223 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3224 }
3225
3226 if (FAILED(rc))
3227 pSessionControl->Uninitialize();
3228 }
3229
3230 /* acquire the lock again */
3231 alock.acquire();
3232
3233 /* Restore the session state */
3234 mData->mSession.mState = origState;
3235 }
3236
3237 // finalize spawning anyway (this is why we don't return on errors above)
3238 if (fLaunchingVMProcess)
3239 {
3240 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3241 /* Note that the progress object is finalized later */
3242 /** @todo Consider checking mData->mSession.mProgress for cancellation
3243 * around here. */
3244
3245 /* We don't reset mSession.mPID here because it is necessary for
3246 * SessionMachine::uninit() to reap the child process later. */
3247
3248 if (FAILED(rc))
3249 {
3250 /* Close the remote session, remove the remote control from the list
3251 * and reset session state to Closed (@note keep the code in sync
3252 * with the relevant part in checkForSpawnFailure()). */
3253
3254 Assert(mData->mSession.mRemoteControls.size() == 1);
3255 if (mData->mSession.mRemoteControls.size() == 1)
3256 {
3257 ErrorInfoKeeper eik;
3258 mData->mSession.mRemoteControls.front()->Uninitialize();
3259 }
3260
3261 mData->mSession.mRemoteControls.clear();
3262 mData->mSession.mState = SessionState_Unlocked;
3263 }
3264 }
3265 else
3266 {
3267 /* memorize PID of the directly opened session */
3268 if (SUCCEEDED(rc))
3269 mData->mSession.mPID = pid;
3270 }
3271
3272 if (SUCCEEDED(rc))
3273 {
3274 mData->mSession.mLockType = aLockType;
3275 /* memorize the direct session control and cache IUnknown for it */
3276 mData->mSession.mDirectControl = pSessionControl;
3277 mData->mSession.mState = SessionState_Locked;
3278 if (!fLaunchingVMProcess)
3279 mData->mSession.mName = strSessionName;
3280 /* associate the SessionMachine with this Machine */
3281 mData->mSession.mMachine = sessionMachine;
3282
3283 /* request an IUnknown pointer early from the remote party for later
3284 * identity checks (it will be internally cached within mDirectControl
3285 * at least on XPCOM) */
3286 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3287 NOREF(unk);
3288 }
3289
3290 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3291 * would break the lock order */
3292 alock.release();
3293
3294 /* uninitialize the created session machine on failure */
3295 if (FAILED(rc))
3296 sessionMachine->uninit();
3297 }
3298
3299 if (SUCCEEDED(rc))
3300 {
3301 /*
3302 * tell the client watcher thread to update the set of
3303 * machines that have open sessions
3304 */
3305 mParent->i_updateClientWatcher();
3306
3307 if (oldState != SessionState_Locked)
3308 /* fire an event */
3309 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3310 }
3311
3312 return rc;
3313}
3314
3315/**
3316 * @note Locks objects!
3317 */
3318HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3319 const com::Utf8Str &aName,
3320 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3321 ComPtr<IProgress> &aProgress)
3322{
3323 Utf8Str strFrontend(aName);
3324 /* "emergencystop" doesn't need the session, so skip the checks/interface
3325 * retrieval. This code doesn't quite fit in here, but introducing a
3326 * special API method would be even more effort, and would require explicit
3327 * support by every API client. It's better to hide the feature a bit. */
3328 if (strFrontend != "emergencystop")
3329 CheckComArgNotNull(aSession);
3330
3331 HRESULT rc = S_OK;
3332 if (strFrontend.isEmpty())
3333 {
3334 Bstr bstrFrontend;
3335 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3336 if (FAILED(rc))
3337 return rc;
3338 strFrontend = bstrFrontend;
3339 if (strFrontend.isEmpty())
3340 {
3341 ComPtr<ISystemProperties> systemProperties;
3342 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3343 if (FAILED(rc))
3344 return rc;
3345 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3346 if (FAILED(rc))
3347 return rc;
3348 strFrontend = bstrFrontend;
3349 }
3350 /* paranoia - emergencystop is not a valid default */
3351 if (strFrontend == "emergencystop")
3352 strFrontend = Utf8Str::Empty;
3353 }
3354 /* default frontend: Qt GUI */
3355 if (strFrontend.isEmpty())
3356 strFrontend = "GUI/Qt";
3357
3358 if (strFrontend != "emergencystop")
3359 {
3360 /* check the session state */
3361 SessionState_T state;
3362 rc = aSession->COMGETTER(State)(&state);
3363 if (FAILED(rc))
3364 return rc;
3365
3366 if (state != SessionState_Unlocked)
3367 return setError(VBOX_E_INVALID_OBJECT_STATE,
3368 tr("The given session is busy"));
3369
3370 /* get the IInternalSessionControl interface */
3371 ComPtr<IInternalSessionControl> control(aSession);
3372 ComAssertMsgRet(!control.isNull(),
3373 ("No IInternalSessionControl interface"),
3374 E_INVALIDARG);
3375
3376 /* get the teleporter enable state for the progress object init. */
3377 BOOL fTeleporterEnabled;
3378 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3379 if (FAILED(rc))
3380 return rc;
3381
3382 /* create a progress object */
3383 ComObjPtr<ProgressProxy> progress;
3384 progress.createObject();
3385 rc = progress->init(mParent,
3386 static_cast<IMachine*>(this),
3387 Bstr(tr("Starting VM")).raw(),
3388 TRUE /* aCancelable */,
3389 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3390 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3391 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3392 2 /* uFirstOperationWeight */,
3393 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3394
3395 if (SUCCEEDED(rc))
3396 {
3397 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3398 if (SUCCEEDED(rc))
3399 {
3400 aProgress = progress;
3401
3402 /* signal the client watcher thread */
3403 mParent->i_updateClientWatcher();
3404
3405 /* fire an event */
3406 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3407 }
3408 }
3409 }
3410 else
3411 {
3412 /* no progress object - either instant success or failure */
3413 aProgress = NULL;
3414
3415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3416
3417 if (mData->mSession.mState != SessionState_Locked)
3418 return setError(VBOX_E_INVALID_OBJECT_STATE,
3419 tr("The machine '%s' is not locked by a session"),
3420 mUserData->s.strName.c_str());
3421
3422 /* must have a VM process associated - do not kill normal API clients
3423 * with an open session */
3424 if (!Global::IsOnline(mData->mMachineState))
3425 return setError(VBOX_E_INVALID_OBJECT_STATE,
3426 tr("The machine '%s' does not have a VM process"),
3427 mUserData->s.strName.c_str());
3428
3429 /* forcibly terminate the VM process */
3430 if (mData->mSession.mPID != NIL_RTPROCESS)
3431 RTProcTerminate(mData->mSession.mPID);
3432
3433 /* signal the client watcher thread, as most likely the client has
3434 * been terminated */
3435 mParent->i_updateClientWatcher();
3436 }
3437
3438 return rc;
3439}
3440
3441HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3442{
3443 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3444 return setError(E_INVALIDARG,
3445 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3446 aPosition, SchemaDefs::MaxBootPosition);
3447
3448 if (aDevice == DeviceType_USB)
3449 return setError(E_NOTIMPL,
3450 tr("Booting from USB device is currently not supported"));
3451
3452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3453
3454 HRESULT rc = i_checkStateDependency(MutableStateDep);
3455 if (FAILED(rc)) return rc;
3456
3457 i_setModified(IsModified_MachineData);
3458 mHWData.backup();
3459 mHWData->mBootOrder[aPosition - 1] = aDevice;
3460
3461 return S_OK;
3462}
3463
3464HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3465{
3466 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3467 return setError(E_INVALIDARG,
3468 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3469 aPosition, SchemaDefs::MaxBootPosition);
3470
3471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 *aDevice = mHWData->mBootOrder[aPosition - 1];
3474
3475 return S_OK;
3476}
3477
3478HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3479 LONG aControllerPort,
3480 LONG aDevice,
3481 DeviceType_T aType,
3482 const ComPtr<IMedium> &aMedium)
3483{
3484 IMedium *aM = aMedium;
3485 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3486 aName.c_str(), aControllerPort, aDevice, aType, aM));
3487
3488 // request the host lock first, since might be calling Host methods for getting host drives;
3489 // next, protect the media tree all the while we're in here, as well as our member variables
3490 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3491 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3492
3493 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3494 if (FAILED(rc)) return rc;
3495
3496 /// @todo NEWMEDIA implicit machine registration
3497 if (!mData->mRegistered)
3498 return setError(VBOX_E_INVALID_OBJECT_STATE,
3499 tr("Cannot attach storage devices to an unregistered machine"));
3500
3501 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3502
3503 /* Check for an existing controller. */
3504 ComObjPtr<StorageController> ctl;
3505 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3506 if (FAILED(rc)) return rc;
3507
3508 StorageControllerType_T ctrlType;
3509 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3510 if (FAILED(rc))
3511 return setError(E_FAIL,
3512 tr("Could not get type of controller '%s'"),
3513 aName.c_str());
3514
3515 bool fSilent = false;
3516 Utf8Str strReconfig;
3517
3518 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3519 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3520 if ( mData->mMachineState == MachineState_Paused
3521 && strReconfig == "1")
3522 fSilent = true;
3523
3524 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3525 bool fHotplug = false;
3526 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3527 fHotplug = true;
3528
3529 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3530 return setError(VBOX_E_INVALID_VM_STATE,
3531 tr("Controller '%s' does not support hotplugging"),
3532 aName.c_str());
3533
3534 // check that the port and device are not out of range
3535 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3536 if (FAILED(rc)) return rc;
3537
3538 /* check if the device slot is already busy */
3539 MediumAttachment *pAttachTemp;
3540 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3541 aName,
3542 aControllerPort,
3543 aDevice)))
3544 {
3545 Medium *pMedium = pAttachTemp->i_getMedium();
3546 if (pMedium)
3547 {
3548 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3549 return setError(VBOX_E_OBJECT_IN_USE,
3550 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3551 pMedium->i_getLocationFull().c_str(),
3552 aControllerPort,
3553 aDevice,
3554 aName.c_str());
3555 }
3556 else
3557 return setError(VBOX_E_OBJECT_IN_USE,
3558 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3559 aControllerPort, aDevice, aName.c_str());
3560 }
3561
3562 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3563 if (aMedium && medium.isNull())
3564 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3565
3566 AutoCaller mediumCaller(medium);
3567 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3568
3569 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3570
3571 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3572 && !medium.isNull()
3573 && ( medium->i_getType() != MediumType_Readonly
3574 || medium->i_getDeviceType() != DeviceType_DVD)
3575 )
3576 return setError(VBOX_E_OBJECT_IN_USE,
3577 tr("Medium '%s' is already attached to this virtual machine"),
3578 medium->i_getLocationFull().c_str());
3579
3580 if (!medium.isNull())
3581 {
3582 MediumType_T mtype = medium->i_getType();
3583 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3584 // For DVDs it's not written to the config file, so needs no global config
3585 // version bump. For floppies it's a new attribute "type", which is ignored
3586 // by older VirtualBox version, so needs no global config version bump either.
3587 // For hard disks this type is not accepted.
3588 if (mtype == MediumType_MultiAttach)
3589 {
3590 // This type is new with VirtualBox 4.0 and therefore requires settings
3591 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3592 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3593 // two reasons: The medium type is a property of the media registry tree, which
3594 // can reside in the global config file (for pre-4.0 media); we would therefore
3595 // possibly need to bump the global config version. We don't want to do that though
3596 // because that might make downgrading to pre-4.0 impossible.
3597 // As a result, we can only use these two new types if the medium is NOT in the
3598 // global registry:
3599 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3600 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3601 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3602 )
3603 return setError(VBOX_E_INVALID_OBJECT_STATE,
3604 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3605 "to machines that were created with VirtualBox 4.0 or later"),
3606 medium->i_getLocationFull().c_str());
3607 }
3608 }
3609
3610 bool fIndirect = false;
3611 if (!medium.isNull())
3612 fIndirect = medium->i_isReadOnly();
3613 bool associate = true;
3614
3615 do
3616 {
3617 if ( aType == DeviceType_HardDisk
3618 && mMediumAttachments.isBackedUp())
3619 {
3620 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3621
3622 /* check if the medium was attached to the VM before we started
3623 * changing attachments in which case the attachment just needs to
3624 * be restored */
3625 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3626 {
3627 AssertReturn(!fIndirect, E_FAIL);
3628
3629 /* see if it's the same bus/channel/device */
3630 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3631 {
3632 /* the simplest case: restore the whole attachment
3633 * and return, nothing else to do */
3634 mMediumAttachments->push_back(pAttachTemp);
3635
3636 /* Reattach the medium to the VM. */
3637 if (fHotplug || fSilent)
3638 {
3639 mediumLock.release();
3640 treeLock.release();
3641 alock.release();
3642
3643 MediumLockList *pMediumLockList(new MediumLockList());
3644
3645 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3646 medium /* pToLockWrite */,
3647 false /* fMediumLockWriteAll */,
3648 NULL,
3649 *pMediumLockList);
3650 alock.acquire();
3651 if (FAILED(rc))
3652 delete pMediumLockList;
3653 else
3654 {
3655 mData->mSession.mLockedMedia.Unlock();
3656 alock.release();
3657 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3658 mData->mSession.mLockedMedia.Lock();
3659 alock.acquire();
3660 }
3661 alock.release();
3662
3663 if (SUCCEEDED(rc))
3664 {
3665 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3666 /* Remove lock list in case of error. */
3667 if (FAILED(rc))
3668 {
3669 mData->mSession.mLockedMedia.Unlock();
3670 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3671 mData->mSession.mLockedMedia.Lock();
3672 }
3673 }
3674 }
3675
3676 return S_OK;
3677 }
3678
3679 /* bus/channel/device differ; we need a new attachment object,
3680 * but don't try to associate it again */
3681 associate = false;
3682 break;
3683 }
3684 }
3685
3686 /* go further only if the attachment is to be indirect */
3687 if (!fIndirect)
3688 break;
3689
3690 /* perform the so called smart attachment logic for indirect
3691 * attachments. Note that smart attachment is only applicable to base
3692 * hard disks. */
3693
3694 if (medium->i_getParent().isNull())
3695 {
3696 /* first, investigate the backup copy of the current hard disk
3697 * attachments to make it possible to re-attach existing diffs to
3698 * another device slot w/o losing their contents */
3699 if (mMediumAttachments.isBackedUp())
3700 {
3701 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3702
3703 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3704 uint32_t foundLevel = 0;
3705
3706 for (MediumAttachmentList::const_iterator
3707 it = oldAtts.begin();
3708 it != oldAtts.end();
3709 ++it)
3710 {
3711 uint32_t level = 0;
3712 MediumAttachment *pAttach = *it;
3713 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3714 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3715 if (pMedium.isNull())
3716 continue;
3717
3718 if (pMedium->i_getBase(&level) == medium)
3719 {
3720 /* skip the hard disk if its currently attached (we
3721 * cannot attach the same hard disk twice) */
3722 if (i_findAttachment(*mMediumAttachments.data(),
3723 pMedium))
3724 continue;
3725
3726 /* matched device, channel and bus (i.e. attached to the
3727 * same place) will win and immediately stop the search;
3728 * otherwise the attachment that has the youngest
3729 * descendant of medium will be used
3730 */
3731 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3732 {
3733 /* the simplest case: restore the whole attachment
3734 * and return, nothing else to do */
3735 mMediumAttachments->push_back(*it);
3736
3737 /* Reattach the medium to the VM. */
3738 if (fHotplug || fSilent)
3739 {
3740 mediumLock.release();
3741 treeLock.release();
3742 alock.release();
3743
3744 MediumLockList *pMediumLockList(new MediumLockList());
3745
3746 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3747 medium /* pToLockWrite */,
3748 false /* fMediumLockWriteAll */,
3749 NULL,
3750 *pMediumLockList);
3751 alock.acquire();
3752 if (FAILED(rc))
3753 delete pMediumLockList;
3754 else
3755 {
3756 mData->mSession.mLockedMedia.Unlock();
3757 alock.release();
3758 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3759 mData->mSession.mLockedMedia.Lock();
3760 alock.acquire();
3761 }
3762 alock.release();
3763
3764 if (SUCCEEDED(rc))
3765 {
3766 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3767 /* Remove lock list in case of error. */
3768 if (FAILED(rc))
3769 {
3770 mData->mSession.mLockedMedia.Unlock();
3771 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3772 mData->mSession.mLockedMedia.Lock();
3773 }
3774 }
3775 }
3776
3777 return S_OK;
3778 }
3779 else if ( foundIt == oldAtts.end()
3780 || level > foundLevel /* prefer younger */
3781 )
3782 {
3783 foundIt = it;
3784 foundLevel = level;
3785 }
3786 }
3787 }
3788
3789 if (foundIt != oldAtts.end())
3790 {
3791 /* use the previously attached hard disk */
3792 medium = (*foundIt)->i_getMedium();
3793 mediumCaller.attach(medium);
3794 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3795 mediumLock.attach(medium);
3796 /* not implicit, doesn't require association with this VM */
3797 fIndirect = false;
3798 associate = false;
3799 /* go right to the MediumAttachment creation */
3800 break;
3801 }
3802 }
3803
3804 /* must give up the medium lock and medium tree lock as below we
3805 * go over snapshots, which needs a lock with higher lock order. */
3806 mediumLock.release();
3807 treeLock.release();
3808
3809 /* then, search through snapshots for the best diff in the given
3810 * hard disk's chain to base the new diff on */
3811
3812 ComObjPtr<Medium> base;
3813 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3814 while (snap)
3815 {
3816 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3817
3818 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3819
3820 MediumAttachment *pAttachFound = NULL;
3821 uint32_t foundLevel = 0;
3822
3823 for (MediumAttachmentList::const_iterator
3824 it = snapAtts.begin();
3825 it != snapAtts.end();
3826 ++it)
3827 {
3828 MediumAttachment *pAttach = *it;
3829 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3830 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3831 if (pMedium.isNull())
3832 continue;
3833
3834 uint32_t level = 0;
3835 if (pMedium->i_getBase(&level) == medium)
3836 {
3837 /* matched device, channel and bus (i.e. attached to the
3838 * same place) will win and immediately stop the search;
3839 * otherwise the attachment that has the youngest
3840 * descendant of medium will be used
3841 */
3842 if ( pAttach->i_getDevice() == aDevice
3843 && pAttach->i_getPort() == aControllerPort
3844 && pAttach->i_getControllerName() == aName
3845 )
3846 {
3847 pAttachFound = pAttach;
3848 break;
3849 }
3850 else if ( !pAttachFound
3851 || level > foundLevel /* prefer younger */
3852 )
3853 {
3854 pAttachFound = pAttach;
3855 foundLevel = level;
3856 }
3857 }
3858 }
3859
3860 if (pAttachFound)
3861 {
3862 base = pAttachFound->i_getMedium();
3863 break;
3864 }
3865
3866 snap = snap->i_getParent();
3867 }
3868
3869 /* re-lock medium tree and the medium, as we need it below */
3870 treeLock.acquire();
3871 mediumLock.acquire();
3872
3873 /* found a suitable diff, use it as a base */
3874 if (!base.isNull())
3875 {
3876 medium = base;
3877 mediumCaller.attach(medium);
3878 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3879 mediumLock.attach(medium);
3880 }
3881 }
3882
3883 Utf8Str strFullSnapshotFolder;
3884 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3885
3886 ComObjPtr<Medium> diff;
3887 diff.createObject();
3888 // store this diff in the same registry as the parent
3889 Guid uuidRegistryParent;
3890 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3891 {
3892 // parent image has no registry: this can happen if we're attaching a new immutable
3893 // image that has not yet been attached (medium then points to the base and we're
3894 // creating the diff image for the immutable, and the parent is not yet registered);
3895 // put the parent in the machine registry then
3896 mediumLock.release();
3897 treeLock.release();
3898 alock.release();
3899 i_addMediumToRegistry(medium);
3900 alock.acquire();
3901 treeLock.acquire();
3902 mediumLock.acquire();
3903 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3904 }
3905 rc = diff->init(mParent,
3906 medium->i_getPreferredDiffFormat(),
3907 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3908 uuidRegistryParent,
3909 DeviceType_HardDisk);
3910 if (FAILED(rc)) return rc;
3911
3912 /* Apply the normal locking logic to the entire chain. */
3913 MediumLockList *pMediumLockList(new MediumLockList());
3914 mediumLock.release();
3915 treeLock.release();
3916 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3917 diff /* pToLockWrite */,
3918 false /* fMediumLockWriteAll */,
3919 medium,
3920 *pMediumLockList);
3921 treeLock.acquire();
3922 mediumLock.acquire();
3923 if (SUCCEEDED(rc))
3924 {
3925 mediumLock.release();
3926 treeLock.release();
3927 rc = pMediumLockList->Lock();
3928 treeLock.acquire();
3929 mediumLock.acquire();
3930 if (FAILED(rc))
3931 setError(rc,
3932 tr("Could not lock medium when creating diff '%s'"),
3933 diff->i_getLocationFull().c_str());
3934 else
3935 {
3936 /* will release the lock before the potentially lengthy
3937 * operation, so protect with the special state */
3938 MachineState_T oldState = mData->mMachineState;
3939 i_setMachineState(MachineState_SettingUp);
3940
3941 mediumLock.release();
3942 treeLock.release();
3943 alock.release();
3944
3945 rc = medium->i_createDiffStorage(diff,
3946 medium->i_getPreferredDiffVariant(),
3947 pMediumLockList,
3948 NULL /* aProgress */,
3949 true /* aWait */,
3950 false /* aNotify */);
3951
3952 alock.acquire();
3953 treeLock.acquire();
3954 mediumLock.acquire();
3955
3956 i_setMachineState(oldState);
3957 }
3958 }
3959
3960 /* Unlock the media and free the associated memory. */
3961 delete pMediumLockList;
3962
3963 if (FAILED(rc)) return rc;
3964
3965 /* use the created diff for the actual attachment */
3966 medium = diff;
3967 mediumCaller.attach(medium);
3968 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3969 mediumLock.attach(medium);
3970 }
3971 while (0);
3972
3973 ComObjPtr<MediumAttachment> attachment;
3974 attachment.createObject();
3975 rc = attachment->init(this,
3976 medium,
3977 aName,
3978 aControllerPort,
3979 aDevice,
3980 aType,
3981 fIndirect,
3982 false /* fPassthrough */,
3983 false /* fTempEject */,
3984 false /* fNonRotational */,
3985 false /* fDiscard */,
3986 fHotplug /* fHotPluggable */,
3987 Utf8Str::Empty);
3988 if (FAILED(rc)) return rc;
3989
3990 if (associate && !medium.isNull())
3991 {
3992 // as the last step, associate the medium to the VM
3993 rc = medium->i_addBackReference(mData->mUuid);
3994 // here we can fail because of Deleting, or being in process of creating a Diff
3995 if (FAILED(rc)) return rc;
3996
3997 mediumLock.release();
3998 treeLock.release();
3999 alock.release();
4000 i_addMediumToRegistry(medium);
4001 alock.acquire();
4002 treeLock.acquire();
4003 mediumLock.acquire();
4004 }
4005
4006 /* success: finally remember the attachment */
4007 i_setModified(IsModified_Storage);
4008 mMediumAttachments.backup();
4009 mMediumAttachments->push_back(attachment);
4010
4011 mediumLock.release();
4012 treeLock.release();
4013 alock.release();
4014
4015 if (fHotplug || fSilent)
4016 {
4017 if (!medium.isNull())
4018 {
4019 MediumLockList *pMediumLockList(new MediumLockList());
4020
4021 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4022 medium /* pToLockWrite */,
4023 false /* fMediumLockWriteAll */,
4024 NULL,
4025 *pMediumLockList);
4026 alock.acquire();
4027 if (FAILED(rc))
4028 delete pMediumLockList;
4029 else
4030 {
4031 mData->mSession.mLockedMedia.Unlock();
4032 alock.release();
4033 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4034 mData->mSession.mLockedMedia.Lock();
4035 alock.acquire();
4036 }
4037 alock.release();
4038 }
4039
4040 if (SUCCEEDED(rc))
4041 {
4042 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4043 /* Remove lock list in case of error. */
4044 if (FAILED(rc))
4045 {
4046 mData->mSession.mLockedMedia.Unlock();
4047 mData->mSession.mLockedMedia.Remove(attachment);
4048 mData->mSession.mLockedMedia.Lock();
4049 }
4050 }
4051 }
4052
4053 /* Save modified registries, but skip this machine as it's the caller's
4054 * job to save its settings like all other settings changes. */
4055 mParent->i_unmarkRegistryModified(i_getId());
4056 mParent->i_saveModifiedRegistries();
4057
4058 if (SUCCEEDED(rc))
4059 {
4060 if (fIndirect && medium != aM)
4061 mParent->i_onMediumConfigChanged(medium);
4062 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4063 }
4064
4065 return rc;
4066}
4067
4068HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4069 LONG aDevice)
4070{
4071 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4072 aName.c_str(), aControllerPort, aDevice));
4073
4074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4075
4076 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4077 if (FAILED(rc)) return rc;
4078
4079 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4080
4081 /* Check for an existing controller. */
4082 ComObjPtr<StorageController> ctl;
4083 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4084 if (FAILED(rc)) return rc;
4085
4086 StorageControllerType_T ctrlType;
4087 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4088 if (FAILED(rc))
4089 return setError(E_FAIL,
4090 tr("Could not get type of controller '%s'"),
4091 aName.c_str());
4092
4093 bool fSilent = false;
4094 Utf8Str strReconfig;
4095
4096 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4097 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4098 if ( mData->mMachineState == MachineState_Paused
4099 && strReconfig == "1")
4100 fSilent = true;
4101
4102 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4103 bool fHotplug = false;
4104 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4105 fHotplug = true;
4106
4107 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4108 return setError(VBOX_E_INVALID_VM_STATE,
4109 tr("Controller '%s' does not support hotplugging"),
4110 aName.c_str());
4111
4112 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4113 aName,
4114 aControllerPort,
4115 aDevice);
4116 if (!pAttach)
4117 return setError(VBOX_E_OBJECT_NOT_FOUND,
4118 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4119 aDevice, aControllerPort, aName.c_str());
4120
4121 if (fHotplug && !pAttach->i_getHotPluggable())
4122 return setError(VBOX_E_NOT_SUPPORTED,
4123 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4124 aDevice, aControllerPort, aName.c_str());
4125
4126 /*
4127 * The VM has to detach the device before we delete any implicit diffs.
4128 * If this fails we can roll back without loosing data.
4129 */
4130 if (fHotplug || fSilent)
4131 {
4132 alock.release();
4133 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4134 alock.acquire();
4135 }
4136 if (FAILED(rc)) return rc;
4137
4138 /* If we are here everything went well and we can delete the implicit now. */
4139 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4140
4141 alock.release();
4142
4143 /* Save modified registries, but skip this machine as it's the caller's
4144 * job to save its settings like all other settings changes. */
4145 mParent->i_unmarkRegistryModified(i_getId());
4146 mParent->i_saveModifiedRegistries();
4147
4148 if (SUCCEEDED(rc))
4149 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4150
4151 return rc;
4152}
4153
4154HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4155 LONG aDevice, BOOL aPassthrough)
4156{
4157 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4158 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4159
4160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4161
4162 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4163 if (FAILED(rc)) return rc;
4164
4165 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4166
4167 /* Check for an existing controller. */
4168 ComObjPtr<StorageController> ctl;
4169 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4170 if (FAILED(rc)) return rc;
4171
4172 StorageControllerType_T ctrlType;
4173 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4174 if (FAILED(rc))
4175 return setError(E_FAIL,
4176 tr("Could not get type of controller '%s'"),
4177 aName.c_str());
4178
4179 bool fSilent = false;
4180 Utf8Str strReconfig;
4181
4182 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4183 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4184 if ( mData->mMachineState == MachineState_Paused
4185 && strReconfig == "1")
4186 fSilent = true;
4187
4188 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4189 bool fHotplug = false;
4190 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4191 fHotplug = true;
4192
4193 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4194 return setError(VBOX_E_INVALID_VM_STATE,
4195 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4196 aName.c_str());
4197
4198 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4199 aName,
4200 aControllerPort,
4201 aDevice);
4202 if (!pAttach)
4203 return setError(VBOX_E_OBJECT_NOT_FOUND,
4204 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4205 aDevice, aControllerPort, aName.c_str());
4206
4207
4208 i_setModified(IsModified_Storage);
4209 mMediumAttachments.backup();
4210
4211 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4212
4213 if (pAttach->i_getType() != DeviceType_DVD)
4214 return setError(E_INVALIDARG,
4215 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4216 aDevice, aControllerPort, aName.c_str());
4217
4218 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4219
4220 pAttach->i_updatePassthrough(!!aPassthrough);
4221
4222 attLock.release();
4223 alock.release();
4224 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4225 if (SUCCEEDED(rc) && fValueChanged)
4226 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4227
4228 return rc;
4229}
4230
4231HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4232 LONG aDevice, BOOL aTemporaryEject)
4233{
4234
4235 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4236 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4237
4238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4239
4240 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4241 if (FAILED(rc)) return rc;
4242
4243 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4244 aName,
4245 aControllerPort,
4246 aDevice);
4247 if (!pAttach)
4248 return setError(VBOX_E_OBJECT_NOT_FOUND,
4249 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4250 aDevice, aControllerPort, aName.c_str());
4251
4252
4253 i_setModified(IsModified_Storage);
4254 mMediumAttachments.backup();
4255
4256 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4257
4258 if (pAttach->i_getType() != DeviceType_DVD)
4259 return setError(E_INVALIDARG,
4260 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4261 aDevice, aControllerPort, aName.c_str());
4262 pAttach->i_updateTempEject(!!aTemporaryEject);
4263
4264 return S_OK;
4265}
4266
4267HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4268 LONG aDevice, BOOL aNonRotational)
4269{
4270
4271 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4272 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4273
4274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4275
4276 HRESULT rc = i_checkStateDependency(MutableStateDep);
4277 if (FAILED(rc)) return rc;
4278
4279 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4280
4281 if (Global::IsOnlineOrTransient(mData->mMachineState))
4282 return setError(VBOX_E_INVALID_VM_STATE,
4283 tr("Invalid machine state: %s"),
4284 Global::stringifyMachineState(mData->mMachineState));
4285
4286 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4287 aName,
4288 aControllerPort,
4289 aDevice);
4290 if (!pAttach)
4291 return setError(VBOX_E_OBJECT_NOT_FOUND,
4292 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4293 aDevice, aControllerPort, aName.c_str());
4294
4295
4296 i_setModified(IsModified_Storage);
4297 mMediumAttachments.backup();
4298
4299 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4300
4301 if (pAttach->i_getType() != DeviceType_HardDisk)
4302 return setError(E_INVALIDARG,
4303 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"),
4304 aDevice, aControllerPort, aName.c_str());
4305 pAttach->i_updateNonRotational(!!aNonRotational);
4306
4307 return S_OK;
4308}
4309
4310HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4311 LONG aDevice, BOOL aDiscard)
4312{
4313
4314 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4315 aName.c_str(), aControllerPort, aDevice, aDiscard));
4316
4317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4318
4319 HRESULT rc = i_checkStateDependency(MutableStateDep);
4320 if (FAILED(rc)) return rc;
4321
4322 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4323
4324 if (Global::IsOnlineOrTransient(mData->mMachineState))
4325 return setError(VBOX_E_INVALID_VM_STATE,
4326 tr("Invalid machine state: %s"),
4327 Global::stringifyMachineState(mData->mMachineState));
4328
4329 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4330 aName,
4331 aControllerPort,
4332 aDevice);
4333 if (!pAttach)
4334 return setError(VBOX_E_OBJECT_NOT_FOUND,
4335 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4336 aDevice, aControllerPort, aName.c_str());
4337
4338
4339 i_setModified(IsModified_Storage);
4340 mMediumAttachments.backup();
4341
4342 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4343
4344 if (pAttach->i_getType() != DeviceType_HardDisk)
4345 return setError(E_INVALIDARG,
4346 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"),
4347 aDevice, aControllerPort, aName.c_str());
4348 pAttach->i_updateDiscard(!!aDiscard);
4349
4350 return S_OK;
4351}
4352
4353HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4354 LONG aDevice, BOOL aHotPluggable)
4355{
4356 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4357 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4358
4359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4360
4361 HRESULT rc = i_checkStateDependency(MutableStateDep);
4362 if (FAILED(rc)) return rc;
4363
4364 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4365
4366 if (Global::IsOnlineOrTransient(mData->mMachineState))
4367 return setError(VBOX_E_INVALID_VM_STATE,
4368 tr("Invalid machine state: %s"),
4369 Global::stringifyMachineState(mData->mMachineState));
4370
4371 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4372 aName,
4373 aControllerPort,
4374 aDevice);
4375 if (!pAttach)
4376 return setError(VBOX_E_OBJECT_NOT_FOUND,
4377 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4378 aDevice, aControllerPort, aName.c_str());
4379
4380 /* Check for an existing controller. */
4381 ComObjPtr<StorageController> ctl;
4382 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4383 if (FAILED(rc)) return rc;
4384
4385 StorageControllerType_T ctrlType;
4386 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4387 if (FAILED(rc))
4388 return setError(E_FAIL,
4389 tr("Could not get type of controller '%s'"),
4390 aName.c_str());
4391
4392 if (!i_isControllerHotplugCapable(ctrlType))
4393 return setError(VBOX_E_NOT_SUPPORTED,
4394 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4395 aName.c_str());
4396
4397 i_setModified(IsModified_Storage);
4398 mMediumAttachments.backup();
4399
4400 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4401
4402 if (pAttach->i_getType() == DeviceType_Floppy)
4403 return setError(E_INVALIDARG,
4404 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"),
4405 aDevice, aControllerPort, aName.c_str());
4406 pAttach->i_updateHotPluggable(!!aHotPluggable);
4407
4408 return S_OK;
4409}
4410
4411HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4412 LONG aDevice)
4413{
4414 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4415 aName.c_str(), aControllerPort, aDevice));
4416
4417 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4418}
4419
4420HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4421 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4422{
4423 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4424 aName.c_str(), aControllerPort, aDevice));
4425
4426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4427
4428 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4429 if (FAILED(rc)) return rc;
4430
4431 if (Global::IsOnlineOrTransient(mData->mMachineState))
4432 return setError(VBOX_E_INVALID_VM_STATE,
4433 tr("Invalid machine state: %s"),
4434 Global::stringifyMachineState(mData->mMachineState));
4435
4436 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4437 aName,
4438 aControllerPort,
4439 aDevice);
4440 if (!pAttach)
4441 return setError(VBOX_E_OBJECT_NOT_FOUND,
4442 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4443 aDevice, aControllerPort, aName.c_str());
4444
4445
4446 i_setModified(IsModified_Storage);
4447 mMediumAttachments.backup();
4448
4449 IBandwidthGroup *iB = aBandwidthGroup;
4450 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4451 if (aBandwidthGroup && group.isNull())
4452 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4453
4454 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4455
4456 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4457 if (strBandwidthGroupOld.isNotEmpty())
4458 {
4459 /* Get the bandwidth group object and release it - this must not fail. */
4460 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4461 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4462 Assert(SUCCEEDED(rc));
4463
4464 pBandwidthGroupOld->i_release();
4465 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4466 }
4467
4468 if (!group.isNull())
4469 {
4470 group->i_reference();
4471 pAttach->i_updateBandwidthGroup(group->i_getName());
4472 }
4473
4474 return S_OK;
4475}
4476
4477HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4478 LONG aControllerPort,
4479 LONG aDevice,
4480 DeviceType_T aType)
4481{
4482 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4483 aName.c_str(), aControllerPort, aDevice, aType));
4484
4485 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4486}
4487
4488
4489HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4490 LONG aControllerPort,
4491 LONG aDevice,
4492 BOOL aForce)
4493{
4494 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4495 aName.c_str(), aControllerPort, aForce));
4496
4497 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4498}
4499
4500HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4501 LONG aControllerPort,
4502 LONG aDevice,
4503 const ComPtr<IMedium> &aMedium,
4504 BOOL aForce)
4505{
4506 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4507 aName.c_str(), aControllerPort, aDevice, aForce));
4508
4509 // request the host lock first, since might be calling Host methods for getting host drives;
4510 // next, protect the media tree all the while we're in here, as well as our member variables
4511 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4512 this->lockHandle(),
4513 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4514
4515 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4516 aName,
4517 aControllerPort,
4518 aDevice);
4519 if (pAttach.isNull())
4520 return setError(VBOX_E_OBJECT_NOT_FOUND,
4521 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4522 aDevice, aControllerPort, aName.c_str());
4523
4524 /* Remember previously mounted medium. The medium before taking the
4525 * backup is not necessarily the same thing. */
4526 ComObjPtr<Medium> oldmedium;
4527 oldmedium = pAttach->i_getMedium();
4528
4529 IMedium *iM = aMedium;
4530 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4531 if (aMedium && pMedium.isNull())
4532 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4533
4534 AutoCaller mediumCaller(pMedium);
4535 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4536
4537 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4538 if (pMedium)
4539 {
4540 DeviceType_T mediumType = pAttach->i_getType();
4541 switch (mediumType)
4542 {
4543 case DeviceType_DVD:
4544 case DeviceType_Floppy:
4545 break;
4546
4547 default:
4548 return setError(VBOX_E_INVALID_OBJECT_STATE,
4549 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4550 aControllerPort,
4551 aDevice,
4552 aName.c_str());
4553 }
4554 }
4555
4556 i_setModified(IsModified_Storage);
4557 mMediumAttachments.backup();
4558
4559 {
4560 // The backup operation makes the pAttach reference point to the
4561 // old settings. Re-get the correct reference.
4562 pAttach = i_findAttachment(*mMediumAttachments.data(),
4563 aName,
4564 aControllerPort,
4565 aDevice);
4566 if (!oldmedium.isNull())
4567 oldmedium->i_removeBackReference(mData->mUuid);
4568 if (!pMedium.isNull())
4569 {
4570 pMedium->i_addBackReference(mData->mUuid);
4571
4572 mediumLock.release();
4573 multiLock.release();
4574 i_addMediumToRegistry(pMedium);
4575 multiLock.acquire();
4576 mediumLock.acquire();
4577 }
4578
4579 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4580 pAttach->i_updateMedium(pMedium);
4581 }
4582
4583 i_setModified(IsModified_Storage);
4584
4585 mediumLock.release();
4586 multiLock.release();
4587 HRESULT rc = i_onMediumChange(pAttach, aForce);
4588 multiLock.acquire();
4589 mediumLock.acquire();
4590
4591 /* On error roll back this change only. */
4592 if (FAILED(rc))
4593 {
4594 if (!pMedium.isNull())
4595 pMedium->i_removeBackReference(mData->mUuid);
4596 pAttach = i_findAttachment(*mMediumAttachments.data(),
4597 aName,
4598 aControllerPort,
4599 aDevice);
4600 /* If the attachment is gone in the meantime, bail out. */
4601 if (pAttach.isNull())
4602 return rc;
4603 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4604 if (!oldmedium.isNull())
4605 oldmedium->i_addBackReference(mData->mUuid);
4606 pAttach->i_updateMedium(oldmedium);
4607 }
4608
4609 mediumLock.release();
4610 multiLock.release();
4611
4612 /* Save modified registries, but skip this machine as it's the caller's
4613 * job to save its settings like all other settings changes. */
4614 mParent->i_unmarkRegistryModified(i_getId());
4615 mParent->i_saveModifiedRegistries();
4616
4617 return rc;
4618}
4619HRESULT Machine::getMedium(const com::Utf8Str &aName,
4620 LONG aControllerPort,
4621 LONG aDevice,
4622 ComPtr<IMedium> &aMedium)
4623{
4624 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4625 aName.c_str(), aControllerPort, aDevice));
4626
4627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4628
4629 aMedium = NULL;
4630
4631 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4632 aName,
4633 aControllerPort,
4634 aDevice);
4635 if (pAttach.isNull())
4636 return setError(VBOX_E_OBJECT_NOT_FOUND,
4637 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4638 aDevice, aControllerPort, aName.c_str());
4639
4640 aMedium = pAttach->i_getMedium();
4641
4642 return S_OK;
4643}
4644
4645HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4646{
4647 if (aSlot < RT_ELEMENTS(mSerialPorts))
4648 {
4649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4650 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4651 return S_OK;
4652 }
4653 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4654}
4655
4656HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4657{
4658 if (aSlot < RT_ELEMENTS(mParallelPorts))
4659 {
4660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4661 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4662 return S_OK;
4663 }
4664 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4665}
4666
4667
4668HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4669{
4670 /* Do not assert if slot is out of range, just return the advertised
4671 status. testdriver/vbox.py triggers this in logVmInfo. */
4672 if (aSlot >= mNetworkAdapters.size())
4673 return setError(E_INVALIDARG,
4674 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4675 aSlot, mNetworkAdapters.size());
4676
4677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4678
4679 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4680
4681 return S_OK;
4682}
4683
4684HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4685{
4686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4687
4688 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4689 size_t i = 0;
4690 for (settings::StringsMap::const_iterator
4691 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4692 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4693 ++it, ++i)
4694 aKeys[i] = it->first;
4695
4696 return S_OK;
4697}
4698
4699 /**
4700 * @note Locks this object for reading.
4701 */
4702HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4703 com::Utf8Str &aValue)
4704{
4705 /* start with nothing found */
4706 aValue = "";
4707
4708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4709
4710 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4711 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4712 // found:
4713 aValue = it->second; // source is a Utf8Str
4714
4715 /* return the result to caller (may be empty) */
4716 return S_OK;
4717}
4718
4719 /**
4720 * @note Locks mParent for writing + this object for writing.
4721 */
4722HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4723{
4724 /* Because control characters in aKey have caused problems in the settings
4725 * they are rejected unless the key should be deleted. */
4726 if (!aValue.isEmpty())
4727 {
4728 for (size_t i = 0; i < aKey.length(); ++i)
4729 {
4730 char ch = aKey[i];
4731 if (RTLocCIsCntrl(ch))
4732 return E_INVALIDARG;
4733 }
4734 }
4735
4736 Utf8Str strOldValue; // empty
4737
4738 // locking note: we only hold the read lock briefly to look up the old value,
4739 // then release it and call the onExtraCanChange callbacks. There is a small
4740 // chance of a race insofar as the callback might be called twice if two callers
4741 // change the same key at the same time, but that's a much better solution
4742 // than the deadlock we had here before. The actual changing of the extradata
4743 // is then performed under the write lock and race-free.
4744
4745 // look up the old value first; if nothing has changed then we need not do anything
4746 {
4747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4748
4749 // For snapshots don't even think about allowing changes, extradata
4750 // is global for a machine, so there is nothing snapshot specific.
4751 if (i_isSnapshotMachine())
4752 return setError(VBOX_E_INVALID_VM_STATE,
4753 tr("Cannot set extradata for a snapshot"));
4754
4755 // check if the right IMachine instance is used
4756 if (mData->mRegistered && !i_isSessionMachine())
4757 return setError(VBOX_E_INVALID_VM_STATE,
4758 tr("Cannot set extradata for an immutable machine"));
4759
4760 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4761 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4762 strOldValue = it->second;
4763 }
4764
4765 bool fChanged;
4766 if ((fChanged = (strOldValue != aValue)))
4767 {
4768 // ask for permission from all listeners outside the locks;
4769 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4770 // lock to copy the list of callbacks to invoke
4771 Bstr bstrError;
4772 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4773 {
4774 const char *sep = bstrError.isEmpty() ? "" : ": ";
4775 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4776 return setError(E_ACCESSDENIED,
4777 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4778 aKey.c_str(),
4779 aValue.c_str(),
4780 sep,
4781 bstrError.raw());
4782 }
4783
4784 // data is changing and change not vetoed: then write it out under the lock
4785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4786
4787 if (aValue.isEmpty())
4788 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4789 else
4790 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4791 // creates a new key if needed
4792
4793 bool fNeedsGlobalSaveSettings = false;
4794 // This saving of settings is tricky: there is no "old state" for the
4795 // extradata items at all (unlike all other settings), so the old/new
4796 // settings comparison would give a wrong result!
4797 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4798
4799 if (fNeedsGlobalSaveSettings)
4800 {
4801 // save the global settings; for that we should hold only the VirtualBox lock
4802 alock.release();
4803 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4804 mParent->i_saveSettings();
4805 }
4806 }
4807
4808 // fire notification outside the lock
4809 if (fChanged)
4810 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4811
4812 return S_OK;
4813}
4814
4815HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4816{
4817 aProgress = NULL;
4818 NOREF(aSettingsFilePath);
4819 ReturnComNotImplemented();
4820}
4821
4822HRESULT Machine::saveSettings()
4823{
4824 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4827 if (FAILED(rc)) return rc;
4828
4829 /* the settings file path may never be null */
4830 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4831
4832 /* save all VM data excluding snapshots */
4833 bool fNeedsGlobalSaveSettings = false;
4834 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4835 mlock.release();
4836
4837 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4838 {
4839 // save the global settings; for that we should hold only the VirtualBox lock
4840 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4841 rc = mParent->i_saveSettings();
4842 }
4843
4844 return rc;
4845}
4846
4847
4848HRESULT Machine::discardSettings()
4849{
4850 /*
4851 * We need to take the machine list lock here as well as the machine one
4852 * or we'll get into trouble should any media stuff require rolling back.
4853 *
4854 * Details:
4855 *
4856 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4857 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4858 * 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]
4859 * 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
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4861 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4862 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4863 * 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
4864 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4865 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4866 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4867 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4869 * 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]
4870 * 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] (*)
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4872 * 0:005> k
4873 * # Child-SP RetAddr Call Site
4874 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4875 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4876 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4877 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4878 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4879 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4880 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4881 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4882 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4883 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4884 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4885 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4886 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4887 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4888 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4889 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4890 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4891 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4892 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4893 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4894 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4895 *
4896 */
4897 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4899
4900 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4901 if (FAILED(rc)) return rc;
4902
4903 /*
4904 * during this rollback, the session will be notified if data has
4905 * been actually changed
4906 */
4907 i_rollback(true /* aNotify */);
4908
4909 return S_OK;
4910}
4911
4912/** @note Locks objects! */
4913HRESULT Machine::unregister(AutoCaller &autoCaller,
4914 CleanupMode_T aCleanupMode,
4915 std::vector<ComPtr<IMedium> > &aMedia)
4916{
4917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4918
4919 Guid id(i_getId());
4920
4921 if (mData->mSession.mState != SessionState_Unlocked)
4922 return setError(VBOX_E_INVALID_OBJECT_STATE,
4923 tr("Cannot unregister the machine '%s' while it is locked"),
4924 mUserData->s.strName.c_str());
4925
4926 // wait for state dependents to drop to zero
4927 i_ensureNoStateDependencies();
4928
4929 if (!mData->mAccessible)
4930 {
4931 // inaccessible machines can only be unregistered; uninitialize ourselves
4932 // here because currently there may be no unregistered that are inaccessible
4933 // (this state combination is not supported). Note releasing the caller and
4934 // leaving the lock before calling uninit()
4935 alock.release();
4936 autoCaller.release();
4937
4938 uninit();
4939
4940 mParent->i_unregisterMachine(this, id);
4941 // calls VirtualBox::i_saveSettings()
4942
4943 return S_OK;
4944 }
4945
4946 HRESULT rc = S_OK;
4947 mData->llFilesToDelete.clear();
4948
4949 if (!mSSData->strStateFilePath.isEmpty())
4950 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4951
4952 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4953 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4954 mData->llFilesToDelete.push_back(strNVRAMFile);
4955
4956 // This list collects the medium objects from all medium attachments
4957 // which we will detach from the machine and its snapshots, in a specific
4958 // order which allows for closing all media without getting "media in use"
4959 // errors, simply by going through the list from the front to the back:
4960 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4961 // and must be closed before the parent media from the snapshots, or closing the parents
4962 // will fail because they still have children);
4963 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4964 // the root ("first") snapshot of the machine.
4965 MediaList llMedia;
4966
4967 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4968 && mMediumAttachments->size()
4969 )
4970 {
4971 // we have media attachments: detach them all and add the Medium objects to our list
4972 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4973 }
4974
4975 if (mData->mFirstSnapshot)
4976 {
4977 // add the media from the medium attachments of the snapshots to llMedia
4978 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4979 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4980 // into the children first
4981
4982 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4983 MachineState_T oldState = mData->mMachineState;
4984 mData->mMachineState = MachineState_DeletingSnapshot;
4985
4986 // make a copy of the first snapshot reference so the refcount does not
4987 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4988 // (would hang due to the AutoCaller voodoo)
4989 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4990
4991 // GO!
4992 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4993
4994 mData->mMachineState = oldState;
4995 }
4996
4997 if (FAILED(rc))
4998 {
4999 i_rollbackMedia();
5000 return rc;
5001 }
5002
5003 // commit all the media changes made above
5004 i_commitMedia();
5005
5006 mData->mRegistered = false;
5007
5008 // machine lock no longer needed
5009 alock.release();
5010
5011 /* Make sure that the settings of the current VM are not saved, because
5012 * they are rather crippled at this point to meet the cleanup expectations
5013 * and there's no point destroying the VM config on disk just because. */
5014 mParent->i_unmarkRegistryModified(id);
5015
5016 // return media to caller
5017 aMedia.resize(llMedia.size());
5018 size_t i = 0;
5019 for (MediaList::const_iterator
5020 it = llMedia.begin();
5021 it != llMedia.end();
5022 ++it, ++i)
5023 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5024
5025 mParent->i_unregisterMachine(this, id);
5026 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5027
5028 return S_OK;
5029}
5030
5031/**
5032 * Task record for deleting a machine config.
5033 */
5034class Machine::DeleteConfigTask
5035 : public Machine::Task
5036{
5037public:
5038 DeleteConfigTask(Machine *m,
5039 Progress *p,
5040 const Utf8Str &t,
5041 const RTCList<ComPtr<IMedium> > &llMediums,
5042 const StringsList &llFilesToDelete)
5043 : Task(m, p, t),
5044 m_llMediums(llMediums),
5045 m_llFilesToDelete(llFilesToDelete)
5046 {}
5047
5048private:
5049 void handler()
5050 {
5051 try
5052 {
5053 m_pMachine->i_deleteConfigHandler(*this);
5054 }
5055 catch (...)
5056 {
5057 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5058 }
5059 }
5060
5061 RTCList<ComPtr<IMedium> > m_llMediums;
5062 StringsList m_llFilesToDelete;
5063
5064 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5065};
5066
5067/**
5068 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5069 * SessionMachine::taskHandler().
5070 *
5071 * @note Locks this object for writing.
5072 *
5073 * @param task
5074 * @return
5075 */
5076void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5077{
5078 LogFlowThisFuncEnter();
5079
5080 AutoCaller autoCaller(this);
5081 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5082 if (FAILED(autoCaller.rc()))
5083 {
5084 /* we might have been uninitialized because the session was accidentally
5085 * closed by the client, so don't assert */
5086 HRESULT rc = setError(E_FAIL,
5087 tr("The session has been accidentally closed"));
5088 task.m_pProgress->i_notifyComplete(rc);
5089 LogFlowThisFuncLeave();
5090 return;
5091 }
5092
5093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5094
5095 HRESULT rc = S_OK;
5096
5097 try
5098 {
5099 ULONG uLogHistoryCount = 3;
5100 ComPtr<ISystemProperties> systemProperties;
5101 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5102 if (FAILED(rc)) throw rc;
5103
5104 if (!systemProperties.isNull())
5105 {
5106 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5107 if (FAILED(rc)) throw rc;
5108 }
5109
5110 MachineState_T oldState = mData->mMachineState;
5111 i_setMachineState(MachineState_SettingUp);
5112 alock.release();
5113 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5114 {
5115 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5116 {
5117 AutoCaller mac(pMedium);
5118 if (FAILED(mac.rc())) throw mac.rc();
5119 Utf8Str strLocation = pMedium->i_getLocationFull();
5120 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5121 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5122 if (FAILED(rc)) throw rc;
5123 }
5124 if (pMedium->i_isMediumFormatFile())
5125 {
5126 ComPtr<IProgress> pProgress2;
5127 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5128 if (FAILED(rc)) throw rc;
5129 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5130 if (FAILED(rc)) throw rc;
5131 }
5132
5133 /* Close the medium, deliberately without checking the return
5134 * code, and without leaving any trace in the error info, as
5135 * a failure here is a very minor issue, which shouldn't happen
5136 * as above we even managed to delete the medium. */
5137 {
5138 ErrorInfoKeeper eik;
5139 pMedium->Close();
5140 }
5141 }
5142 i_setMachineState(oldState);
5143 alock.acquire();
5144
5145 // delete the files pushed on the task list by Machine::Delete()
5146 // (this includes saved states of the machine and snapshots and
5147 // medium storage files from the IMedium list passed in, and the
5148 // machine XML file)
5149 for (StringsList::const_iterator
5150 it = task.m_llFilesToDelete.begin();
5151 it != task.m_llFilesToDelete.end();
5152 ++it)
5153 {
5154 const Utf8Str &strFile = *it;
5155 LogFunc(("Deleting file %s\n", strFile.c_str()));
5156 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5157 if (FAILED(rc)) throw rc;
5158
5159 int vrc = RTFileDelete(strFile.c_str());
5160 if (RT_FAILURE(vrc))
5161 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5162 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5163 }
5164
5165 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5166 if (FAILED(rc)) throw rc;
5167
5168 /* delete the settings only when the file actually exists */
5169 if (mData->pMachineConfigFile->fileExists())
5170 {
5171 /* Delete any backup or uncommitted XML files. Ignore failures.
5172 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5173 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5174 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5175 RTFileDelete(otherXml.c_str());
5176 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5177 RTFileDelete(otherXml.c_str());
5178
5179 /* delete the Logs folder, nothing important should be left
5180 * there (we don't check for errors because the user might have
5181 * some private files there that we don't want to delete) */
5182 Utf8Str logFolder;
5183 getLogFolder(logFolder);
5184 Assert(logFolder.length());
5185 if (RTDirExists(logFolder.c_str()))
5186 {
5187 /* Delete all VBox.log[.N] files from the Logs folder
5188 * (this must be in sync with the rotation logic in
5189 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5190 * files that may have been created by the GUI. */
5191 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5192 RTFileDelete(log.c_str());
5193 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5194 RTFileDelete(log.c_str());
5195 for (ULONG i = uLogHistoryCount; i > 0; i--)
5196 {
5197 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5198 RTFileDelete(log.c_str());
5199 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5200 RTFileDelete(log.c_str());
5201 }
5202 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5203 RTFileDelete(log.c_str());
5204#if defined(RT_OS_WINDOWS)
5205 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5206 RTFileDelete(log.c_str());
5207 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5208 RTFileDelete(log.c_str());
5209#endif
5210
5211 RTDirRemove(logFolder.c_str());
5212 }
5213
5214 /* delete the Snapshots folder, nothing important should be left
5215 * there (we don't check for errors because the user might have
5216 * some private files there that we don't want to delete) */
5217 Utf8Str strFullSnapshotFolder;
5218 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5219 Assert(!strFullSnapshotFolder.isEmpty());
5220 if (RTDirExists(strFullSnapshotFolder.c_str()))
5221 RTDirRemove(strFullSnapshotFolder.c_str());
5222
5223 // delete the directory that contains the settings file, but only
5224 // if it matches the VM name
5225 Utf8Str settingsDir;
5226 if (i_isInOwnDir(&settingsDir))
5227 RTDirRemove(settingsDir.c_str());
5228 }
5229
5230 alock.release();
5231
5232 mParent->i_saveModifiedRegistries();
5233 }
5234 catch (HRESULT aRC) { rc = aRC; }
5235
5236 task.m_pProgress->i_notifyComplete(rc);
5237
5238 LogFlowThisFuncLeave();
5239}
5240
5241HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5242{
5243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5244
5245 HRESULT rc = i_checkStateDependency(MutableStateDep);
5246 if (FAILED(rc)) return rc;
5247
5248 if (mData->mRegistered)
5249 return setError(VBOX_E_INVALID_VM_STATE,
5250 tr("Cannot delete settings of a registered machine"));
5251
5252 // collect files to delete
5253 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5254 // machine config file
5255 if (mData->pMachineConfigFile->fileExists())
5256 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5257 // backup of machine config file
5258 Utf8Str strTmp(mData->m_strConfigFileFull);
5259 strTmp.append("-prev");
5260 if (RTFileExists(strTmp.c_str()))
5261 llFilesToDelete.push_back(strTmp);
5262
5263 RTCList<ComPtr<IMedium> > llMediums;
5264 for (size_t i = 0; i < aMedia.size(); ++i)
5265 {
5266 IMedium *pIMedium(aMedia[i]);
5267 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5268 if (pMedium.isNull())
5269 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5270 SafeArray<BSTR> ids;
5271 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5272 if (FAILED(rc)) return rc;
5273 /* At this point the medium should not have any back references
5274 * anymore. If it has it is attached to another VM and *must* not
5275 * deleted. */
5276 if (ids.size() < 1)
5277 llMediums.append(pMedium);
5278 }
5279
5280 ComObjPtr<Progress> pProgress;
5281 pProgress.createObject();
5282 rc = pProgress->init(i_getVirtualBox(),
5283 static_cast<IMachine*>(this) /* aInitiator */,
5284 tr("Deleting files"),
5285 true /* fCancellable */,
5286 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5287 tr("Collecting file inventory"));
5288 if (FAILED(rc))
5289 return rc;
5290
5291 /* create and start the task on a separate thread (note that it will not
5292 * start working until we release alock) */
5293 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5294 rc = pTask->createThread();
5295 pTask = NULL;
5296 if (FAILED(rc))
5297 return rc;
5298
5299 pProgress.queryInterfaceTo(aProgress.asOutParam());
5300
5301 LogFlowFuncLeave();
5302
5303 return S_OK;
5304}
5305
5306HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5307{
5308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5309
5310 ComObjPtr<Snapshot> pSnapshot;
5311 HRESULT rc;
5312
5313 if (aNameOrId.isEmpty())
5314 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5315 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5316 else
5317 {
5318 Guid uuid(aNameOrId);
5319 if (uuid.isValid())
5320 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5321 else
5322 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5323 }
5324 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5325
5326 return rc;
5327}
5328
5329HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5330 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5331{
5332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5333
5334 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5335 if (FAILED(rc)) return rc;
5336
5337 ComObjPtr<SharedFolder> sharedFolder;
5338 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5339 if (SUCCEEDED(rc))
5340 return setError(VBOX_E_OBJECT_IN_USE,
5341 tr("Shared folder named '%s' already exists"),
5342 aName.c_str());
5343
5344 sharedFolder.createObject();
5345 rc = sharedFolder->init(i_getMachine(),
5346 aName,
5347 aHostPath,
5348 !!aWritable,
5349 !!aAutomount,
5350 aAutoMountPoint,
5351 true /* fFailOnError */);
5352 if (FAILED(rc)) return rc;
5353
5354 i_setModified(IsModified_SharedFolders);
5355 mHWData.backup();
5356 mHWData->mSharedFolders.push_back(sharedFolder);
5357
5358 /* inform the direct session if any */
5359 alock.release();
5360 i_onSharedFolderChange();
5361
5362 return S_OK;
5363}
5364
5365HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5366{
5367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5368
5369 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5370 if (FAILED(rc)) return rc;
5371
5372 ComObjPtr<SharedFolder> sharedFolder;
5373 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5374 if (FAILED(rc)) return rc;
5375
5376 i_setModified(IsModified_SharedFolders);
5377 mHWData.backup();
5378 mHWData->mSharedFolders.remove(sharedFolder);
5379
5380 /* inform the direct session if any */
5381 alock.release();
5382 i_onSharedFolderChange();
5383
5384 return S_OK;
5385}
5386
5387HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5388{
5389 /* start with No */
5390 *aCanShow = FALSE;
5391
5392 ComPtr<IInternalSessionControl> directControl;
5393 {
5394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5395
5396 if (mData->mSession.mState != SessionState_Locked)
5397 return setError(VBOX_E_INVALID_VM_STATE,
5398 tr("Machine is not locked for session (session state: %s)"),
5399 Global::stringifySessionState(mData->mSession.mState));
5400
5401 if (mData->mSession.mLockType == LockType_VM)
5402 directControl = mData->mSession.mDirectControl;
5403 }
5404
5405 /* ignore calls made after #OnSessionEnd() is called */
5406 if (!directControl)
5407 return S_OK;
5408
5409 LONG64 dummy;
5410 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5411}
5412
5413HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5414{
5415 ComPtr<IInternalSessionControl> directControl;
5416 {
5417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5418
5419 if (mData->mSession.mState != SessionState_Locked)
5420 return setError(E_FAIL,
5421 tr("Machine is not locked for session (session state: %s)"),
5422 Global::stringifySessionState(mData->mSession.mState));
5423
5424 if (mData->mSession.mLockType == LockType_VM)
5425 directControl = mData->mSession.mDirectControl;
5426 }
5427
5428 /* ignore calls made after #OnSessionEnd() is called */
5429 if (!directControl)
5430 return S_OK;
5431
5432 BOOL dummy;
5433 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5434}
5435
5436#ifdef VBOX_WITH_GUEST_PROPS
5437/**
5438 * Look up a guest property in VBoxSVC's internal structures.
5439 */
5440HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5441 com::Utf8Str &aValue,
5442 LONG64 *aTimestamp,
5443 com::Utf8Str &aFlags) const
5444{
5445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5446
5447 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5448 if (it != mHWData->mGuestProperties.end())
5449 {
5450 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5451 aValue = it->second.strValue;
5452 *aTimestamp = it->second.mTimestamp;
5453 GuestPropWriteFlags(it->second.mFlags, szFlags);
5454 aFlags = Utf8Str(szFlags);
5455 }
5456
5457 return S_OK;
5458}
5459
5460/**
5461 * Query the VM that a guest property belongs to for the property.
5462 * @returns E_ACCESSDENIED if the VM process is not available or not
5463 * currently handling queries and the lookup should then be done in
5464 * VBoxSVC.
5465 */
5466HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5467 com::Utf8Str &aValue,
5468 LONG64 *aTimestamp,
5469 com::Utf8Str &aFlags) const
5470{
5471 HRESULT rc = S_OK;
5472 Bstr bstrValue;
5473 Bstr bstrFlags;
5474
5475 ComPtr<IInternalSessionControl> directControl;
5476 {
5477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5478 if (mData->mSession.mLockType == LockType_VM)
5479 directControl = mData->mSession.mDirectControl;
5480 }
5481
5482 /* ignore calls made after #OnSessionEnd() is called */
5483 if (!directControl)
5484 rc = E_ACCESSDENIED;
5485 else
5486 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5487 0 /* accessMode */,
5488 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5489
5490 aValue = bstrValue;
5491 aFlags = bstrFlags;
5492
5493 return rc;
5494}
5495#endif // VBOX_WITH_GUEST_PROPS
5496
5497HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5498 com::Utf8Str &aValue,
5499 LONG64 *aTimestamp,
5500 com::Utf8Str &aFlags)
5501{
5502#ifndef VBOX_WITH_GUEST_PROPS
5503 ReturnComNotImplemented();
5504#else // VBOX_WITH_GUEST_PROPS
5505
5506 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5507
5508 if (rc == E_ACCESSDENIED)
5509 /* The VM is not running or the service is not (yet) accessible */
5510 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5511 return rc;
5512#endif // VBOX_WITH_GUEST_PROPS
5513}
5514
5515HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5516{
5517 LONG64 dummyTimestamp;
5518 com::Utf8Str dummyFlags;
5519 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5520 return rc;
5521
5522}
5523HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5524{
5525 com::Utf8Str dummyFlags;
5526 com::Utf8Str dummyValue;
5527 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5528 return rc;
5529}
5530
5531#ifdef VBOX_WITH_GUEST_PROPS
5532/**
5533 * Set a guest property in VBoxSVC's internal structures.
5534 */
5535HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5536 const com::Utf8Str &aFlags, bool fDelete)
5537{
5538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5539 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5540 if (FAILED(rc)) return rc;
5541
5542 try
5543 {
5544 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5545 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5546 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5547
5548 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5549 if (it == mHWData->mGuestProperties.end())
5550 {
5551 if (!fDelete)
5552 {
5553 i_setModified(IsModified_MachineData);
5554 mHWData.backupEx();
5555
5556 RTTIMESPEC time;
5557 HWData::GuestProperty prop;
5558 prop.strValue = Bstr(aValue).raw();
5559 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5560 prop.mFlags = fFlags;
5561 mHWData->mGuestProperties[aName] = prop;
5562 }
5563 }
5564 else
5565 {
5566 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5567 {
5568 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5569 }
5570 else
5571 {
5572 i_setModified(IsModified_MachineData);
5573 mHWData.backupEx();
5574
5575 /* The backupEx() operation invalidates our iterator,
5576 * so get a new one. */
5577 it = mHWData->mGuestProperties.find(aName);
5578 Assert(it != mHWData->mGuestProperties.end());
5579
5580 if (!fDelete)
5581 {
5582 RTTIMESPEC time;
5583 it->second.strValue = aValue;
5584 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5585 it->second.mFlags = fFlags;
5586 }
5587 else
5588 mHWData->mGuestProperties.erase(it);
5589 }
5590 }
5591
5592 if (SUCCEEDED(rc))
5593 {
5594 alock.release();
5595
5596 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5597 }
5598 }
5599 catch (std::bad_alloc &)
5600 {
5601 rc = E_OUTOFMEMORY;
5602 }
5603
5604 return rc;
5605}
5606
5607/**
5608 * Set a property on the VM that that property belongs to.
5609 * @returns E_ACCESSDENIED if the VM process is not available or not
5610 * currently handling queries and the setting should then be done in
5611 * VBoxSVC.
5612 */
5613HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5614 const com::Utf8Str &aFlags, bool fDelete)
5615{
5616 HRESULT rc;
5617
5618 try
5619 {
5620 ComPtr<IInternalSessionControl> directControl;
5621 {
5622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5623 if (mData->mSession.mLockType == LockType_VM)
5624 directControl = mData->mSession.mDirectControl;
5625 }
5626
5627 Bstr dummy1; /* will not be changed (setter) */
5628 Bstr dummy2; /* will not be changed (setter) */
5629 LONG64 dummy64;
5630 if (!directControl)
5631 rc = E_ACCESSDENIED;
5632 else
5633 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5634 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5635 fDelete ? 2 : 1 /* accessMode */,
5636 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5637 }
5638 catch (std::bad_alloc &)
5639 {
5640 rc = E_OUTOFMEMORY;
5641 }
5642
5643 return rc;
5644}
5645#endif // VBOX_WITH_GUEST_PROPS
5646
5647HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5648 const com::Utf8Str &aFlags)
5649{
5650#ifndef VBOX_WITH_GUEST_PROPS
5651 ReturnComNotImplemented();
5652#else // VBOX_WITH_GUEST_PROPS
5653 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5654 if (rc == E_ACCESSDENIED)
5655 /* The VM is not running or the service is not (yet) accessible */
5656 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5657 return rc;
5658#endif // VBOX_WITH_GUEST_PROPS
5659}
5660
5661HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5662{
5663 return setGuestProperty(aProperty, aValue, "");
5664}
5665
5666HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5667{
5668#ifndef VBOX_WITH_GUEST_PROPS
5669 ReturnComNotImplemented();
5670#else // VBOX_WITH_GUEST_PROPS
5671 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5672 if (rc == E_ACCESSDENIED)
5673 /* The VM is not running or the service is not (yet) accessible */
5674 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5675 return rc;
5676#endif // VBOX_WITH_GUEST_PROPS
5677}
5678
5679#ifdef VBOX_WITH_GUEST_PROPS
5680/**
5681 * Enumerate the guest properties in VBoxSVC's internal structures.
5682 */
5683HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5684 std::vector<com::Utf8Str> &aNames,
5685 std::vector<com::Utf8Str> &aValues,
5686 std::vector<LONG64> &aTimestamps,
5687 std::vector<com::Utf8Str> &aFlags)
5688{
5689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5690 Utf8Str strPatterns(aPatterns);
5691
5692 /*
5693 * Look for matching patterns and build up a list.
5694 */
5695 HWData::GuestPropertyMap propMap;
5696 for (HWData::GuestPropertyMap::const_iterator
5697 it = mHWData->mGuestProperties.begin();
5698 it != mHWData->mGuestProperties.end();
5699 ++it)
5700 {
5701 if ( strPatterns.isEmpty()
5702 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5703 RTSTR_MAX,
5704 it->first.c_str(),
5705 RTSTR_MAX,
5706 NULL)
5707 )
5708 propMap.insert(*it);
5709 }
5710
5711 alock.release();
5712
5713 /*
5714 * And build up the arrays for returning the property information.
5715 */
5716 size_t cEntries = propMap.size();
5717
5718 aNames.resize(cEntries);
5719 aValues.resize(cEntries);
5720 aTimestamps.resize(cEntries);
5721 aFlags.resize(cEntries);
5722
5723 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5724 size_t i = 0;
5725 for (HWData::GuestPropertyMap::const_iterator
5726 it = propMap.begin();
5727 it != propMap.end();
5728 ++it, ++i)
5729 {
5730 aNames[i] = it->first;
5731 aValues[i] = it->second.strValue;
5732 aTimestamps[i] = it->second.mTimestamp;
5733 GuestPropWriteFlags(it->second.mFlags, szFlags);
5734 aFlags[i] = Utf8Str(szFlags);
5735 }
5736
5737 return S_OK;
5738}
5739
5740/**
5741 * Enumerate the properties managed by a VM.
5742 * @returns E_ACCESSDENIED if the VM process is not available or not
5743 * currently handling queries and the setting should then be done in
5744 * VBoxSVC.
5745 */
5746HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5747 std::vector<com::Utf8Str> &aNames,
5748 std::vector<com::Utf8Str> &aValues,
5749 std::vector<LONG64> &aTimestamps,
5750 std::vector<com::Utf8Str> &aFlags)
5751{
5752 HRESULT rc;
5753 ComPtr<IInternalSessionControl> directControl;
5754 {
5755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5756 if (mData->mSession.mLockType == LockType_VM)
5757 directControl = mData->mSession.mDirectControl;
5758 }
5759
5760 com::SafeArray<BSTR> bNames;
5761 com::SafeArray<BSTR> bValues;
5762 com::SafeArray<LONG64> bTimestamps;
5763 com::SafeArray<BSTR> bFlags;
5764
5765 if (!directControl)
5766 rc = E_ACCESSDENIED;
5767 else
5768 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5769 ComSafeArrayAsOutParam(bNames),
5770 ComSafeArrayAsOutParam(bValues),
5771 ComSafeArrayAsOutParam(bTimestamps),
5772 ComSafeArrayAsOutParam(bFlags));
5773 size_t i;
5774 aNames.resize(bNames.size());
5775 for (i = 0; i < bNames.size(); ++i)
5776 aNames[i] = Utf8Str(bNames[i]);
5777 aValues.resize(bValues.size());
5778 for (i = 0; i < bValues.size(); ++i)
5779 aValues[i] = Utf8Str(bValues[i]);
5780 aTimestamps.resize(bTimestamps.size());
5781 for (i = 0; i < bTimestamps.size(); ++i)
5782 aTimestamps[i] = bTimestamps[i];
5783 aFlags.resize(bFlags.size());
5784 for (i = 0; i < bFlags.size(); ++i)
5785 aFlags[i] = Utf8Str(bFlags[i]);
5786
5787 return rc;
5788}
5789#endif // VBOX_WITH_GUEST_PROPS
5790HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5791 std::vector<com::Utf8Str> &aNames,
5792 std::vector<com::Utf8Str> &aValues,
5793 std::vector<LONG64> &aTimestamps,
5794 std::vector<com::Utf8Str> &aFlags)
5795{
5796#ifndef VBOX_WITH_GUEST_PROPS
5797 ReturnComNotImplemented();
5798#else // VBOX_WITH_GUEST_PROPS
5799
5800 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5801
5802 if (rc == E_ACCESSDENIED)
5803 /* The VM is not running or the service is not (yet) accessible */
5804 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5805 return rc;
5806#endif // VBOX_WITH_GUEST_PROPS
5807}
5808
5809HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5810 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5811{
5812 MediumAttachmentList atts;
5813
5814 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5815 if (FAILED(rc)) return rc;
5816
5817 aMediumAttachments.resize(atts.size());
5818 size_t i = 0;
5819 for (MediumAttachmentList::const_iterator
5820 it = atts.begin();
5821 it != atts.end();
5822 ++it, ++i)
5823 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5824
5825 return S_OK;
5826}
5827
5828HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5829 LONG aControllerPort,
5830 LONG aDevice,
5831 ComPtr<IMediumAttachment> &aAttachment)
5832{
5833 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5834 aName.c_str(), aControllerPort, aDevice));
5835
5836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5837
5838 aAttachment = NULL;
5839
5840 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5841 aName,
5842 aControllerPort,
5843 aDevice);
5844 if (pAttach.isNull())
5845 return setError(VBOX_E_OBJECT_NOT_FOUND,
5846 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5847 aDevice, aControllerPort, aName.c_str());
5848
5849 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5850
5851 return S_OK;
5852}
5853
5854
5855HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5856 StorageBus_T aConnectionType,
5857 ComPtr<IStorageController> &aController)
5858{
5859 if ( (aConnectionType <= StorageBus_Null)
5860 || (aConnectionType > StorageBus_VirtioSCSI))
5861 return setError(E_INVALIDARG,
5862 tr("Invalid connection type: %d"),
5863 aConnectionType);
5864
5865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5866
5867 HRESULT rc = i_checkStateDependency(MutableStateDep);
5868 if (FAILED(rc)) return rc;
5869
5870 /* try to find one with the name first. */
5871 ComObjPtr<StorageController> ctrl;
5872
5873 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5874 if (SUCCEEDED(rc))
5875 return setError(VBOX_E_OBJECT_IN_USE,
5876 tr("Storage controller named '%s' already exists"),
5877 aName.c_str());
5878
5879 ctrl.createObject();
5880
5881 /* get a new instance number for the storage controller */
5882 ULONG ulInstance = 0;
5883 bool fBootable = true;
5884 for (StorageControllerList::const_iterator
5885 it = mStorageControllers->begin();
5886 it != mStorageControllers->end();
5887 ++it)
5888 {
5889 if ((*it)->i_getStorageBus() == aConnectionType)
5890 {
5891 ULONG ulCurInst = (*it)->i_getInstance();
5892
5893 if (ulCurInst >= ulInstance)
5894 ulInstance = ulCurInst + 1;
5895
5896 /* Only one controller of each type can be marked as bootable. */
5897 if ((*it)->i_getBootable())
5898 fBootable = false;
5899 }
5900 }
5901
5902 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5903 if (FAILED(rc)) return rc;
5904
5905 i_setModified(IsModified_Storage);
5906 mStorageControllers.backup();
5907 mStorageControllers->push_back(ctrl);
5908
5909 ctrl.queryInterfaceTo(aController.asOutParam());
5910
5911 /* inform the direct session if any */
5912 alock.release();
5913 i_onStorageControllerChange(i_getId(), aName);
5914
5915 return S_OK;
5916}
5917
5918HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5919 ComPtr<IStorageController> &aStorageController)
5920{
5921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5922
5923 ComObjPtr<StorageController> ctrl;
5924
5925 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5926 if (SUCCEEDED(rc))
5927 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5928
5929 return rc;
5930}
5931
5932HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5933 ULONG aInstance,
5934 ComPtr<IStorageController> &aStorageController)
5935{
5936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5937
5938 for (StorageControllerList::const_iterator
5939 it = mStorageControllers->begin();
5940 it != mStorageControllers->end();
5941 ++it)
5942 {
5943 if ( (*it)->i_getStorageBus() == aConnectionType
5944 && (*it)->i_getInstance() == aInstance)
5945 {
5946 (*it).queryInterfaceTo(aStorageController.asOutParam());
5947 return S_OK;
5948 }
5949 }
5950
5951 return setError(VBOX_E_OBJECT_NOT_FOUND,
5952 tr("Could not find a storage controller with instance number '%lu'"),
5953 aInstance);
5954}
5955
5956HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5957{
5958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5959
5960 HRESULT rc = i_checkStateDependency(MutableStateDep);
5961 if (FAILED(rc)) return rc;
5962
5963 ComObjPtr<StorageController> ctrl;
5964
5965 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5966 if (SUCCEEDED(rc))
5967 {
5968 /* Ensure that only one controller of each type is marked as bootable. */
5969 if (aBootable == TRUE)
5970 {
5971 for (StorageControllerList::const_iterator
5972 it = mStorageControllers->begin();
5973 it != mStorageControllers->end();
5974 ++it)
5975 {
5976 ComObjPtr<StorageController> aCtrl = (*it);
5977
5978 if ( (aCtrl->i_getName() != aName)
5979 && aCtrl->i_getBootable() == TRUE
5980 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5981 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5982 {
5983 aCtrl->i_setBootable(FALSE);
5984 break;
5985 }
5986 }
5987 }
5988
5989 if (SUCCEEDED(rc))
5990 {
5991 ctrl->i_setBootable(aBootable);
5992 i_setModified(IsModified_Storage);
5993 }
5994 }
5995
5996 if (SUCCEEDED(rc))
5997 {
5998 /* inform the direct session if any */
5999 alock.release();
6000 i_onStorageControllerChange(i_getId(), aName);
6001 }
6002
6003 return rc;
6004}
6005
6006HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6007{
6008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6009
6010 HRESULT rc = i_checkStateDependency(MutableStateDep);
6011 if (FAILED(rc)) return rc;
6012
6013 ComObjPtr<StorageController> ctrl;
6014 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6015 if (FAILED(rc)) return rc;
6016
6017 MediumAttachmentList llDetachedAttachments;
6018 {
6019 /* find all attached devices to the appropriate storage controller and detach them all */
6020 // make a temporary list because detachDevice invalidates iterators into
6021 // mMediumAttachments
6022 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6023
6024 for (MediumAttachmentList::const_iterator
6025 it = llAttachments2.begin();
6026 it != llAttachments2.end();
6027 ++it)
6028 {
6029 MediumAttachment *pAttachTemp = *it;
6030
6031 AutoCaller localAutoCaller(pAttachTemp);
6032 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6033
6034 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6035
6036 if (pAttachTemp->i_getControllerName() == aName)
6037 {
6038 llDetachedAttachments.push_back(pAttachTemp);
6039 rc = i_detachDevice(pAttachTemp, alock, NULL);
6040 if (FAILED(rc)) return rc;
6041 }
6042 }
6043 }
6044
6045 /* send event about detached devices before removing parent controller */
6046 for (MediumAttachmentList::const_iterator
6047 it = llDetachedAttachments.begin();
6048 it != llDetachedAttachments.end();
6049 ++it)
6050 {
6051 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6052 }
6053
6054 /* We can remove it now. */
6055 i_setModified(IsModified_Storage);
6056 mStorageControllers.backup();
6057
6058 ctrl->i_unshare();
6059
6060 mStorageControllers->remove(ctrl);
6061
6062 /* inform the direct session if any */
6063 alock.release();
6064 i_onStorageControllerChange(i_getId(), aName);
6065
6066 return S_OK;
6067}
6068
6069HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6070 ComPtr<IUSBController> &aController)
6071{
6072 if ( (aType <= USBControllerType_Null)
6073 || (aType >= USBControllerType_Last))
6074 return setError(E_INVALIDARG,
6075 tr("Invalid USB controller type: %d"),
6076 aType);
6077
6078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6079
6080 HRESULT rc = i_checkStateDependency(MutableStateDep);
6081 if (FAILED(rc)) return rc;
6082
6083 /* try to find one with the same type first. */
6084 ComObjPtr<USBController> ctrl;
6085
6086 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6087 if (SUCCEEDED(rc))
6088 return setError(VBOX_E_OBJECT_IN_USE,
6089 tr("USB controller named '%s' already exists"),
6090 aName.c_str());
6091
6092 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6093 ULONG maxInstances;
6094 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6095 if (FAILED(rc))
6096 return rc;
6097
6098 ULONG cInstances = i_getUSBControllerCountByType(aType);
6099 if (cInstances >= maxInstances)
6100 return setError(E_INVALIDARG,
6101 tr("Too many USB controllers of this type"));
6102
6103 ctrl.createObject();
6104
6105 rc = ctrl->init(this, aName, aType);
6106 if (FAILED(rc)) return rc;
6107
6108 i_setModified(IsModified_USB);
6109 mUSBControllers.backup();
6110 mUSBControllers->push_back(ctrl);
6111
6112 ctrl.queryInterfaceTo(aController.asOutParam());
6113
6114 /* inform the direct session if any */
6115 alock.release();
6116 i_onUSBControllerChange();
6117
6118 return S_OK;
6119}
6120
6121HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6122{
6123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6124
6125 ComObjPtr<USBController> ctrl;
6126
6127 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6128 if (SUCCEEDED(rc))
6129 ctrl.queryInterfaceTo(aController.asOutParam());
6130
6131 return rc;
6132}
6133
6134HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6135 ULONG *aControllers)
6136{
6137 if ( (aType <= USBControllerType_Null)
6138 || (aType >= USBControllerType_Last))
6139 return setError(E_INVALIDARG,
6140 tr("Invalid USB controller type: %d"),
6141 aType);
6142
6143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6144
6145 ComObjPtr<USBController> ctrl;
6146
6147 *aControllers = i_getUSBControllerCountByType(aType);
6148
6149 return S_OK;
6150}
6151
6152HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6153{
6154
6155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6156
6157 HRESULT rc = i_checkStateDependency(MutableStateDep);
6158 if (FAILED(rc)) return rc;
6159
6160 ComObjPtr<USBController> ctrl;
6161 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6162 if (FAILED(rc)) return rc;
6163
6164 i_setModified(IsModified_USB);
6165 mUSBControllers.backup();
6166
6167 ctrl->i_unshare();
6168
6169 mUSBControllers->remove(ctrl);
6170
6171 /* inform the direct session if any */
6172 alock.release();
6173 i_onUSBControllerChange();
6174
6175 return S_OK;
6176}
6177
6178HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6179 ULONG *aOriginX,
6180 ULONG *aOriginY,
6181 ULONG *aWidth,
6182 ULONG *aHeight,
6183 BOOL *aEnabled)
6184{
6185 uint32_t u32OriginX= 0;
6186 uint32_t u32OriginY= 0;
6187 uint32_t u32Width = 0;
6188 uint32_t u32Height = 0;
6189 uint16_t u16Flags = 0;
6190
6191 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6192 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6193 if (RT_FAILURE(vrc))
6194 {
6195#ifdef RT_OS_WINDOWS
6196 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6197 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6198 * So just assign fEnable to TRUE again.
6199 * The right fix would be to change GUI API wrappers to make sure that parameters
6200 * are changed only if API succeeds.
6201 */
6202 *aEnabled = TRUE;
6203#endif
6204 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6205 tr("Saved guest size is not available (%Rrc)"),
6206 vrc);
6207 }
6208
6209 *aOriginX = u32OriginX;
6210 *aOriginY = u32OriginY;
6211 *aWidth = u32Width;
6212 *aHeight = u32Height;
6213 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6214
6215 return S_OK;
6216}
6217
6218HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6219 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6220{
6221 if (aScreenId != 0)
6222 return E_NOTIMPL;
6223
6224 if ( aBitmapFormat != BitmapFormat_BGR0
6225 && aBitmapFormat != BitmapFormat_BGRA
6226 && aBitmapFormat != BitmapFormat_RGBA
6227 && aBitmapFormat != BitmapFormat_PNG)
6228 return setError(E_NOTIMPL,
6229 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6230
6231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6232
6233 uint8_t *pu8Data = NULL;
6234 uint32_t cbData = 0;
6235 uint32_t u32Width = 0;
6236 uint32_t u32Height = 0;
6237
6238 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6239
6240 if (RT_FAILURE(vrc))
6241 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6242 tr("Saved thumbnail data is not available (%Rrc)"),
6243 vrc);
6244
6245 HRESULT hr = S_OK;
6246
6247 *aWidth = u32Width;
6248 *aHeight = u32Height;
6249
6250 if (cbData > 0)
6251 {
6252 /* Convert pixels to the format expected by the API caller. */
6253 if (aBitmapFormat == BitmapFormat_BGR0)
6254 {
6255 /* [0] B, [1] G, [2] R, [3] 0. */
6256 aData.resize(cbData);
6257 memcpy(&aData.front(), pu8Data, cbData);
6258 }
6259 else if (aBitmapFormat == BitmapFormat_BGRA)
6260 {
6261 /* [0] B, [1] G, [2] R, [3] A. */
6262 aData.resize(cbData);
6263 for (uint32_t i = 0; i < cbData; i += 4)
6264 {
6265 aData[i] = pu8Data[i];
6266 aData[i + 1] = pu8Data[i + 1];
6267 aData[i + 2] = pu8Data[i + 2];
6268 aData[i + 3] = 0xff;
6269 }
6270 }
6271 else if (aBitmapFormat == BitmapFormat_RGBA)
6272 {
6273 /* [0] R, [1] G, [2] B, [3] A. */
6274 aData.resize(cbData);
6275 for (uint32_t i = 0; i < cbData; i += 4)
6276 {
6277 aData[i] = pu8Data[i + 2];
6278 aData[i + 1] = pu8Data[i + 1];
6279 aData[i + 2] = pu8Data[i];
6280 aData[i + 3] = 0xff;
6281 }
6282 }
6283 else if (aBitmapFormat == BitmapFormat_PNG)
6284 {
6285 uint8_t *pu8PNG = NULL;
6286 uint32_t cbPNG = 0;
6287 uint32_t cxPNG = 0;
6288 uint32_t cyPNG = 0;
6289
6290 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6291
6292 if (RT_SUCCESS(vrc))
6293 {
6294 aData.resize(cbPNG);
6295 if (cbPNG)
6296 memcpy(&aData.front(), pu8PNG, cbPNG);
6297 }
6298 else
6299 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6300 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6301 vrc);
6302
6303 RTMemFree(pu8PNG);
6304 }
6305 }
6306
6307 freeSavedDisplayScreenshot(pu8Data);
6308
6309 return hr;
6310}
6311
6312HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6313 ULONG *aWidth,
6314 ULONG *aHeight,
6315 std::vector<BitmapFormat_T> &aBitmapFormats)
6316{
6317 if (aScreenId != 0)
6318 return E_NOTIMPL;
6319
6320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6321
6322 uint8_t *pu8Data = NULL;
6323 uint32_t cbData = 0;
6324 uint32_t u32Width = 0;
6325 uint32_t u32Height = 0;
6326
6327 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6328
6329 if (RT_FAILURE(vrc))
6330 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6331 tr("Saved screenshot data is not available (%Rrc)"),
6332 vrc);
6333
6334 *aWidth = u32Width;
6335 *aHeight = u32Height;
6336 aBitmapFormats.resize(1);
6337 aBitmapFormats[0] = BitmapFormat_PNG;
6338
6339 freeSavedDisplayScreenshot(pu8Data);
6340
6341 return S_OK;
6342}
6343
6344HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6345 BitmapFormat_T aBitmapFormat,
6346 ULONG *aWidth,
6347 ULONG *aHeight,
6348 std::vector<BYTE> &aData)
6349{
6350 if (aScreenId != 0)
6351 return E_NOTIMPL;
6352
6353 if (aBitmapFormat != BitmapFormat_PNG)
6354 return E_NOTIMPL;
6355
6356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6357
6358 uint8_t *pu8Data = NULL;
6359 uint32_t cbData = 0;
6360 uint32_t u32Width = 0;
6361 uint32_t u32Height = 0;
6362
6363 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6364
6365 if (RT_FAILURE(vrc))
6366 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6367 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6368 vrc);
6369
6370 *aWidth = u32Width;
6371 *aHeight = u32Height;
6372
6373 aData.resize(cbData);
6374 if (cbData)
6375 memcpy(&aData.front(), pu8Data, cbData);
6376
6377 freeSavedDisplayScreenshot(pu8Data);
6378
6379 return S_OK;
6380}
6381
6382HRESULT Machine::hotPlugCPU(ULONG aCpu)
6383{
6384 HRESULT rc = S_OK;
6385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6386
6387 if (!mHWData->mCPUHotPlugEnabled)
6388 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6389
6390 if (aCpu >= mHWData->mCPUCount)
6391 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6392
6393 if (mHWData->mCPUAttached[aCpu])
6394 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6395
6396 alock.release();
6397 rc = i_onCPUChange(aCpu, false);
6398 alock.acquire();
6399 if (FAILED(rc)) return rc;
6400
6401 i_setModified(IsModified_MachineData);
6402 mHWData.backup();
6403 mHWData->mCPUAttached[aCpu] = true;
6404
6405 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6406 if (Global::IsOnline(mData->mMachineState))
6407 i_saveSettings(NULL);
6408
6409 return S_OK;
6410}
6411
6412HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6413{
6414 HRESULT rc = S_OK;
6415
6416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 if (!mHWData->mCPUHotPlugEnabled)
6419 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6420
6421 if (aCpu >= SchemaDefs::MaxCPUCount)
6422 return setError(E_INVALIDARG,
6423 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6424 SchemaDefs::MaxCPUCount);
6425
6426 if (!mHWData->mCPUAttached[aCpu])
6427 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6428
6429 /* CPU 0 can't be detached */
6430 if (aCpu == 0)
6431 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6432
6433 alock.release();
6434 rc = i_onCPUChange(aCpu, true);
6435 alock.acquire();
6436 if (FAILED(rc)) return rc;
6437
6438 i_setModified(IsModified_MachineData);
6439 mHWData.backup();
6440 mHWData->mCPUAttached[aCpu] = false;
6441
6442 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6443 if (Global::IsOnline(mData->mMachineState))
6444 i_saveSettings(NULL);
6445
6446 return S_OK;
6447}
6448
6449HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6450{
6451 *aAttached = false;
6452
6453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 /* If hotplug is enabled the CPU is always enabled. */
6456 if (!mHWData->mCPUHotPlugEnabled)
6457 {
6458 if (aCpu < mHWData->mCPUCount)
6459 *aAttached = true;
6460 }
6461 else
6462 {
6463 if (aCpu < SchemaDefs::MaxCPUCount)
6464 *aAttached = mHWData->mCPUAttached[aCpu];
6465 }
6466
6467 return S_OK;
6468}
6469
6470HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6471{
6472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6473
6474 Utf8Str log = i_getLogFilename(aIdx);
6475 if (!RTFileExists(log.c_str()))
6476 log.setNull();
6477 aFilename = log;
6478
6479 return S_OK;
6480}
6481
6482HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6483{
6484 if (aSize < 0)
6485 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6486
6487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6488
6489 HRESULT rc = S_OK;
6490 Utf8Str log = i_getLogFilename(aIdx);
6491
6492 /* do not unnecessarily hold the lock while doing something which does
6493 * not need the lock and potentially takes a long time. */
6494 alock.release();
6495
6496 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6497 * keeps the SOAP reply size under 1M for the webservice (we're using
6498 * base64 encoded strings for binary data for years now, avoiding the
6499 * expansion of each byte array element to approx. 25 bytes of XML. */
6500 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6501 aData.resize(cbData);
6502
6503 RTFILE LogFile;
6504 int vrc = RTFileOpen(&LogFile, log.c_str(),
6505 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6506 if (RT_SUCCESS(vrc))
6507 {
6508 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6509 if (RT_SUCCESS(vrc))
6510 aData.resize(cbData);
6511 else
6512 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6513 tr("Could not read log file '%s' (%Rrc)"),
6514 log.c_str(), vrc);
6515 RTFileClose(LogFile);
6516 }
6517 else
6518 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6519 tr("Could not open log file '%s' (%Rrc)"),
6520 log.c_str(), vrc);
6521
6522 if (FAILED(rc))
6523 aData.resize(0);
6524
6525 return rc;
6526}
6527
6528
6529/**
6530 * Currently this method doesn't attach device to the running VM,
6531 * just makes sure it's plugged on next VM start.
6532 */
6533HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6534{
6535 // lock scope
6536 {
6537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 HRESULT rc = i_checkStateDependency(MutableStateDep);
6540 if (FAILED(rc)) return rc;
6541
6542 ChipsetType_T aChipset = ChipsetType_PIIX3;
6543 COMGETTER(ChipsetType)(&aChipset);
6544
6545 if (aChipset != ChipsetType_ICH9)
6546 {
6547 return setError(E_INVALIDARG,
6548 tr("Host PCI attachment only supported with ICH9 chipset"));
6549 }
6550
6551 // check if device with this host PCI address already attached
6552 for (HWData::PCIDeviceAssignmentList::const_iterator
6553 it = mHWData->mPCIDeviceAssignments.begin();
6554 it != mHWData->mPCIDeviceAssignments.end();
6555 ++it)
6556 {
6557 LONG iHostAddress = -1;
6558 ComPtr<PCIDeviceAttachment> pAttach;
6559 pAttach = *it;
6560 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6561 if (iHostAddress == aHostAddress)
6562 return setError(E_INVALIDARG,
6563 tr("Device with host PCI address already attached to this VM"));
6564 }
6565
6566 ComObjPtr<PCIDeviceAttachment> pda;
6567 char name[32];
6568
6569 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6570 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6571 pda.createObject();
6572 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6573 i_setModified(IsModified_MachineData);
6574 mHWData.backup();
6575 mHWData->mPCIDeviceAssignments.push_back(pda);
6576 }
6577
6578 return S_OK;
6579}
6580
6581/**
6582 * Currently this method doesn't detach device from the running VM,
6583 * just makes sure it's not plugged on next VM start.
6584 */
6585HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6586{
6587 ComObjPtr<PCIDeviceAttachment> pAttach;
6588 bool fRemoved = false;
6589 HRESULT rc;
6590
6591 // lock scope
6592 {
6593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6594
6595 rc = i_checkStateDependency(MutableStateDep);
6596 if (FAILED(rc)) return rc;
6597
6598 for (HWData::PCIDeviceAssignmentList::const_iterator
6599 it = mHWData->mPCIDeviceAssignments.begin();
6600 it != mHWData->mPCIDeviceAssignments.end();
6601 ++it)
6602 {
6603 LONG iHostAddress = -1;
6604 pAttach = *it;
6605 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6606 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6607 {
6608 i_setModified(IsModified_MachineData);
6609 mHWData.backup();
6610 mHWData->mPCIDeviceAssignments.remove(pAttach);
6611 fRemoved = true;
6612 break;
6613 }
6614 }
6615 }
6616
6617
6618 /* Fire event outside of the lock */
6619 if (fRemoved)
6620 {
6621 Assert(!pAttach.isNull());
6622 ComPtr<IEventSource> es;
6623 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6624 Assert(SUCCEEDED(rc));
6625 Bstr mid;
6626 rc = this->COMGETTER(Id)(mid.asOutParam());
6627 Assert(SUCCEEDED(rc));
6628 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6629 }
6630
6631 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6632 tr("No host PCI device %08x attached"),
6633 aHostAddress
6634 );
6635}
6636
6637HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6638{
6639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6640
6641 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6642 size_t i = 0;
6643 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6644 it = mHWData->mPCIDeviceAssignments.begin();
6645 it != mHWData->mPCIDeviceAssignments.end();
6646 ++it, ++i)
6647 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6648
6649 return S_OK;
6650}
6651
6652HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6653{
6654 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6655
6656 return S_OK;
6657}
6658
6659HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6660{
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662
6663 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6664
6665 return S_OK;
6666}
6667
6668HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6669{
6670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6671 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6672 if (SUCCEEDED(hrc))
6673 {
6674 hrc = mHWData.backupEx();
6675 if (SUCCEEDED(hrc))
6676 {
6677 i_setModified(IsModified_MachineData);
6678 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6679 }
6680 }
6681 return hrc;
6682}
6683
6684HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6685{
6686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6687 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6688 return S_OK;
6689}
6690
6691HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6692{
6693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6694 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6695 if (SUCCEEDED(hrc))
6696 {
6697 hrc = mHWData.backupEx();
6698 if (SUCCEEDED(hrc))
6699 {
6700 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6701 if (SUCCEEDED(hrc))
6702 i_setModified(IsModified_MachineData);
6703 }
6704 }
6705 return hrc;
6706}
6707
6708HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6709{
6710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6711
6712 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6713
6714 return S_OK;
6715}
6716
6717HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6718{
6719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6720 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6721 if (SUCCEEDED(hrc))
6722 {
6723 hrc = mHWData.backupEx();
6724 if (SUCCEEDED(hrc))
6725 {
6726 i_setModified(IsModified_MachineData);
6727 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6728 }
6729 }
6730 return hrc;
6731}
6732
6733HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6734{
6735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6736
6737 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6738
6739 return S_OK;
6740}
6741
6742HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6743{
6744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6745
6746 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6747 if ( SUCCEEDED(hrc)
6748 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6749 {
6750 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6751 int vrc;
6752
6753 if (aAutostartEnabled)
6754 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6755 else
6756 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6757
6758 if (RT_SUCCESS(vrc))
6759 {
6760 hrc = mHWData.backupEx();
6761 if (SUCCEEDED(hrc))
6762 {
6763 i_setModified(IsModified_MachineData);
6764 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6765 }
6766 }
6767 else if (vrc == VERR_NOT_SUPPORTED)
6768 hrc = setError(VBOX_E_NOT_SUPPORTED,
6769 tr("The VM autostart feature is not supported on this platform"));
6770 else if (vrc == VERR_PATH_NOT_FOUND)
6771 hrc = setError(E_FAIL,
6772 tr("The path to the autostart database is not set"));
6773 else
6774 hrc = setError(E_UNEXPECTED,
6775 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6776 aAutostartEnabled ? "Adding" : "Removing",
6777 mUserData->s.strName.c_str(), vrc);
6778 }
6779 return hrc;
6780}
6781
6782HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6783{
6784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6785
6786 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6787
6788 return S_OK;
6789}
6790
6791HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6792{
6793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6794 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6795 if (SUCCEEDED(hrc))
6796 {
6797 hrc = mHWData.backupEx();
6798 if (SUCCEEDED(hrc))
6799 {
6800 i_setModified(IsModified_MachineData);
6801 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6802 }
6803 }
6804 return hrc;
6805}
6806
6807HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6808{
6809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6810
6811 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6812
6813 return S_OK;
6814}
6815
6816HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6817{
6818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6819 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6820 if ( SUCCEEDED(hrc)
6821 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6822 {
6823 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6824 int vrc;
6825
6826 if (aAutostopType != AutostopType_Disabled)
6827 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6828 else
6829 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6830
6831 if (RT_SUCCESS(vrc))
6832 {
6833 hrc = mHWData.backupEx();
6834 if (SUCCEEDED(hrc))
6835 {
6836 i_setModified(IsModified_MachineData);
6837 mHWData->mAutostart.enmAutostopType = aAutostopType;
6838 }
6839 }
6840 else if (vrc == VERR_NOT_SUPPORTED)
6841 hrc = setError(VBOX_E_NOT_SUPPORTED,
6842 tr("The VM autostop feature is not supported on this platform"));
6843 else if (vrc == VERR_PATH_NOT_FOUND)
6844 hrc = setError(E_FAIL,
6845 tr("The path to the autostart database is not set"));
6846 else
6847 hrc = setError(E_UNEXPECTED,
6848 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6849 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6850 mUserData->s.strName.c_str(), vrc);
6851 }
6852 return hrc;
6853}
6854
6855HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6856{
6857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 aDefaultFrontend = mHWData->mDefaultFrontend;
6860
6861 return S_OK;
6862}
6863
6864HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6865{
6866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6867 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6868 if (SUCCEEDED(hrc))
6869 {
6870 hrc = mHWData.backupEx();
6871 if (SUCCEEDED(hrc))
6872 {
6873 i_setModified(IsModified_MachineData);
6874 mHWData->mDefaultFrontend = aDefaultFrontend;
6875 }
6876 }
6877 return hrc;
6878}
6879
6880HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6881{
6882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6883 size_t cbIcon = mUserData->s.ovIcon.size();
6884 aIcon.resize(cbIcon);
6885 if (cbIcon)
6886 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6887 return S_OK;
6888}
6889
6890HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6891{
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6894 if (SUCCEEDED(hrc))
6895 {
6896 i_setModified(IsModified_MachineData);
6897 mUserData.backup();
6898 size_t cbIcon = aIcon.size();
6899 mUserData->s.ovIcon.resize(cbIcon);
6900 if (cbIcon)
6901 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6902 }
6903 return hrc;
6904}
6905
6906HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6907{
6908#ifdef VBOX_WITH_USB
6909 *aUSBProxyAvailable = true;
6910#else
6911 *aUSBProxyAvailable = false;
6912#endif
6913 return S_OK;
6914}
6915
6916HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6917{
6918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6919
6920 *aVMProcessPriority = mUserData->s.enmVMPriority;
6921
6922 return S_OK;
6923}
6924
6925HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6926{
6927 RT_NOREF(aVMProcessPriority);
6928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6929 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6930 if (SUCCEEDED(hrc))
6931 {
6932 hrc = mUserData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 i_setModified(IsModified_MachineData);
6936 mUserData->s.enmVMPriority = aVMProcessPriority;
6937 }
6938 }
6939 alock.release();
6940 if (SUCCEEDED(hrc))
6941 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6942 return hrc;
6943}
6944
6945HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6946 ComPtr<IProgress> &aProgress)
6947{
6948 ComObjPtr<Progress> pP;
6949 Progress *ppP = pP;
6950 IProgress *iP = static_cast<IProgress *>(ppP);
6951 IProgress **pProgress = &iP;
6952
6953 IMachine *pTarget = aTarget;
6954
6955 /* Convert the options. */
6956 RTCList<CloneOptions_T> optList;
6957 if (aOptions.size())
6958 for (size_t i = 0; i < aOptions.size(); ++i)
6959 optList.append(aOptions[i]);
6960
6961 if (optList.contains(CloneOptions_Link))
6962 {
6963 if (!i_isSnapshotMachine())
6964 return setError(E_INVALIDARG,
6965 tr("Linked clone can only be created from a snapshot"));
6966 if (aMode != CloneMode_MachineState)
6967 return setError(E_INVALIDARG,
6968 tr("Linked clone can only be created for a single machine state"));
6969 }
6970 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6971
6972 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6973
6974 HRESULT rc = pWorker->start(pProgress);
6975
6976 pP = static_cast<Progress *>(*pProgress);
6977 pP.queryInterfaceTo(aProgress.asOutParam());
6978
6979 return rc;
6980
6981}
6982
6983HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6984 const com::Utf8Str &aType,
6985 ComPtr<IProgress> &aProgress)
6986{
6987 LogFlowThisFuncEnter();
6988
6989 ComObjPtr<Progress> ptrProgress;
6990 HRESULT hrc = ptrProgress.createObject();
6991 if (SUCCEEDED(hrc))
6992 {
6993 com::Utf8Str strDefaultPath;
6994 if (aTargetPath.isEmpty())
6995 i_calculateFullPath(".", strDefaultPath);
6996
6997 /* Initialize our worker task */
6998 MachineMoveVM *pTask = NULL;
6999 try
7000 {
7001 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7002 }
7003 catch (std::bad_alloc &)
7004 {
7005 return E_OUTOFMEMORY;
7006 }
7007
7008 hrc = pTask->init();//no exceptions are thrown
7009
7010 if (SUCCEEDED(hrc))
7011 {
7012 hrc = pTask->createThread();
7013 pTask = NULL; /* Consumed by createThread(). */
7014 if (SUCCEEDED(hrc))
7015 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7016 else
7017 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7018 }
7019 else
7020 delete pTask;
7021 }
7022
7023 LogFlowThisFuncLeave();
7024 return hrc;
7025
7026}
7027
7028HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7029{
7030 NOREF(aProgress);
7031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7032
7033 // This check should always fail.
7034 HRESULT rc = i_checkStateDependency(MutableStateDep);
7035 if (FAILED(rc)) return rc;
7036
7037 AssertFailedReturn(E_NOTIMPL);
7038}
7039
7040HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7041{
7042 NOREF(aSavedStateFile);
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044
7045 // This check should always fail.
7046 HRESULT rc = i_checkStateDependency(MutableStateDep);
7047 if (FAILED(rc)) return rc;
7048
7049 AssertFailedReturn(E_NOTIMPL);
7050}
7051
7052HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7053{
7054 NOREF(aFRemoveFile);
7055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7056
7057 // This check should always fail.
7058 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7059 if (FAILED(rc)) return rc;
7060
7061 AssertFailedReturn(E_NOTIMPL);
7062}
7063
7064// public methods for internal purposes
7065/////////////////////////////////////////////////////////////////////////////
7066
7067/**
7068 * Adds the given IsModified_* flag to the dirty flags of the machine.
7069 * This must be called either during i_loadSettings or under the machine write lock.
7070 * @param fl Flag
7071 * @param fAllowStateModification If state modifications are allowed.
7072 */
7073void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7074{
7075 mData->flModifications |= fl;
7076 if (fAllowStateModification && i_isStateModificationAllowed())
7077 mData->mCurrentStateModified = true;
7078}
7079
7080/**
7081 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7082 * care of the write locking.
7083 *
7084 * @param fModification The flag to add.
7085 * @param fAllowStateModification If state modifications are allowed.
7086 */
7087void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7088{
7089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7090 i_setModified(fModification, fAllowStateModification);
7091}
7092
7093/**
7094 * Saves the registry entry of this machine to the given configuration node.
7095 *
7096 * @param data Machine registry data.
7097 *
7098 * @note locks this object for reading.
7099 */
7100HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7101{
7102 AutoLimitedCaller autoCaller(this);
7103 AssertComRCReturnRC(autoCaller.rc());
7104
7105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7106
7107 data.uuid = mData->mUuid;
7108 data.strSettingsFile = mData->m_strConfigFile;
7109
7110 return S_OK;
7111}
7112
7113/**
7114 * Calculates the absolute path of the given path taking the directory of the
7115 * machine settings file as the current directory.
7116 *
7117 * @param strPath Path to calculate the absolute path for.
7118 * @param aResult Where to put the result (used only on success, can be the
7119 * same Utf8Str instance as passed in @a aPath).
7120 * @return IPRT result.
7121 *
7122 * @note Locks this object for reading.
7123 */
7124int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7125{
7126 AutoCaller autoCaller(this);
7127 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7132
7133 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7134
7135 strSettingsDir.stripFilename();
7136 char szFolder[RTPATH_MAX];
7137 size_t cbFolder = sizeof(szFolder);
7138 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7139 if (RT_SUCCESS(vrc))
7140 aResult = szFolder;
7141
7142 return vrc;
7143}
7144
7145/**
7146 * Copies strSource to strTarget, making it relative to the machine folder
7147 * if it is a subdirectory thereof, or simply copying it otherwise.
7148 *
7149 * @param strSource Path to evaluate and copy.
7150 * @param strTarget Buffer to receive target path.
7151 *
7152 * @note Locks this object for reading.
7153 */
7154void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7155 Utf8Str &strTarget)
7156{
7157 AutoCaller autoCaller(this);
7158 AssertComRCReturn(autoCaller.rc(), (void)0);
7159
7160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7161
7162 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7163 // use strTarget as a temporary buffer to hold the machine settings dir
7164 strTarget = mData->m_strConfigFileFull;
7165 strTarget.stripFilename();
7166 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7167 {
7168 // is relative: then append what's left
7169 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7170 // for empty paths (only possible for subdirs) use "." to avoid
7171 // triggering default settings for not present config attributes.
7172 if (strTarget.isEmpty())
7173 strTarget = ".";
7174 }
7175 else
7176 // is not relative: then overwrite
7177 strTarget = strSource;
7178}
7179
7180/**
7181 * Returns the full path to the machine's log folder in the
7182 * \a aLogFolder argument.
7183 */
7184void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7185{
7186 AutoCaller autoCaller(this);
7187 AssertComRCReturnVoid(autoCaller.rc());
7188
7189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 char szTmp[RTPATH_MAX];
7192 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7193 if (RT_SUCCESS(vrc))
7194 {
7195 if (szTmp[0] && !mUserData.isNull())
7196 {
7197 char szTmp2[RTPATH_MAX];
7198 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7199 if (RT_SUCCESS(vrc))
7200 aLogFolder.printf("%s%c%s",
7201 szTmp2,
7202 RTPATH_DELIMITER,
7203 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7204 }
7205 else
7206 vrc = VERR_PATH_IS_RELATIVE;
7207 }
7208
7209 if (RT_FAILURE(vrc))
7210 {
7211 // fallback if VBOX_USER_LOGHOME is not set or invalid
7212 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7213 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7214 aLogFolder.append(RTPATH_DELIMITER);
7215 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7216 }
7217}
7218
7219/**
7220 * Returns the full path to the machine's log file for an given index.
7221 */
7222Utf8Str Machine::i_getLogFilename(ULONG idx)
7223{
7224 Utf8Str logFolder;
7225 getLogFolder(logFolder);
7226 Assert(logFolder.length());
7227
7228 Utf8Str log;
7229 if (idx == 0)
7230 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7231#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7232 else if (idx == 1)
7233 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7234 else
7235 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7236#else
7237 else
7238 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7239#endif
7240 return log;
7241}
7242
7243/**
7244 * Returns the full path to the machine's hardened log file.
7245 */
7246Utf8Str Machine::i_getHardeningLogFilename(void)
7247{
7248 Utf8Str strFilename;
7249 getLogFolder(strFilename);
7250 Assert(strFilename.length());
7251 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7252 return strFilename;
7253}
7254
7255/**
7256 * Returns the default NVRAM filename based on the location of the VM config.
7257 * Note that this is a relative path.
7258 */
7259Utf8Str Machine::i_getDefaultNVRAMFilename()
7260{
7261 AutoCaller autoCaller(this);
7262 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7263
7264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7265
7266 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7267 || i_isSnapshotMachine())
7268 return Utf8Str::Empty;
7269
7270 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7271 strNVRAMFilePath.stripPath();
7272 strNVRAMFilePath.stripSuffix();
7273 strNVRAMFilePath += ".nvram";
7274
7275 return strNVRAMFilePath;
7276}
7277
7278/**
7279 * Returns the NVRAM filename for a new snapshot. This intentionally works
7280 * similarly to the saved state file naming. Note that this is usually
7281 * a relative path, unless the snapshot folder is absolute.
7282 */
7283Utf8Str Machine::i_getSnapshotNVRAMFilename()
7284{
7285 AutoCaller autoCaller(this);
7286 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7287
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289
7290 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7291 return Utf8Str::Empty;
7292
7293 RTTIMESPEC ts;
7294 RTTimeNow(&ts);
7295 RTTIME time;
7296 RTTimeExplode(&time, &ts);
7297
7298 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7299 strNVRAMFilePath += RTPATH_DELIMITER;
7300 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7301 time.i32Year, time.u8Month, time.u8MonthDay,
7302 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7303
7304 return strNVRAMFilePath;
7305}
7306
7307/**
7308 * Composes a unique saved state filename based on the current system time. The filename is
7309 * granular to the second so this will work so long as no more than one snapshot is taken on
7310 * a machine per second.
7311 *
7312 * Before version 4.1, we used this formula for saved state files:
7313 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7314 * which no longer works because saved state files can now be shared between the saved state of the
7315 * "saved" machine and an online snapshot, and the following would cause problems:
7316 * 1) save machine
7317 * 2) create online snapshot from that machine state --> reusing saved state file
7318 * 3) save machine again --> filename would be reused, breaking the online snapshot
7319 *
7320 * So instead we now use a timestamp.
7321 *
7322 * @param strStateFilePath
7323 */
7324
7325void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7326{
7327 AutoCaller autoCaller(this);
7328 AssertComRCReturnVoid(autoCaller.rc());
7329
7330 {
7331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7332 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7333 }
7334
7335 RTTIMESPEC ts;
7336 RTTimeNow(&ts);
7337 RTTIME time;
7338 RTTimeExplode(&time, &ts);
7339
7340 strStateFilePath += RTPATH_DELIMITER;
7341 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7342 time.i32Year, time.u8Month, time.u8MonthDay,
7343 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7344}
7345
7346/**
7347 * Returns whether at least one USB controller is present for the VM.
7348 */
7349bool Machine::i_isUSBControllerPresent()
7350{
7351 AutoCaller autoCaller(this);
7352 AssertComRCReturn(autoCaller.rc(), false);
7353
7354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7355
7356 return (mUSBControllers->size() > 0);
7357}
7358
7359
7360/**
7361 * @note Locks this object for writing, calls the client process
7362 * (inside the lock).
7363 */
7364HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7365 const Utf8Str &strFrontend,
7366 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7367 ProgressProxy *aProgress)
7368{
7369 LogFlowThisFuncEnter();
7370
7371 AssertReturn(aControl, E_FAIL);
7372 AssertReturn(aProgress, E_FAIL);
7373 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7374
7375 AutoCaller autoCaller(this);
7376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7377
7378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7379
7380 if (!mData->mRegistered)
7381 return setError(E_UNEXPECTED,
7382 tr("The machine '%s' is not registered"),
7383 mUserData->s.strName.c_str());
7384
7385 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7386
7387 /* The process started when launching a VM with separate UI/VM processes is always
7388 * the UI process, i.e. needs special handling as it won't claim the session. */
7389 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7390
7391 if (fSeparate)
7392 {
7393 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7394 return setError(VBOX_E_INVALID_OBJECT_STATE,
7395 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7396 mUserData->s.strName.c_str());
7397 }
7398 else
7399 {
7400 if ( mData->mSession.mState == SessionState_Locked
7401 || mData->mSession.mState == SessionState_Spawning
7402 || mData->mSession.mState == SessionState_Unlocking)
7403 return setError(VBOX_E_INVALID_OBJECT_STATE,
7404 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7405 mUserData->s.strName.c_str());
7406
7407 /* may not be busy */
7408 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7409 }
7410
7411 /* Hardening logging */
7412#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7413 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7414 {
7415 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7416 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7417 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7418 {
7419 Utf8Str strStartupLogDir = strHardeningLogFile;
7420 strStartupLogDir.stripFilename();
7421 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7422 file without stripping the file. */
7423 }
7424 strSupHardeningLogArg.append(strHardeningLogFile);
7425
7426 /* Remove legacy log filename to avoid confusion. */
7427 Utf8Str strOldStartupLogFile;
7428 getLogFolder(strOldStartupLogFile);
7429 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7430 RTFileDelete(strOldStartupLogFile.c_str());
7431 }
7432#else
7433 Utf8Str strSupHardeningLogArg;
7434#endif
7435
7436 Utf8Str strAppOverride;
7437#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7438 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7439#endif
7440
7441 bool fUseVBoxSDS = false;
7442 Utf8Str strCanonicalName;
7443 if (false)
7444 { }
7445#ifdef VBOX_WITH_QTGUI
7446 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7447 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7448 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7449 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7450 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7451 {
7452 strCanonicalName = "GUI/Qt";
7453 fUseVBoxSDS = true;
7454 }
7455#endif
7456#ifdef VBOX_WITH_VBOXSDL
7457 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7458 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7459 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7460 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7461 {
7462 strCanonicalName = "GUI/SDL";
7463 fUseVBoxSDS = true;
7464 }
7465#endif
7466#ifdef VBOX_WITH_HEADLESS
7467 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7468 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7469 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7470 {
7471 strCanonicalName = "headless";
7472 }
7473#endif
7474 else
7475 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7476
7477 Utf8Str idStr = mData->mUuid.toString();
7478 Utf8Str const &strMachineName = mUserData->s.strName;
7479 RTPROCESS pid = NIL_RTPROCESS;
7480
7481#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7482 RT_NOREF(fUseVBoxSDS);
7483#else
7484 DWORD idCallerSession = ~(DWORD)0;
7485 if (fUseVBoxSDS)
7486 {
7487 /*
7488 * The VBoxSDS should be used for process launching the VM with
7489 * GUI only if the caller and the VBoxSDS are in different Windows
7490 * sessions and the caller in the interactive one.
7491 */
7492 fUseVBoxSDS = false;
7493
7494 /* Get windows session of the current process. The process token used
7495 due to several reasons:
7496 1. The token is absent for the current thread except someone set it
7497 for us.
7498 2. Needs to get the id of the session where the process is started.
7499 We only need to do this once, though. */
7500 static DWORD s_idCurrentSession = ~(DWORD)0;
7501 DWORD idCurrentSession = s_idCurrentSession;
7502 if (idCurrentSession == ~(DWORD)0)
7503 {
7504 HANDLE hCurrentProcessToken = NULL;
7505 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7506 {
7507 DWORD cbIgn = 0;
7508 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7509 s_idCurrentSession = idCurrentSession;
7510 else
7511 {
7512 idCurrentSession = ~(DWORD)0;
7513 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7514 }
7515 CloseHandle(hCurrentProcessToken);
7516 }
7517 else
7518 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7519 }
7520
7521 /* get the caller's session */
7522 HRESULT hrc = CoImpersonateClient();
7523 if (SUCCEEDED(hrc))
7524 {
7525 HANDLE hCallerThreadToken;
7526 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7527 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7528 &hCallerThreadToken))
7529 {
7530 SetLastError(NO_ERROR);
7531 DWORD cbIgn = 0;
7532 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7533 {
7534 /* Only need to use SDS if the session ID differs: */
7535 if (idCurrentSession != idCallerSession)
7536 {
7537 fUseVBoxSDS = false;
7538
7539 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7540 DWORD cbTokenGroups = 0;
7541 PTOKEN_GROUPS pTokenGroups = NULL;
7542 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7543 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7544 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7545 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7546 {
7547 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7548 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7549 PSID pInteractiveSid = NULL;
7550 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7551 {
7552 /* Iterate over the groups looking for the interactive SID: */
7553 fUseVBoxSDS = false;
7554 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7555 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7556 {
7557 fUseVBoxSDS = true;
7558 break;
7559 }
7560 FreeSid(pInteractiveSid);
7561 }
7562 }
7563 else
7564 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7565 RTMemTmpFree(pTokenGroups);
7566 }
7567 }
7568 else
7569 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7570 CloseHandle(hCallerThreadToken);
7571 }
7572 else
7573 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7574 CoRevertToSelf();
7575 }
7576 else
7577 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7578 }
7579 if (fUseVBoxSDS)
7580 {
7581 /* connect to VBoxSDS */
7582 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7583 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7584 if (FAILED(rc))
7585 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7586 strMachineName.c_str());
7587
7588 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7589 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7590 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7591 service to access the files. */
7592 rc = CoSetProxyBlanket(pVBoxSDS,
7593 RPC_C_AUTHN_DEFAULT,
7594 RPC_C_AUTHZ_DEFAULT,
7595 COLE_DEFAULT_PRINCIPAL,
7596 RPC_C_AUTHN_LEVEL_DEFAULT,
7597 RPC_C_IMP_LEVEL_IMPERSONATE,
7598 NULL,
7599 EOAC_DEFAULT);
7600 if (FAILED(rc))
7601 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7602
7603 size_t const cEnvVars = aEnvironmentChanges.size();
7604 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7605 for (size_t i = 0; i < cEnvVars; i++)
7606 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7607
7608 ULONG uPid = 0;
7609 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7610 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7611 idCallerSession, &uPid);
7612 if (FAILED(rc))
7613 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7614 pid = (RTPROCESS)uPid;
7615 }
7616 else
7617#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7618 {
7619 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7620 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7621 if (RT_FAILURE(vrc))
7622 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7623 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7624 }
7625
7626 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7627 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7628
7629 if (!fSeparate)
7630 {
7631 /*
7632 * Note that we don't release the lock here before calling the client,
7633 * because it doesn't need to call us back if called with a NULL argument.
7634 * Releasing the lock here is dangerous because we didn't prepare the
7635 * launch data yet, but the client we've just started may happen to be
7636 * too fast and call LockMachine() that will fail (because of PID, etc.),
7637 * so that the Machine will never get out of the Spawning session state.
7638 */
7639
7640 /* inform the session that it will be a remote one */
7641 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7642#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7643 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7644#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7645 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7646#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7647 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7648
7649 if (FAILED(rc))
7650 {
7651 /* restore the session state */
7652 mData->mSession.mState = SessionState_Unlocked;
7653 alock.release();
7654 mParent->i_addProcessToReap(pid);
7655 /* The failure may occur w/o any error info (from RPC), so provide one */
7656 return setError(VBOX_E_VM_ERROR,
7657 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7658 }
7659
7660 /* attach launch data to the machine */
7661 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7662 mData->mSession.mRemoteControls.push_back(aControl);
7663 mData->mSession.mProgress = aProgress;
7664 mData->mSession.mPID = pid;
7665 mData->mSession.mState = SessionState_Spawning;
7666 Assert(strCanonicalName.isNotEmpty());
7667 mData->mSession.mName = strCanonicalName;
7668 }
7669 else
7670 {
7671 /* For separate UI process we declare the launch as completed instantly, as the
7672 * actual headless VM start may or may not come. No point in remembering anything
7673 * yet, as what matters for us is when the headless VM gets started. */
7674 aProgress->i_notifyComplete(S_OK);
7675 }
7676
7677 alock.release();
7678 mParent->i_addProcessToReap(pid);
7679
7680 LogFlowThisFuncLeave();
7681 return S_OK;
7682}
7683
7684/**
7685 * Returns @c true if the given session machine instance has an open direct
7686 * session (and optionally also for direct sessions which are closing) and
7687 * returns the session control machine instance if so.
7688 *
7689 * Note that when the method returns @c false, the arguments remain unchanged.
7690 *
7691 * @param aMachine Session machine object.
7692 * @param aControl Direct session control object (optional).
7693 * @param aRequireVM If true then only allow VM sessions.
7694 * @param aAllowClosing If true then additionally a session which is currently
7695 * being closed will also be allowed.
7696 *
7697 * @note locks this object for reading.
7698 */
7699bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7700 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7701 bool aRequireVM /*= false*/,
7702 bool aAllowClosing /*= false*/)
7703{
7704 AutoLimitedCaller autoCaller(this);
7705 AssertComRCReturn(autoCaller.rc(), false);
7706
7707 /* just return false for inaccessible machines */
7708 if (getObjectState().getState() != ObjectState::Ready)
7709 return false;
7710
7711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7712
7713 if ( ( mData->mSession.mState == SessionState_Locked
7714 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7715 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7716 )
7717 {
7718 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7719
7720 aMachine = mData->mSession.mMachine;
7721
7722 if (aControl != NULL)
7723 *aControl = mData->mSession.mDirectControl;
7724
7725 return true;
7726 }
7727
7728 return false;
7729}
7730
7731/**
7732 * Returns @c true if the given machine has an spawning direct session.
7733 *
7734 * @note locks this object for reading.
7735 */
7736bool Machine::i_isSessionSpawning()
7737{
7738 AutoLimitedCaller autoCaller(this);
7739 AssertComRCReturn(autoCaller.rc(), false);
7740
7741 /* just return false for inaccessible machines */
7742 if (getObjectState().getState() != ObjectState::Ready)
7743 return false;
7744
7745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7746
7747 if (mData->mSession.mState == SessionState_Spawning)
7748 return true;
7749
7750 return false;
7751}
7752
7753/**
7754 * Called from the client watcher thread to check for unexpected client process
7755 * death during Session_Spawning state (e.g. before it successfully opened a
7756 * direct session).
7757 *
7758 * On Win32 and on OS/2, this method is called only when we've got the
7759 * direct client's process termination notification, so it always returns @c
7760 * true.
7761 *
7762 * On other platforms, this method returns @c true if the client process is
7763 * terminated and @c false if it's still alive.
7764 *
7765 * @note Locks this object for writing.
7766 */
7767bool Machine::i_checkForSpawnFailure()
7768{
7769 AutoCaller autoCaller(this);
7770 if (!autoCaller.isOk())
7771 {
7772 /* nothing to do */
7773 LogFlowThisFunc(("Already uninitialized!\n"));
7774 return true;
7775 }
7776
7777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7778
7779 if (mData->mSession.mState != SessionState_Spawning)
7780 {
7781 /* nothing to do */
7782 LogFlowThisFunc(("Not spawning any more!\n"));
7783 return true;
7784 }
7785
7786 HRESULT rc = S_OK;
7787
7788 /* PID not yet initialized, skip check. */
7789 if (mData->mSession.mPID == NIL_RTPROCESS)
7790 return false;
7791
7792 RTPROCSTATUS status;
7793 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7794
7795 if (vrc != VERR_PROCESS_RUNNING)
7796 {
7797 Utf8Str strExtraInfo;
7798
7799#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7800 /* If the startup logfile exists and is of non-zero length, tell the
7801 user to look there for more details to encourage them to attach it
7802 when reporting startup issues. */
7803 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7804 uint64_t cbStartupLogFile = 0;
7805 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7806 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7807 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7808#endif
7809
7810 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7811 rc = setError(E_FAIL,
7812 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7813 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7814 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7815 rc = setError(E_FAIL,
7816 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7817 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7818 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7819 rc = setError(E_FAIL,
7820 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7821 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7822 else
7823 rc = setErrorBoth(E_FAIL, vrc,
7824 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7825 i_getName().c_str(), vrc, strExtraInfo.c_str());
7826 }
7827
7828 if (FAILED(rc))
7829 {
7830 /* Close the remote session, remove the remote control from the list
7831 * and reset session state to Closed (@note keep the code in sync with
7832 * the relevant part in LockMachine()). */
7833
7834 Assert(mData->mSession.mRemoteControls.size() == 1);
7835 if (mData->mSession.mRemoteControls.size() == 1)
7836 {
7837 ErrorInfoKeeper eik;
7838 mData->mSession.mRemoteControls.front()->Uninitialize();
7839 }
7840
7841 mData->mSession.mRemoteControls.clear();
7842 mData->mSession.mState = SessionState_Unlocked;
7843
7844 /* finalize the progress after setting the state */
7845 if (!mData->mSession.mProgress.isNull())
7846 {
7847 mData->mSession.mProgress->notifyComplete(rc);
7848 mData->mSession.mProgress.setNull();
7849 }
7850
7851 mData->mSession.mPID = NIL_RTPROCESS;
7852
7853 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7854 return true;
7855 }
7856
7857 return false;
7858}
7859
7860/**
7861 * Checks whether the machine can be registered. If so, commits and saves
7862 * all settings.
7863 *
7864 * @note Must be called from mParent's write lock. Locks this object and
7865 * children for writing.
7866 */
7867HRESULT Machine::i_prepareRegister()
7868{
7869 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7870
7871 AutoLimitedCaller autoCaller(this);
7872 AssertComRCReturnRC(autoCaller.rc());
7873
7874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7875
7876 /* wait for state dependents to drop to zero */
7877 i_ensureNoStateDependencies();
7878
7879 if (!mData->mAccessible)
7880 return setError(VBOX_E_INVALID_OBJECT_STATE,
7881 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7882 mUserData->s.strName.c_str(),
7883 mData->mUuid.toString().c_str());
7884
7885 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7886
7887 if (mData->mRegistered)
7888 return setError(VBOX_E_INVALID_OBJECT_STATE,
7889 tr("The machine '%s' with UUID {%s} is already registered"),
7890 mUserData->s.strName.c_str(),
7891 mData->mUuid.toString().c_str());
7892
7893 HRESULT rc = S_OK;
7894
7895 // Ensure the settings are saved. If we are going to be registered and
7896 // no config file exists yet, create it by calling i_saveSettings() too.
7897 if ( (mData->flModifications)
7898 || (!mData->pMachineConfigFile->fileExists())
7899 )
7900 {
7901 rc = i_saveSettings(NULL);
7902 // no need to check whether VirtualBox.xml needs saving too since
7903 // we can't have a machine XML file rename pending
7904 if (FAILED(rc)) return rc;
7905 }
7906
7907 /* more config checking goes here */
7908
7909 if (SUCCEEDED(rc))
7910 {
7911 /* we may have had implicit modifications we want to fix on success */
7912 i_commit();
7913
7914 mData->mRegistered = true;
7915 }
7916 else
7917 {
7918 /* we may have had implicit modifications we want to cancel on failure*/
7919 i_rollback(false /* aNotify */);
7920 }
7921
7922 return rc;
7923}
7924
7925/**
7926 * Increases the number of objects dependent on the machine state or on the
7927 * registered state. Guarantees that these two states will not change at least
7928 * until #i_releaseStateDependency() is called.
7929 *
7930 * Depending on the @a aDepType value, additional state checks may be made.
7931 * These checks will set extended error info on failure. See
7932 * #i_checkStateDependency() for more info.
7933 *
7934 * If this method returns a failure, the dependency is not added and the caller
7935 * is not allowed to rely on any particular machine state or registration state
7936 * value and may return the failed result code to the upper level.
7937 *
7938 * @param aDepType Dependency type to add.
7939 * @param aState Current machine state (NULL if not interested).
7940 * @param aRegistered Current registered state (NULL if not interested).
7941 *
7942 * @note Locks this object for writing.
7943 */
7944HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7945 MachineState_T *aState /* = NULL */,
7946 BOOL *aRegistered /* = NULL */)
7947{
7948 AutoCaller autoCaller(this);
7949 AssertComRCReturnRC(autoCaller.rc());
7950
7951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7952
7953 HRESULT rc = i_checkStateDependency(aDepType);
7954 if (FAILED(rc)) return rc;
7955
7956 {
7957 if (mData->mMachineStateChangePending != 0)
7958 {
7959 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7960 * drop to zero so don't add more. It may make sense to wait a bit
7961 * and retry before reporting an error (since the pending state
7962 * transition should be really quick) but let's just assert for
7963 * now to see if it ever happens on practice. */
7964
7965 AssertFailed();
7966
7967 return setError(E_ACCESSDENIED,
7968 tr("Machine state change is in progress. Please retry the operation later."));
7969 }
7970
7971 ++mData->mMachineStateDeps;
7972 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7973 }
7974
7975 if (aState)
7976 *aState = mData->mMachineState;
7977 if (aRegistered)
7978 *aRegistered = mData->mRegistered;
7979
7980 return S_OK;
7981}
7982
7983/**
7984 * Decreases the number of objects dependent on the machine state.
7985 * Must always complete the #i_addStateDependency() call after the state
7986 * dependency is no more necessary.
7987 */
7988void Machine::i_releaseStateDependency()
7989{
7990 AutoCaller autoCaller(this);
7991 AssertComRCReturnVoid(autoCaller.rc());
7992
7993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7994
7995 /* releaseStateDependency() w/o addStateDependency()? */
7996 AssertReturnVoid(mData->mMachineStateDeps != 0);
7997 -- mData->mMachineStateDeps;
7998
7999 if (mData->mMachineStateDeps == 0)
8000 {
8001 /* inform i_ensureNoStateDependencies() that there are no more deps */
8002 if (mData->mMachineStateChangePending != 0)
8003 {
8004 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8005 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8006 }
8007 }
8008}
8009
8010Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8011{
8012 /* start with nothing found */
8013 Utf8Str strResult("");
8014
8015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8016
8017 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8018 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8019 // found:
8020 strResult = it->second; // source is a Utf8Str
8021
8022 return strResult;
8023}
8024
8025// protected methods
8026/////////////////////////////////////////////////////////////////////////////
8027
8028/**
8029 * Performs machine state checks based on the @a aDepType value. If a check
8030 * fails, this method will set extended error info, otherwise it will return
8031 * S_OK. It is supposed, that on failure, the caller will immediately return
8032 * the return value of this method to the upper level.
8033 *
8034 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8035 *
8036 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8037 * current state of this machine object allows to change settings of the
8038 * machine (i.e. the machine is not registered, or registered but not running
8039 * and not saved). It is useful to call this method from Machine setters
8040 * before performing any change.
8041 *
8042 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8043 * as for MutableStateDep except that if the machine is saved, S_OK is also
8044 * returned. This is useful in setters which allow changing machine
8045 * properties when it is in the saved state.
8046 *
8047 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8048 * if the current state of this machine object allows to change runtime
8049 * changeable settings of the machine (i.e. the machine is not registered, or
8050 * registered but either running or not running and not saved). It is useful
8051 * to call this method from Machine setters before performing any changes to
8052 * runtime changeable settings.
8053 *
8054 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8055 * the same as for MutableOrRunningStateDep except that if the machine is
8056 * saved, S_OK is also returned. This is useful in setters which allow
8057 * changing runtime and saved state changeable machine properties.
8058 *
8059 * @param aDepType Dependency type to check.
8060 *
8061 * @note Non Machine based classes should use #i_addStateDependency() and
8062 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8063 * template.
8064 *
8065 * @note This method must be called from under this object's read or write
8066 * lock.
8067 */
8068HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8069{
8070 switch (aDepType)
8071 {
8072 case AnyStateDep:
8073 {
8074 break;
8075 }
8076 case MutableStateDep:
8077 {
8078 if ( mData->mRegistered
8079 && ( !i_isSessionMachine()
8080 || ( mData->mMachineState != MachineState_Aborted
8081 && mData->mMachineState != MachineState_Teleported
8082 && mData->mMachineState != MachineState_PoweredOff
8083 )
8084 )
8085 )
8086 return setError(VBOX_E_INVALID_VM_STATE,
8087 tr("The machine is not mutable (state is %s)"),
8088 Global::stringifyMachineState(mData->mMachineState));
8089 break;
8090 }
8091 case MutableOrSavedStateDep:
8092 {
8093 if ( mData->mRegistered
8094 && ( !i_isSessionMachine()
8095 || ( mData->mMachineState != MachineState_Aborted
8096 && mData->mMachineState != MachineState_Teleported
8097 && mData->mMachineState != MachineState_Saved
8098 && mData->mMachineState != MachineState_PoweredOff
8099 )
8100 )
8101 )
8102 return setError(VBOX_E_INVALID_VM_STATE,
8103 tr("The machine is not mutable or saved (state is %s)"),
8104 Global::stringifyMachineState(mData->mMachineState));
8105 break;
8106 }
8107 case MutableOrRunningStateDep:
8108 {
8109 if ( mData->mRegistered
8110 && ( !i_isSessionMachine()
8111 || ( mData->mMachineState != MachineState_Aborted
8112 && mData->mMachineState != MachineState_Teleported
8113 && mData->mMachineState != MachineState_PoweredOff
8114 && !Global::IsOnline(mData->mMachineState)
8115 )
8116 )
8117 )
8118 return setError(VBOX_E_INVALID_VM_STATE,
8119 tr("The machine is not mutable or running (state is %s)"),
8120 Global::stringifyMachineState(mData->mMachineState));
8121 break;
8122 }
8123 case MutableOrSavedOrRunningStateDep:
8124 {
8125 if ( mData->mRegistered
8126 && ( !i_isSessionMachine()
8127 || ( mData->mMachineState != MachineState_Aborted
8128 && mData->mMachineState != MachineState_Teleported
8129 && mData->mMachineState != MachineState_Saved
8130 && mData->mMachineState != MachineState_PoweredOff
8131 && !Global::IsOnline(mData->mMachineState)
8132 )
8133 )
8134 )
8135 return setError(VBOX_E_INVALID_VM_STATE,
8136 tr("The machine is not mutable, saved or running (state is %s)"),
8137 Global::stringifyMachineState(mData->mMachineState));
8138 break;
8139 }
8140 }
8141
8142 return S_OK;
8143}
8144
8145/**
8146 * Helper to initialize all associated child objects and allocate data
8147 * structures.
8148 *
8149 * This method must be called as a part of the object's initialization procedure
8150 * (usually done in the #init() method).
8151 *
8152 * @note Must be called only from #init() or from #i_registeredInit().
8153 */
8154HRESULT Machine::initDataAndChildObjects()
8155{
8156 AutoCaller autoCaller(this);
8157 AssertComRCReturnRC(autoCaller.rc());
8158 AssertReturn( getObjectState().getState() == ObjectState::InInit
8159 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8160
8161 AssertReturn(!mData->mAccessible, E_FAIL);
8162
8163 /* allocate data structures */
8164 mSSData.allocate();
8165 mUserData.allocate();
8166 mHWData.allocate();
8167 mMediumAttachments.allocate();
8168 mStorageControllers.allocate();
8169 mUSBControllers.allocate();
8170
8171 /* initialize mOSTypeId */
8172 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8173
8174/** @todo r=bird: init() methods never fails, right? Why don't we make them
8175 * return void then! */
8176
8177 /* create associated BIOS settings object */
8178 unconst(mBIOSSettings).createObject();
8179 mBIOSSettings->init(this);
8180
8181 /* create associated trusted platform module object */
8182 unconst(mTrustedPlatformModule).createObject();
8183 mTrustedPlatformModule->init(this);
8184
8185 /* create associated NVRAM store object */
8186 unconst(mNvramStore).createObject();
8187 mNvramStore->init(this);
8188
8189 /* create associated record settings object */
8190 unconst(mRecordingSettings).createObject();
8191 mRecordingSettings->init(this);
8192
8193 /* create the graphics adapter object (always present) */
8194 unconst(mGraphicsAdapter).createObject();
8195 mGraphicsAdapter->init(this);
8196
8197 /* create an associated VRDE object (default is disabled) */
8198 unconst(mVRDEServer).createObject();
8199 mVRDEServer->init(this);
8200
8201 /* create associated serial port objects */
8202 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8203 {
8204 unconst(mSerialPorts[slot]).createObject();
8205 mSerialPorts[slot]->init(this, slot);
8206 }
8207
8208 /* create associated parallel port objects */
8209 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8210 {
8211 unconst(mParallelPorts[slot]).createObject();
8212 mParallelPorts[slot]->init(this, slot);
8213 }
8214
8215 /* create the audio adapter object (always present, default is disabled) */
8216 unconst(mAudioAdapter).createObject();
8217 mAudioAdapter->init(this);
8218
8219 /* create the USB device filters object (always present) */
8220 unconst(mUSBDeviceFilters).createObject();
8221 mUSBDeviceFilters->init(this);
8222
8223 /* create associated network adapter objects */
8224 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8225 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8226 {
8227 unconst(mNetworkAdapters[slot]).createObject();
8228 mNetworkAdapters[slot]->init(this, slot);
8229 }
8230
8231 /* create the bandwidth control */
8232 unconst(mBandwidthControl).createObject();
8233 mBandwidthControl->init(this);
8234
8235 return S_OK;
8236}
8237
8238/**
8239 * Helper to uninitialize all associated child objects and to free all data
8240 * structures.
8241 *
8242 * This method must be called as a part of the object's uninitialization
8243 * procedure (usually done in the #uninit() method).
8244 *
8245 * @note Must be called only from #uninit() or from #i_registeredInit().
8246 */
8247void Machine::uninitDataAndChildObjects()
8248{
8249 AutoCaller autoCaller(this);
8250 AssertComRCReturnVoid(autoCaller.rc());
8251 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8252 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8253 || getObjectState().getState() == ObjectState::InUninit
8254 || getObjectState().getState() == ObjectState::Limited);
8255
8256 /* tell all our other child objects we've been uninitialized */
8257 if (mBandwidthControl)
8258 {
8259 mBandwidthControl->uninit();
8260 unconst(mBandwidthControl).setNull();
8261 }
8262
8263 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8264 {
8265 if (mNetworkAdapters[slot])
8266 {
8267 mNetworkAdapters[slot]->uninit();
8268 unconst(mNetworkAdapters[slot]).setNull();
8269 }
8270 }
8271
8272 if (mUSBDeviceFilters)
8273 {
8274 mUSBDeviceFilters->uninit();
8275 unconst(mUSBDeviceFilters).setNull();
8276 }
8277
8278 if (mAudioAdapter)
8279 {
8280 mAudioAdapter->uninit();
8281 unconst(mAudioAdapter).setNull();
8282 }
8283
8284 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8285 {
8286 if (mParallelPorts[slot])
8287 {
8288 mParallelPorts[slot]->uninit();
8289 unconst(mParallelPorts[slot]).setNull();
8290 }
8291 }
8292
8293 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8294 {
8295 if (mSerialPorts[slot])
8296 {
8297 mSerialPorts[slot]->uninit();
8298 unconst(mSerialPorts[slot]).setNull();
8299 }
8300 }
8301
8302 if (mVRDEServer)
8303 {
8304 mVRDEServer->uninit();
8305 unconst(mVRDEServer).setNull();
8306 }
8307
8308 if (mGraphicsAdapter)
8309 {
8310 mGraphicsAdapter->uninit();
8311 unconst(mGraphicsAdapter).setNull();
8312 }
8313
8314 if (mBIOSSettings)
8315 {
8316 mBIOSSettings->uninit();
8317 unconst(mBIOSSettings).setNull();
8318 }
8319
8320 if (mTrustedPlatformModule)
8321 {
8322 mTrustedPlatformModule->uninit();
8323 unconst(mTrustedPlatformModule).setNull();
8324 }
8325
8326 if (mNvramStore)
8327 {
8328 mNvramStore->uninit();
8329 unconst(mNvramStore).setNull();
8330 }
8331
8332 if (mRecordingSettings)
8333 {
8334 mRecordingSettings->uninit();
8335 unconst(mRecordingSettings).setNull();
8336 }
8337
8338 /* Deassociate media (only when a real Machine or a SnapshotMachine
8339 * instance is uninitialized; SessionMachine instances refer to real
8340 * Machine media). This is necessary for a clean re-initialization of
8341 * the VM after successfully re-checking the accessibility state. Note
8342 * that in case of normal Machine or SnapshotMachine uninitialization (as
8343 * a result of unregistering or deleting the snapshot), outdated media
8344 * attachments will already be uninitialized and deleted, so this
8345 * code will not affect them. */
8346 if ( !mMediumAttachments.isNull()
8347 && !i_isSessionMachine()
8348 )
8349 {
8350 for (MediumAttachmentList::const_iterator
8351 it = mMediumAttachments->begin();
8352 it != mMediumAttachments->end();
8353 ++it)
8354 {
8355 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8356 if (pMedium.isNull())
8357 continue;
8358 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8359 AssertComRC(rc);
8360 }
8361 }
8362
8363 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8364 {
8365 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8366 if (mData->mFirstSnapshot)
8367 {
8368 // snapshots tree is protected by machine write lock; strictly
8369 // this isn't necessary here since we're deleting the entire
8370 // machine, but otherwise we assert in Snapshot::uninit()
8371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8372 mData->mFirstSnapshot->uninit();
8373 mData->mFirstSnapshot.setNull();
8374 }
8375
8376 mData->mCurrentSnapshot.setNull();
8377 }
8378
8379 /* free data structures (the essential mData structure is not freed here
8380 * since it may be still in use) */
8381 mMediumAttachments.free();
8382 mStorageControllers.free();
8383 mUSBControllers.free();
8384 mHWData.free();
8385 mUserData.free();
8386 mSSData.free();
8387}
8388
8389/**
8390 * Returns a pointer to the Machine object for this machine that acts like a
8391 * parent for complex machine data objects such as shared folders, etc.
8392 *
8393 * For primary Machine objects and for SnapshotMachine objects, returns this
8394 * object's pointer itself. For SessionMachine objects, returns the peer
8395 * (primary) machine pointer.
8396 */
8397Machine *Machine::i_getMachine()
8398{
8399 if (i_isSessionMachine())
8400 return (Machine*)mPeer;
8401 return this;
8402}
8403
8404/**
8405 * Makes sure that there are no machine state dependents. If necessary, waits
8406 * for the number of dependents to drop to zero.
8407 *
8408 * Make sure this method is called from under this object's write lock to
8409 * guarantee that no new dependents may be added when this method returns
8410 * control to the caller.
8411 *
8412 * @note Locks this object for writing. The lock will be released while waiting
8413 * (if necessary).
8414 *
8415 * @warning To be used only in methods that change the machine state!
8416 */
8417void Machine::i_ensureNoStateDependencies()
8418{
8419 AssertReturnVoid(isWriteLockOnCurrentThread());
8420
8421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8422
8423 /* Wait for all state dependents if necessary */
8424 if (mData->mMachineStateDeps != 0)
8425 {
8426 /* lazy semaphore creation */
8427 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8428 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8429
8430 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8431 mData->mMachineStateDeps));
8432
8433 ++mData->mMachineStateChangePending;
8434
8435 /* reset the semaphore before waiting, the last dependent will signal
8436 * it */
8437 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8438
8439 alock.release();
8440
8441 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8442
8443 alock.acquire();
8444
8445 -- mData->mMachineStateChangePending;
8446 }
8447}
8448
8449/**
8450 * Changes the machine state and informs callbacks.
8451 *
8452 * This method is not intended to fail so it either returns S_OK or asserts (and
8453 * returns a failure).
8454 *
8455 * @note Locks this object for writing.
8456 */
8457HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8458{
8459 LogFlowThisFuncEnter();
8460 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8461 Assert(aMachineState != MachineState_Null);
8462
8463 AutoCaller autoCaller(this);
8464 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8465
8466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8467
8468 /* wait for state dependents to drop to zero */
8469 i_ensureNoStateDependencies();
8470
8471 MachineState_T const enmOldState = mData->mMachineState;
8472 if (enmOldState != aMachineState)
8473 {
8474 mData->mMachineState = aMachineState;
8475 RTTimeNow(&mData->mLastStateChange);
8476
8477#ifdef VBOX_WITH_DTRACE_R3_MAIN
8478 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8479#endif
8480 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8481 }
8482
8483 LogFlowThisFuncLeave();
8484 return S_OK;
8485}
8486
8487/**
8488 * Searches for a shared folder with the given logical name
8489 * in the collection of shared folders.
8490 *
8491 * @param aName logical name of the shared folder
8492 * @param aSharedFolder where to return the found object
8493 * @param aSetError whether to set the error info if the folder is
8494 * not found
8495 * @return
8496 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8497 *
8498 * @note
8499 * must be called from under the object's lock!
8500 */
8501HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8502 ComObjPtr<SharedFolder> &aSharedFolder,
8503 bool aSetError /* = false */)
8504{
8505 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8506 for (HWData::SharedFolderList::const_iterator
8507 it = mHWData->mSharedFolders.begin();
8508 it != mHWData->mSharedFolders.end();
8509 ++it)
8510 {
8511 SharedFolder *pSF = *it;
8512 AutoCaller autoCaller(pSF);
8513 if (pSF->i_getName() == aName)
8514 {
8515 aSharedFolder = pSF;
8516 rc = S_OK;
8517 break;
8518 }
8519 }
8520
8521 if (aSetError && FAILED(rc))
8522 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8523
8524 return rc;
8525}
8526
8527/**
8528 * Initializes all machine instance data from the given settings structures
8529 * from XML. The exception is the machine UUID which needs special handling
8530 * depending on the caller's use case, so the caller needs to set that herself.
8531 *
8532 * This gets called in several contexts during machine initialization:
8533 *
8534 * -- When machine XML exists on disk already and needs to be loaded into memory,
8535 * for example, from #i_registeredInit() to load all registered machines on
8536 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8537 * attached to the machine should be part of some media registry already.
8538 *
8539 * -- During OVF import, when a machine config has been constructed from an
8540 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8541 * ensure that the media listed as attachments in the config (which have
8542 * been imported from the OVF) receive the correct registry ID.
8543 *
8544 * -- During VM cloning.
8545 *
8546 * @param config Machine settings from XML.
8547 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8548 * for each attached medium in the config.
8549 * @return
8550 */
8551HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8552 const Guid *puuidRegistry)
8553{
8554 // copy name, description, OS type, teleporter, UTC etc.
8555 mUserData->s = config.machineUserData;
8556
8557 // look up the object by Id to check it is valid
8558 ComObjPtr<GuestOSType> pGuestOSType;
8559 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8560 if (!pGuestOSType.isNull())
8561 mUserData->s.strOsType = pGuestOSType->i_id();
8562
8563 // stateFile (optional)
8564 if (config.strStateFile.isEmpty())
8565 mSSData->strStateFilePath.setNull();
8566 else
8567 {
8568 Utf8Str stateFilePathFull(config.strStateFile);
8569 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8570 if (RT_FAILURE(vrc))
8571 return setErrorBoth(E_FAIL, vrc,
8572 tr("Invalid saved state file path '%s' (%Rrc)"),
8573 config.strStateFile.c_str(),
8574 vrc);
8575 mSSData->strStateFilePath = stateFilePathFull;
8576 }
8577
8578 // snapshot folder needs special processing so set it again
8579 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8580 if (FAILED(rc)) return rc;
8581
8582 /* Copy the extra data items (config may or may not be the same as
8583 * mData->pMachineConfigFile) if necessary. When loading the XML files
8584 * from disk they are the same, but not for OVF import. */
8585 if (mData->pMachineConfigFile != &config)
8586 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8587
8588 /* currentStateModified (optional, default is true) */
8589 mData->mCurrentStateModified = config.fCurrentStateModified;
8590
8591 mData->mLastStateChange = config.timeLastStateChange;
8592
8593 /*
8594 * note: all mUserData members must be assigned prior this point because
8595 * we need to commit changes in order to let mUserData be shared by all
8596 * snapshot machine instances.
8597 */
8598 mUserData.commitCopy();
8599
8600 // machine registry, if present (must be loaded before snapshots)
8601 if (config.canHaveOwnMediaRegistry())
8602 {
8603 // determine machine folder
8604 Utf8Str strMachineFolder = i_getSettingsFileFull();
8605 strMachineFolder.stripFilename();
8606 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8607 config.mediaRegistry,
8608 strMachineFolder);
8609 if (FAILED(rc)) return rc;
8610 }
8611
8612 /* Snapshot node (optional) */
8613 size_t cRootSnapshots;
8614 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8615 {
8616 // there must be only one root snapshot
8617 Assert(cRootSnapshots == 1);
8618
8619 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8620
8621 rc = i_loadSnapshot(snap,
8622 config.uuidCurrentSnapshot,
8623 NULL); // no parent == first snapshot
8624 if (FAILED(rc)) return rc;
8625 }
8626
8627 // hardware data
8628 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8629 if (FAILED(rc)) return rc;
8630
8631 /*
8632 * NOTE: the assignment below must be the last thing to do,
8633 * otherwise it will be not possible to change the settings
8634 * somewhere in the code above because all setters will be
8635 * blocked by i_checkStateDependency(MutableStateDep).
8636 */
8637
8638 /* set the machine state to Aborted or Saved when appropriate */
8639 if (config.fAborted)
8640 {
8641 mSSData->strStateFilePath.setNull();
8642
8643 /* no need to use i_setMachineState() during init() */
8644 mData->mMachineState = MachineState_Aborted;
8645 }
8646 else if (!mSSData->strStateFilePath.isEmpty())
8647 {
8648 /* no need to use i_setMachineState() during init() */
8649 mData->mMachineState = MachineState_Saved;
8650 }
8651
8652 // after loading settings, we are no longer different from the XML on disk
8653 mData->flModifications = 0;
8654
8655 return S_OK;
8656}
8657
8658/**
8659 * Recursively loads all snapshots starting from the given.
8660 *
8661 * @param data snapshot settings.
8662 * @param aCurSnapshotId Current snapshot ID from the settings file.
8663 * @param aParentSnapshot Parent snapshot.
8664 */
8665HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8666 const Guid &aCurSnapshotId,
8667 Snapshot *aParentSnapshot)
8668{
8669 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8670 AssertReturn(!i_isSessionMachine(), E_FAIL);
8671
8672 HRESULT rc = S_OK;
8673
8674 Utf8Str strStateFile;
8675 if (!data.strStateFile.isEmpty())
8676 {
8677 /* optional */
8678 strStateFile = data.strStateFile;
8679 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8680 if (RT_FAILURE(vrc))
8681 return setErrorBoth(E_FAIL, vrc,
8682 tr("Invalid saved state file path '%s' (%Rrc)"),
8683 strStateFile.c_str(),
8684 vrc);
8685 }
8686
8687 /* create a snapshot machine object */
8688 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8689 pSnapshotMachine.createObject();
8690 rc = pSnapshotMachine->initFromSettings(this,
8691 data.hardware,
8692 &data.debugging,
8693 &data.autostart,
8694 data.uuid.ref(),
8695 strStateFile);
8696 if (FAILED(rc)) return rc;
8697
8698 /* create a snapshot object */
8699 ComObjPtr<Snapshot> pSnapshot;
8700 pSnapshot.createObject();
8701 /* initialize the snapshot */
8702 rc = pSnapshot->init(mParent, // VirtualBox object
8703 data.uuid,
8704 data.strName,
8705 data.strDescription,
8706 data.timestamp,
8707 pSnapshotMachine,
8708 aParentSnapshot);
8709 if (FAILED(rc)) return rc;
8710
8711 /* memorize the first snapshot if necessary */
8712 if (!mData->mFirstSnapshot)
8713 mData->mFirstSnapshot = pSnapshot;
8714
8715 /* memorize the current snapshot when appropriate */
8716 if ( !mData->mCurrentSnapshot
8717 && pSnapshot->i_getId() == aCurSnapshotId
8718 )
8719 mData->mCurrentSnapshot = pSnapshot;
8720
8721 // now create the children
8722 for (settings::SnapshotsList::const_iterator
8723 it = data.llChildSnapshots.begin();
8724 it != data.llChildSnapshots.end();
8725 ++it)
8726 {
8727 const settings::Snapshot &childData = *it;
8728 // recurse
8729 rc = i_loadSnapshot(childData,
8730 aCurSnapshotId,
8731 pSnapshot); // parent = the one we created above
8732 if (FAILED(rc)) return rc;
8733 }
8734
8735 return rc;
8736}
8737
8738/**
8739 * Loads settings into mHWData.
8740 *
8741 * @param puuidRegistry Registry ID.
8742 * @param puuidSnapshot Snapshot ID
8743 * @param data Reference to the hardware settings.
8744 * @param pDbg Pointer to the debugging settings.
8745 * @param pAutostart Pointer to the autostart settings.
8746 */
8747HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8748 const Guid *puuidSnapshot,
8749 const settings::Hardware &data,
8750 const settings::Debugging *pDbg,
8751 const settings::Autostart *pAutostart)
8752{
8753 AssertReturn(!i_isSessionMachine(), E_FAIL);
8754
8755 HRESULT rc = S_OK;
8756
8757 try
8758 {
8759 ComObjPtr<GuestOSType> pGuestOSType;
8760 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8761
8762 /* The hardware version attribute (optional). */
8763 mHWData->mHWVersion = data.strVersion;
8764 mHWData->mHardwareUUID = data.uuid;
8765
8766 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8767 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8768 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8769 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8770 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8771 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8772 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8773 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8774 mHWData->mPAEEnabled = data.fPAE;
8775 mHWData->mLongMode = data.enmLongMode;
8776 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8777 mHWData->mAPIC = data.fAPIC;
8778 mHWData->mX2APIC = data.fX2APIC;
8779 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8780 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8781 mHWData->mSpecCtrl = data.fSpecCtrl;
8782 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8783 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8784 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8785 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8786 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8787 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8788 mHWData->mCPUCount = data.cCPUs;
8789 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8790 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8791 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8792 mHWData->mCpuProfile = data.strCpuProfile;
8793
8794 // cpu
8795 if (mHWData->mCPUHotPlugEnabled)
8796 {
8797 for (settings::CpuList::const_iterator
8798 it = data.llCpus.begin();
8799 it != data.llCpus.end();
8800 ++it)
8801 {
8802 const settings::Cpu &cpu = *it;
8803
8804 mHWData->mCPUAttached[cpu.ulId] = true;
8805 }
8806 }
8807
8808 // cpuid leafs
8809 for (settings::CpuIdLeafsList::const_iterator
8810 it = data.llCpuIdLeafs.begin();
8811 it != data.llCpuIdLeafs.end();
8812 ++it)
8813 {
8814 const settings::CpuIdLeaf &rLeaf= *it;
8815 if ( rLeaf.idx < UINT32_C(0x20)
8816 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8817 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8818 mHWData->mCpuIdLeafList.push_back(rLeaf);
8819 /* else: just ignore */
8820 }
8821
8822 mHWData->mMemorySize = data.ulMemorySizeMB;
8823 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8824
8825 // boot order
8826 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8827 {
8828 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8829 if (it == data.mapBootOrder.end())
8830 mHWData->mBootOrder[i] = DeviceType_Null;
8831 else
8832 mHWData->mBootOrder[i] = it->second;
8833 }
8834
8835 mHWData->mFirmwareType = data.firmwareType;
8836 mHWData->mPointingHIDType = data.pointingHIDType;
8837 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8838 mHWData->mChipsetType = data.chipsetType;
8839 mHWData->mIommuType = data.iommuType;
8840 mHWData->mParavirtProvider = data.paravirtProvider;
8841 mHWData->mParavirtDebug = data.strParavirtDebug;
8842 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8843 mHWData->mHPETEnabled = data.fHPETEnabled;
8844
8845 /* GraphicsAdapter */
8846 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8847 if (FAILED(rc)) return rc;
8848
8849 /* VRDEServer */
8850 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8851 if (FAILED(rc)) return rc;
8852
8853 /* BIOS */
8854 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8855 if (FAILED(rc)) return rc;
8856
8857 /* Trusted Platform Module */
8858 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8859 if (FAILED(rc)) return rc;
8860
8861 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8862 if (FAILED(rc)) return rc;
8863
8864 /* Recording settings */
8865 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8866 if (FAILED(rc)) return rc;
8867
8868 // Bandwidth control (must come before network adapters)
8869 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8870 if (FAILED(rc)) return rc;
8871
8872 /* USB controllers */
8873 for (settings::USBControllerList::const_iterator
8874 it = data.usbSettings.llUSBControllers.begin();
8875 it != data.usbSettings.llUSBControllers.end();
8876 ++it)
8877 {
8878 const settings::USBController &settingsCtrl = *it;
8879 ComObjPtr<USBController> newCtrl;
8880
8881 newCtrl.createObject();
8882 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8883 mUSBControllers->push_back(newCtrl);
8884 }
8885
8886 /* USB device filters */
8887 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8888 if (FAILED(rc)) return rc;
8889
8890 // network adapters (establish array size first and apply defaults, to
8891 // ensure reading the same settings as we saved, since the list skips
8892 // adapters having defaults)
8893 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8894 size_t oldCount = mNetworkAdapters.size();
8895 if (newCount > oldCount)
8896 {
8897 mNetworkAdapters.resize(newCount);
8898 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8899 {
8900 unconst(mNetworkAdapters[slot]).createObject();
8901 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8902 }
8903 }
8904 else if (newCount < oldCount)
8905 mNetworkAdapters.resize(newCount);
8906 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8907 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8908 for (settings::NetworkAdaptersList::const_iterator
8909 it = data.llNetworkAdapters.begin();
8910 it != data.llNetworkAdapters.end();
8911 ++it)
8912 {
8913 const settings::NetworkAdapter &nic = *it;
8914
8915 /* slot uniqueness is guaranteed by XML Schema */
8916 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8917 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8918 if (FAILED(rc)) return rc;
8919 }
8920
8921 // serial ports (establish defaults first, to ensure reading the same
8922 // settings as we saved, since the list skips ports having defaults)
8923 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8924 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8925 for (settings::SerialPortsList::const_iterator
8926 it = data.llSerialPorts.begin();
8927 it != data.llSerialPorts.end();
8928 ++it)
8929 {
8930 const settings::SerialPort &s = *it;
8931
8932 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8933 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8934 if (FAILED(rc)) return rc;
8935 }
8936
8937 // parallel ports (establish defaults first, to ensure reading the same
8938 // settings as we saved, since the list skips ports having defaults)
8939 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8940 mParallelPorts[i]->i_applyDefaults();
8941 for (settings::ParallelPortsList::const_iterator
8942 it = data.llParallelPorts.begin();
8943 it != data.llParallelPorts.end();
8944 ++it)
8945 {
8946 const settings::ParallelPort &p = *it;
8947
8948 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8949 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8950 if (FAILED(rc)) return rc;
8951 }
8952
8953 /* AudioAdapter */
8954 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8955 if (FAILED(rc)) return rc;
8956
8957 /* storage controllers */
8958 rc = i_loadStorageControllers(data.storage,
8959 puuidRegistry,
8960 puuidSnapshot);
8961 if (FAILED(rc)) return rc;
8962
8963 /* Shared folders */
8964 for (settings::SharedFoldersList::const_iterator
8965 it = data.llSharedFolders.begin();
8966 it != data.llSharedFolders.end();
8967 ++it)
8968 {
8969 const settings::SharedFolder &sf = *it;
8970
8971 ComObjPtr<SharedFolder> sharedFolder;
8972 /* Check for double entries. Not allowed! */
8973 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8974 if (SUCCEEDED(rc))
8975 return setError(VBOX_E_OBJECT_IN_USE,
8976 tr("Shared folder named '%s' already exists"),
8977 sf.strName.c_str());
8978
8979 /* Create the new shared folder. Don't break on error. This will be
8980 * reported when the machine starts. */
8981 sharedFolder.createObject();
8982 rc = sharedFolder->init(i_getMachine(),
8983 sf.strName,
8984 sf.strHostPath,
8985 RT_BOOL(sf.fWritable),
8986 RT_BOOL(sf.fAutoMount),
8987 sf.strAutoMountPoint,
8988 false /* fFailOnError */);
8989 if (FAILED(rc)) return rc;
8990 mHWData->mSharedFolders.push_back(sharedFolder);
8991 }
8992
8993 // Clipboard
8994 mHWData->mClipboardMode = data.clipboardMode;
8995 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8996
8997 // drag'n'drop
8998 mHWData->mDnDMode = data.dndMode;
8999
9000 // guest settings
9001 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9002
9003 // IO settings
9004 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9005 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9006
9007 // Host PCI devices
9008 for (settings::HostPCIDeviceAttachmentList::const_iterator
9009 it = data.pciAttachments.begin();
9010 it != data.pciAttachments.end();
9011 ++it)
9012 {
9013 const settings::HostPCIDeviceAttachment &hpda = *it;
9014 ComObjPtr<PCIDeviceAttachment> pda;
9015
9016 pda.createObject();
9017 pda->i_loadSettings(this, hpda);
9018 mHWData->mPCIDeviceAssignments.push_back(pda);
9019 }
9020
9021 /*
9022 * (The following isn't really real hardware, but it lives in HWData
9023 * for reasons of convenience.)
9024 */
9025
9026#ifdef VBOX_WITH_GUEST_PROPS
9027 /* Guest properties (optional) */
9028
9029 /* Only load transient guest properties for configs which have saved
9030 * state, because there shouldn't be any for powered off VMs. The same
9031 * logic applies for snapshots, as offline snapshots shouldn't have
9032 * any such properties. They confuse the code in various places.
9033 * Note: can't rely on the machine state, as it isn't set yet. */
9034 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9035 /* apologies for the hacky unconst() usage, but this needs hacking
9036 * actually inconsistent settings into consistency, otherwise there
9037 * will be some corner cases where the inconsistency survives
9038 * surprisingly long without getting fixed, especially for snapshots
9039 * as there are no config changes. */
9040 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9041 for (settings::GuestPropertiesList::iterator
9042 it = llGuestProperties.begin();
9043 it != llGuestProperties.end();
9044 /*nothing*/)
9045 {
9046 const settings::GuestProperty &prop = *it;
9047 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9048 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9049 if ( fSkipTransientGuestProperties
9050 && ( fFlags & GUEST_PROP_F_TRANSIENT
9051 || fFlags & GUEST_PROP_F_TRANSRESET))
9052 {
9053 it = llGuestProperties.erase(it);
9054 continue;
9055 }
9056 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9057 mHWData->mGuestProperties[prop.strName] = property;
9058 ++it;
9059 }
9060#endif /* VBOX_WITH_GUEST_PROPS defined */
9061
9062 rc = i_loadDebugging(pDbg);
9063 if (FAILED(rc))
9064 return rc;
9065
9066 mHWData->mAutostart = *pAutostart;
9067
9068 /* default frontend */
9069 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9070 }
9071 catch (std::bad_alloc &)
9072 {
9073 return E_OUTOFMEMORY;
9074 }
9075
9076 AssertComRC(rc);
9077 return rc;
9078}
9079
9080/**
9081 * Called from i_loadHardware() to load the debugging settings of the
9082 * machine.
9083 *
9084 * @param pDbg Pointer to the settings.
9085 */
9086HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9087{
9088 mHWData->mDebugging = *pDbg;
9089 /* no more processing currently required, this will probably change. */
9090 return S_OK;
9091}
9092
9093/**
9094 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9095 *
9096 * @param data storage settings.
9097 * @param puuidRegistry media registry ID to set media to or NULL;
9098 * see Machine::i_loadMachineDataFromSettings()
9099 * @param puuidSnapshot snapshot ID
9100 * @return
9101 */
9102HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9103 const Guid *puuidRegistry,
9104 const Guid *puuidSnapshot)
9105{
9106 AssertReturn(!i_isSessionMachine(), E_FAIL);
9107
9108 HRESULT rc = S_OK;
9109
9110 for (settings::StorageControllersList::const_iterator
9111 it = data.llStorageControllers.begin();
9112 it != data.llStorageControllers.end();
9113 ++it)
9114 {
9115 const settings::StorageController &ctlData = *it;
9116
9117 ComObjPtr<StorageController> pCtl;
9118 /* Try to find one with the name first. */
9119 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9120 if (SUCCEEDED(rc))
9121 return setError(VBOX_E_OBJECT_IN_USE,
9122 tr("Storage controller named '%s' already exists"),
9123 ctlData.strName.c_str());
9124
9125 pCtl.createObject();
9126 rc = pCtl->init(this,
9127 ctlData.strName,
9128 ctlData.storageBus,
9129 ctlData.ulInstance,
9130 ctlData.fBootable);
9131 if (FAILED(rc)) return rc;
9132
9133 mStorageControllers->push_back(pCtl);
9134
9135 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9136 if (FAILED(rc)) return rc;
9137
9138 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9139 if (FAILED(rc)) return rc;
9140
9141 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9142 if (FAILED(rc)) return rc;
9143
9144 /* Load the attached devices now. */
9145 rc = i_loadStorageDevices(pCtl,
9146 ctlData,
9147 puuidRegistry,
9148 puuidSnapshot);
9149 if (FAILED(rc)) return rc;
9150 }
9151
9152 return S_OK;
9153}
9154
9155/**
9156 * Called from i_loadStorageControllers for a controller's devices.
9157 *
9158 * @param aStorageController
9159 * @param data
9160 * @param puuidRegistry media registry ID to set media to or NULL; see
9161 * Machine::i_loadMachineDataFromSettings()
9162 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9163 * @return
9164 */
9165HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9166 const settings::StorageController &data,
9167 const Guid *puuidRegistry,
9168 const Guid *puuidSnapshot)
9169{
9170 HRESULT rc = S_OK;
9171
9172 /* paranoia: detect duplicate attachments */
9173 for (settings::AttachedDevicesList::const_iterator
9174 it = data.llAttachedDevices.begin();
9175 it != data.llAttachedDevices.end();
9176 ++it)
9177 {
9178 const settings::AttachedDevice &ad = *it;
9179
9180 for (settings::AttachedDevicesList::const_iterator it2 = it;
9181 it2 != data.llAttachedDevices.end();
9182 ++it2)
9183 {
9184 if (it == it2)
9185 continue;
9186
9187 const settings::AttachedDevice &ad2 = *it2;
9188
9189 if ( ad.lPort == ad2.lPort
9190 && ad.lDevice == ad2.lDevice)
9191 {
9192 return setError(E_FAIL,
9193 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9194 aStorageController->i_getName().c_str(),
9195 ad.lPort,
9196 ad.lDevice,
9197 mUserData->s.strName.c_str());
9198 }
9199 }
9200 }
9201
9202 for (settings::AttachedDevicesList::const_iterator
9203 it = data.llAttachedDevices.begin();
9204 it != data.llAttachedDevices.end();
9205 ++it)
9206 {
9207 const settings::AttachedDevice &dev = *it;
9208 ComObjPtr<Medium> medium;
9209
9210 switch (dev.deviceType)
9211 {
9212 case DeviceType_Floppy:
9213 case DeviceType_DVD:
9214 if (dev.strHostDriveSrc.isNotEmpty())
9215 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9216 false /* fRefresh */, medium);
9217 else
9218 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9219 dev.uuid,
9220 false /* fRefresh */,
9221 false /* aSetError */,
9222 medium);
9223 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9224 // This is not an error. The host drive or UUID might have vanished, so just go
9225 // ahead without this removeable medium attachment
9226 rc = S_OK;
9227 break;
9228
9229 case DeviceType_HardDisk:
9230 {
9231 /* find a hard disk by UUID */
9232 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9233 if (FAILED(rc))
9234 {
9235 if (i_isSnapshotMachine())
9236 {
9237 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9238 // so the user knows that the bad disk is in a snapshot somewhere
9239 com::ErrorInfo info;
9240 return setError(E_FAIL,
9241 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9242 puuidSnapshot->raw(),
9243 info.getText().raw());
9244 }
9245 else
9246 return rc;
9247 }
9248
9249 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9250
9251 if (medium->i_getType() == MediumType_Immutable)
9252 {
9253 if (i_isSnapshotMachine())
9254 return setError(E_FAIL,
9255 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9256 "of the virtual machine '%s' ('%s')"),
9257 medium->i_getLocationFull().c_str(),
9258 dev.uuid.raw(),
9259 puuidSnapshot->raw(),
9260 mUserData->s.strName.c_str(),
9261 mData->m_strConfigFileFull.c_str());
9262
9263 return setError(E_FAIL,
9264 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9265 medium->i_getLocationFull().c_str(),
9266 dev.uuid.raw(),
9267 mUserData->s.strName.c_str(),
9268 mData->m_strConfigFileFull.c_str());
9269 }
9270
9271 if (medium->i_getType() == MediumType_MultiAttach)
9272 {
9273 if (i_isSnapshotMachine())
9274 return setError(E_FAIL,
9275 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9276 "of the virtual machine '%s' ('%s')"),
9277 medium->i_getLocationFull().c_str(),
9278 dev.uuid.raw(),
9279 puuidSnapshot->raw(),
9280 mUserData->s.strName.c_str(),
9281 mData->m_strConfigFileFull.c_str());
9282
9283 return setError(E_FAIL,
9284 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9285 medium->i_getLocationFull().c_str(),
9286 dev.uuid.raw(),
9287 mUserData->s.strName.c_str(),
9288 mData->m_strConfigFileFull.c_str());
9289 }
9290
9291 if ( !i_isSnapshotMachine()
9292 && medium->i_getChildren().size() != 0
9293 )
9294 return setError(E_FAIL,
9295 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9296 "because it has %d differencing child hard disks"),
9297 medium->i_getLocationFull().c_str(),
9298 dev.uuid.raw(),
9299 mUserData->s.strName.c_str(),
9300 mData->m_strConfigFileFull.c_str(),
9301 medium->i_getChildren().size());
9302
9303 if (i_findAttachment(*mMediumAttachments.data(),
9304 medium))
9305 return setError(E_FAIL,
9306 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9307 medium->i_getLocationFull().c_str(),
9308 dev.uuid.raw(),
9309 mUserData->s.strName.c_str(),
9310 mData->m_strConfigFileFull.c_str());
9311
9312 break;
9313 }
9314
9315 default:
9316 return setError(E_FAIL,
9317 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9318 medium->i_getLocationFull().c_str(),
9319 mUserData->s.strName.c_str(),
9320 mData->m_strConfigFileFull.c_str());
9321 }
9322
9323 if (FAILED(rc))
9324 break;
9325
9326 /* Bandwidth groups are loaded at this point. */
9327 ComObjPtr<BandwidthGroup> pBwGroup;
9328
9329 if (!dev.strBwGroup.isEmpty())
9330 {
9331 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9332 if (FAILED(rc))
9333 return setError(E_FAIL,
9334 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9335 medium->i_getLocationFull().c_str(),
9336 dev.strBwGroup.c_str(),
9337 mUserData->s.strName.c_str(),
9338 mData->m_strConfigFileFull.c_str());
9339 pBwGroup->i_reference();
9340 }
9341
9342 const Utf8Str controllerName = aStorageController->i_getName();
9343 ComObjPtr<MediumAttachment> pAttachment;
9344 pAttachment.createObject();
9345 rc = pAttachment->init(this,
9346 medium,
9347 controllerName,
9348 dev.lPort,
9349 dev.lDevice,
9350 dev.deviceType,
9351 false,
9352 dev.fPassThrough,
9353 dev.fTempEject,
9354 dev.fNonRotational,
9355 dev.fDiscard,
9356 dev.fHotPluggable,
9357 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9358 if (FAILED(rc)) break;
9359
9360 /* associate the medium with this machine and snapshot */
9361 if (!medium.isNull())
9362 {
9363 AutoCaller medCaller(medium);
9364 if (FAILED(medCaller.rc())) return medCaller.rc();
9365 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9366
9367 if (i_isSnapshotMachine())
9368 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9369 else
9370 rc = medium->i_addBackReference(mData->mUuid);
9371 /* If the medium->addBackReference fails it sets an appropriate
9372 * error message, so no need to do any guesswork here. */
9373
9374 if (puuidRegistry)
9375 // caller wants registry ID to be set on all attached media (OVF import case)
9376 medium->i_addRegistry(*puuidRegistry);
9377 }
9378
9379 if (FAILED(rc))
9380 break;
9381
9382 /* back up mMediumAttachments to let registeredInit() properly rollback
9383 * on failure (= limited accessibility) */
9384 i_setModified(IsModified_Storage);
9385 mMediumAttachments.backup();
9386 mMediumAttachments->push_back(pAttachment);
9387 }
9388
9389 return rc;
9390}
9391
9392/**
9393 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9394 *
9395 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9396 * @param aSnapshot where to return the found snapshot
9397 * @param aSetError true to set extended error info on failure
9398 */
9399HRESULT Machine::i_findSnapshotById(const Guid &aId,
9400 ComObjPtr<Snapshot> &aSnapshot,
9401 bool aSetError /* = false */)
9402{
9403 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9404
9405 if (!mData->mFirstSnapshot)
9406 {
9407 if (aSetError)
9408 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9409 return E_FAIL;
9410 }
9411
9412 if (aId.isZero())
9413 aSnapshot = mData->mFirstSnapshot;
9414 else
9415 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9416
9417 if (!aSnapshot)
9418 {
9419 if (aSetError)
9420 return setError(E_FAIL,
9421 tr("Could not find a snapshot with UUID {%s}"),
9422 aId.toString().c_str());
9423 return E_FAIL;
9424 }
9425
9426 return S_OK;
9427}
9428
9429/**
9430 * Returns the snapshot with the given name or fails of no such snapshot.
9431 *
9432 * @param strName snapshot name to find
9433 * @param aSnapshot where to return the found snapshot
9434 * @param aSetError true to set extended error info on failure
9435 */
9436HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9437 ComObjPtr<Snapshot> &aSnapshot,
9438 bool aSetError /* = false */)
9439{
9440 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9441
9442 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9443
9444 if (!mData->mFirstSnapshot)
9445 {
9446 if (aSetError)
9447 return setError(VBOX_E_OBJECT_NOT_FOUND,
9448 tr("This machine does not have any snapshots"));
9449 return VBOX_E_OBJECT_NOT_FOUND;
9450 }
9451
9452 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9453
9454 if (!aSnapshot)
9455 {
9456 if (aSetError)
9457 return setError(VBOX_E_OBJECT_NOT_FOUND,
9458 tr("Could not find a snapshot named '%s'"), strName.c_str());
9459 return VBOX_E_OBJECT_NOT_FOUND;
9460 }
9461
9462 return S_OK;
9463}
9464
9465/**
9466 * Returns a storage controller object with the given name.
9467 *
9468 * @param aName storage controller name to find
9469 * @param aStorageController where to return the found storage controller
9470 * @param aSetError true to set extended error info on failure
9471 */
9472HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9473 ComObjPtr<StorageController> &aStorageController,
9474 bool aSetError /* = false */)
9475{
9476 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9477
9478 for (StorageControllerList::const_iterator
9479 it = mStorageControllers->begin();
9480 it != mStorageControllers->end();
9481 ++it)
9482 {
9483 if ((*it)->i_getName() == aName)
9484 {
9485 aStorageController = (*it);
9486 return S_OK;
9487 }
9488 }
9489
9490 if (aSetError)
9491 return setError(VBOX_E_OBJECT_NOT_FOUND,
9492 tr("Could not find a storage controller named '%s'"),
9493 aName.c_str());
9494 return VBOX_E_OBJECT_NOT_FOUND;
9495}
9496
9497/**
9498 * Returns a USB controller object with the given name.
9499 *
9500 * @param aName USB controller name to find
9501 * @param aUSBController where to return the found USB controller
9502 * @param aSetError true to set extended error info on failure
9503 */
9504HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9505 ComObjPtr<USBController> &aUSBController,
9506 bool aSetError /* = false */)
9507{
9508 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9509
9510 for (USBControllerList::const_iterator
9511 it = mUSBControllers->begin();
9512 it != mUSBControllers->end();
9513 ++it)
9514 {
9515 if ((*it)->i_getName() == aName)
9516 {
9517 aUSBController = (*it);
9518 return S_OK;
9519 }
9520 }
9521
9522 if (aSetError)
9523 return setError(VBOX_E_OBJECT_NOT_FOUND,
9524 tr("Could not find a storage controller named '%s'"),
9525 aName.c_str());
9526 return VBOX_E_OBJECT_NOT_FOUND;
9527}
9528
9529/**
9530 * Returns the number of USB controller instance of the given type.
9531 *
9532 * @param enmType USB controller type.
9533 */
9534ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9535{
9536 ULONG cCtrls = 0;
9537
9538 for (USBControllerList::const_iterator
9539 it = mUSBControllers->begin();
9540 it != mUSBControllers->end();
9541 ++it)
9542 {
9543 if ((*it)->i_getControllerType() == enmType)
9544 cCtrls++;
9545 }
9546
9547 return cCtrls;
9548}
9549
9550HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9551 MediumAttachmentList &atts)
9552{
9553 AutoCaller autoCaller(this);
9554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9555
9556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9557
9558 for (MediumAttachmentList::const_iterator
9559 it = mMediumAttachments->begin();
9560 it != mMediumAttachments->end();
9561 ++it)
9562 {
9563 const ComObjPtr<MediumAttachment> &pAtt = *it;
9564 // should never happen, but deal with NULL pointers in the list.
9565 AssertContinue(!pAtt.isNull());
9566
9567 // getControllerName() needs caller+read lock
9568 AutoCaller autoAttCaller(pAtt);
9569 if (FAILED(autoAttCaller.rc()))
9570 {
9571 atts.clear();
9572 return autoAttCaller.rc();
9573 }
9574 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9575
9576 if (pAtt->i_getControllerName() == aName)
9577 atts.push_back(pAtt);
9578 }
9579
9580 return S_OK;
9581}
9582
9583
9584/**
9585 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9586 * file if the machine name was changed and about creating a new settings file
9587 * if this is a new machine.
9588 *
9589 * @note Must be never called directly but only from #saveSettings().
9590 */
9591HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9592{
9593 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9594
9595 HRESULT rc = S_OK;
9596
9597 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9598
9599 /// @todo need to handle primary group change, too
9600
9601 /* attempt to rename the settings file if machine name is changed */
9602 if ( mUserData->s.fNameSync
9603 && mUserData.isBackedUp()
9604 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9605 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9606 )
9607 {
9608 bool dirRenamed = false;
9609 bool fileRenamed = false;
9610
9611 Utf8Str configFile, newConfigFile;
9612 Utf8Str configFilePrev, newConfigFilePrev;
9613 Utf8Str NVRAMFile, newNVRAMFile;
9614 Utf8Str configDir, newConfigDir;
9615
9616 do
9617 {
9618 int vrc = VINF_SUCCESS;
9619
9620 Utf8Str name = mUserData.backedUpData()->s.strName;
9621 Utf8Str newName = mUserData->s.strName;
9622 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9623 if (group == "/")
9624 group.setNull();
9625 Utf8Str newGroup = mUserData->s.llGroups.front();
9626 if (newGroup == "/")
9627 newGroup.setNull();
9628
9629 configFile = mData->m_strConfigFileFull;
9630
9631 /* first, rename the directory if it matches the group and machine name */
9632 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9633 /** @todo hack, make somehow use of ComposeMachineFilename */
9634 if (mUserData->s.fDirectoryIncludesUUID)
9635 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9636 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9637 /** @todo hack, make somehow use of ComposeMachineFilename */
9638 if (mUserData->s.fDirectoryIncludesUUID)
9639 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9640 configDir = configFile;
9641 configDir.stripFilename();
9642 newConfigDir = configDir;
9643 if ( configDir.length() >= groupPlusName.length()
9644 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9645 groupPlusName.c_str()))
9646 {
9647 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9648 Utf8Str newConfigBaseDir(newConfigDir);
9649 newConfigDir.append(newGroupPlusName);
9650 /* consistency: use \ if appropriate on the platform */
9651 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9652 /* new dir and old dir cannot be equal here because of 'if'
9653 * above and because name != newName */
9654 Assert(configDir != newConfigDir);
9655 if (!fSettingsFileIsNew)
9656 {
9657 /* perform real rename only if the machine is not new */
9658 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9659 if ( vrc == VERR_FILE_NOT_FOUND
9660 || vrc == VERR_PATH_NOT_FOUND)
9661 {
9662 /* create the parent directory, then retry renaming */
9663 Utf8Str parent(newConfigDir);
9664 parent.stripFilename();
9665 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9666 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9667 }
9668 if (RT_FAILURE(vrc))
9669 {
9670 rc = setErrorBoth(E_FAIL, vrc,
9671 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9672 configDir.c_str(),
9673 newConfigDir.c_str(),
9674 vrc);
9675 break;
9676 }
9677 /* delete subdirectories which are no longer needed */
9678 Utf8Str dir(configDir);
9679 dir.stripFilename();
9680 while (dir != newConfigBaseDir && dir != ".")
9681 {
9682 vrc = RTDirRemove(dir.c_str());
9683 if (RT_FAILURE(vrc))
9684 break;
9685 dir.stripFilename();
9686 }
9687 dirRenamed = true;
9688 }
9689 }
9690
9691 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9692
9693 /* then try to rename the settings file itself */
9694 if (newConfigFile != configFile)
9695 {
9696 /* get the path to old settings file in renamed directory */
9697 Assert(mData->m_strConfigFileFull == configFile);
9698 configFile.printf("%s%c%s",
9699 newConfigDir.c_str(),
9700 RTPATH_DELIMITER,
9701 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9702 if (!fSettingsFileIsNew)
9703 {
9704 /* perform real rename only if the machine is not new */
9705 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9706 if (RT_FAILURE(vrc))
9707 {
9708 rc = setErrorBoth(E_FAIL, vrc,
9709 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9710 configFile.c_str(),
9711 newConfigFile.c_str(),
9712 vrc);
9713 break;
9714 }
9715 fileRenamed = true;
9716 configFilePrev = configFile;
9717 configFilePrev += "-prev";
9718 newConfigFilePrev = newConfigFile;
9719 newConfigFilePrev += "-prev";
9720 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9721 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9722 if (NVRAMFile.isNotEmpty())
9723 {
9724 // in the NVRAM file path, replace the old directory with the new directory
9725 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9726 {
9727 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9728 NVRAMFile = newConfigDir + strNVRAMFile;
9729 }
9730 newNVRAMFile = newConfigFile;
9731 newNVRAMFile.stripSuffix();
9732 newNVRAMFile += ".nvram";
9733 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9734 }
9735 }
9736 }
9737
9738 // update m_strConfigFileFull amd mConfigFile
9739 mData->m_strConfigFileFull = newConfigFile;
9740 // compute the relative path too
9741 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9742
9743 // store the old and new so that VirtualBox::i_saveSettings() can update
9744 // the media registry
9745 if ( mData->mRegistered
9746 && (configDir != newConfigDir || configFile != newConfigFile))
9747 {
9748 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9749
9750 if (pfNeedsGlobalSaveSettings)
9751 *pfNeedsGlobalSaveSettings = true;
9752 }
9753
9754 // in the saved state file path, replace the old directory with the new directory
9755 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9756 {
9757 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9758 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9759 }
9760 if (newNVRAMFile.isNotEmpty())
9761 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9762
9763 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9764 if (mData->mFirstSnapshot)
9765 {
9766 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9767 newConfigDir.c_str());
9768 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9769 newConfigDir.c_str());
9770 }
9771 }
9772 while (0);
9773
9774 if (FAILED(rc))
9775 {
9776 /* silently try to rename everything back */
9777 if (fileRenamed)
9778 {
9779 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9780 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9781 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9782 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9783 }
9784 if (dirRenamed)
9785 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9786 }
9787
9788 if (FAILED(rc)) return rc;
9789 }
9790
9791 if (fSettingsFileIsNew)
9792 {
9793 /* create a virgin config file */
9794 int vrc = VINF_SUCCESS;
9795
9796 /* ensure the settings directory exists */
9797 Utf8Str path(mData->m_strConfigFileFull);
9798 path.stripFilename();
9799 if (!RTDirExists(path.c_str()))
9800 {
9801 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9802 if (RT_FAILURE(vrc))
9803 {
9804 return setErrorBoth(E_FAIL, vrc,
9805 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9806 path.c_str(),
9807 vrc);
9808 }
9809 }
9810
9811 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9812 path = Utf8Str(mData->m_strConfigFileFull);
9813 RTFILE f = NIL_RTFILE;
9814 vrc = RTFileOpen(&f, path.c_str(),
9815 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9816 if (RT_FAILURE(vrc))
9817 return setErrorBoth(E_FAIL, vrc,
9818 tr("Could not create the settings file '%s' (%Rrc)"),
9819 path.c_str(),
9820 vrc);
9821 RTFileClose(f);
9822 }
9823
9824 return rc;
9825}
9826
9827/**
9828 * Saves and commits machine data, user data and hardware data.
9829 *
9830 * Note that on failure, the data remains uncommitted.
9831 *
9832 * @a aFlags may combine the following flags:
9833 *
9834 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9835 * Used when saving settings after an operation that makes them 100%
9836 * correspond to the settings from the current snapshot.
9837 * - SaveS_Force: settings will be saved without doing a deep compare of the
9838 * settings structures. This is used when this is called because snapshots
9839 * have changed to avoid the overhead of the deep compare.
9840 *
9841 * @note Must be called from under this object's write lock. Locks children for
9842 * writing.
9843 *
9844 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9845 * initialized to false and that will be set to true by this function if
9846 * the caller must invoke VirtualBox::i_saveSettings() because the global
9847 * settings have changed. This will happen if a machine rename has been
9848 * saved and the global machine and media registries will therefore need
9849 * updating.
9850 * @param aFlags Flags.
9851 */
9852HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9853 int aFlags /*= 0*/)
9854{
9855 LogFlowThisFuncEnter();
9856
9857 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9858
9859 /* make sure child objects are unable to modify the settings while we are
9860 * saving them */
9861 i_ensureNoStateDependencies();
9862
9863 AssertReturn(!i_isSnapshotMachine(),
9864 E_FAIL);
9865
9866 if (!mData->mAccessible)
9867 return setError(VBOX_E_INVALID_VM_STATE,
9868 tr("The machine is not accessible, so cannot save settings"));
9869
9870 HRESULT rc = S_OK;
9871 bool fNeedsWrite = false;
9872
9873 /* First, prepare to save settings. It will care about renaming the
9874 * settings directory and file if the machine name was changed and about
9875 * creating a new settings file if this is a new machine. */
9876 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9877 if (FAILED(rc)) return rc;
9878
9879 // keep a pointer to the current settings structures
9880 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9881 settings::MachineConfigFile *pNewConfig = NULL;
9882
9883 try
9884 {
9885 // make a fresh one to have everyone write stuff into
9886 pNewConfig = new settings::MachineConfigFile(NULL);
9887 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9888
9889 // now go and copy all the settings data from COM to the settings structures
9890 // (this calls i_saveSettings() on all the COM objects in the machine)
9891 i_copyMachineDataToSettings(*pNewConfig);
9892
9893 if (aFlags & SaveS_ResetCurStateModified)
9894 {
9895 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9896 mData->mCurrentStateModified = FALSE;
9897 fNeedsWrite = true; // always, no need to compare
9898 }
9899 else if (aFlags & SaveS_Force)
9900 {
9901 fNeedsWrite = true; // always, no need to compare
9902 }
9903 else
9904 {
9905 if (!mData->mCurrentStateModified)
9906 {
9907 // do a deep compare of the settings that we just saved with the settings
9908 // previously stored in the config file; this invokes MachineConfigFile::operator==
9909 // which does a deep compare of all the settings, which is expensive but less expensive
9910 // than writing out XML in vain
9911 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9912
9913 // could still be modified if any settings changed
9914 mData->mCurrentStateModified = fAnySettingsChanged;
9915
9916 fNeedsWrite = fAnySettingsChanged;
9917 }
9918 else
9919 fNeedsWrite = true;
9920 }
9921
9922 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9923
9924 if (fNeedsWrite)
9925 // now spit it all out!
9926 pNewConfig->write(mData->m_strConfigFileFull);
9927
9928 mData->pMachineConfigFile = pNewConfig;
9929 delete pOldConfig;
9930 i_commit();
9931
9932 // after saving settings, we are no longer different from the XML on disk
9933 mData->flModifications = 0;
9934 }
9935 catch (HRESULT err)
9936 {
9937 // we assume that error info is set by the thrower
9938 rc = err;
9939
9940 // restore old config
9941 delete pNewConfig;
9942 mData->pMachineConfigFile = pOldConfig;
9943 }
9944 catch (...)
9945 {
9946 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9947 }
9948
9949 if (fNeedsWrite)
9950 {
9951 /* Fire the data change event, even on failure (since we've already
9952 * committed all data). This is done only for SessionMachines because
9953 * mutable Machine instances are always not registered (i.e. private
9954 * to the client process that creates them) and thus don't need to
9955 * inform callbacks. */
9956 if (i_isSessionMachine())
9957 mParent->i_onMachineDataChanged(mData->mUuid);
9958 }
9959
9960 LogFlowThisFunc(("rc=%08X\n", rc));
9961 LogFlowThisFuncLeave();
9962 return rc;
9963}
9964
9965/**
9966 * Implementation for saving the machine settings into the given
9967 * settings::MachineConfigFile instance. This copies machine extradata
9968 * from the previous machine config file in the instance data, if any.
9969 *
9970 * This gets called from two locations:
9971 *
9972 * -- Machine::i_saveSettings(), during the regular XML writing;
9973 *
9974 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9975 * exported to OVF and we write the VirtualBox proprietary XML
9976 * into a <vbox:Machine> tag.
9977 *
9978 * This routine fills all the fields in there, including snapshots, *except*
9979 * for the following:
9980 *
9981 * -- fCurrentStateModified. There is some special logic associated with that.
9982 *
9983 * The caller can then call MachineConfigFile::write() or do something else
9984 * with it.
9985 *
9986 * Caller must hold the machine lock!
9987 *
9988 * This throws XML errors and HRESULT, so the caller must have a catch block!
9989 */
9990void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9991{
9992 // deep copy extradata, being extra careful with self assignment (the STL
9993 // map assignment on Mac OS X clang based Xcode isn't checking)
9994 if (&config != mData->pMachineConfigFile)
9995 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9996
9997 config.uuid = mData->mUuid;
9998
9999 // copy name, description, OS type, teleport, UTC etc.
10000 config.machineUserData = mUserData->s;
10001
10002 if ( mData->mMachineState == MachineState_Saved
10003 || mData->mMachineState == MachineState_Restoring
10004 // when doing certain snapshot operations we may or may not have
10005 // a saved state in the current state, so keep everything as is
10006 || ( ( mData->mMachineState == MachineState_Snapshotting
10007 || mData->mMachineState == MachineState_DeletingSnapshot
10008 || mData->mMachineState == MachineState_RestoringSnapshot)
10009 && (!mSSData->strStateFilePath.isEmpty())
10010 )
10011 )
10012 {
10013 Assert(!mSSData->strStateFilePath.isEmpty());
10014 /* try to make the file name relative to the settings file dir */
10015 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10016 }
10017 else
10018 {
10019 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10020 config.strStateFile.setNull();
10021 }
10022
10023 if (mData->mCurrentSnapshot)
10024 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10025 else
10026 config.uuidCurrentSnapshot.clear();
10027
10028 config.timeLastStateChange = mData->mLastStateChange;
10029 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10030 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10031
10032 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10033 if (FAILED(rc)) throw rc;
10034
10035 // save machine's media registry if this is VirtualBox 4.0 or later
10036 if (config.canHaveOwnMediaRegistry())
10037 {
10038 // determine machine folder
10039 Utf8Str strMachineFolder = i_getSettingsFileFull();
10040 strMachineFolder.stripFilename();
10041 mParent->i_saveMediaRegistry(config.mediaRegistry,
10042 i_getId(), // only media with registry ID == machine UUID
10043 strMachineFolder);
10044 // this throws HRESULT
10045 }
10046
10047 // save snapshots
10048 rc = i_saveAllSnapshots(config);
10049 if (FAILED(rc)) throw rc;
10050}
10051
10052/**
10053 * Saves all snapshots of the machine into the given machine config file. Called
10054 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10055 * @param config
10056 * @return
10057 */
10058HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10059{
10060 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10061
10062 HRESULT rc = S_OK;
10063
10064 try
10065 {
10066 config.llFirstSnapshot.clear();
10067
10068 if (mData->mFirstSnapshot)
10069 {
10070 // the settings use a list for "the first snapshot"
10071 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10072
10073 // get reference to the snapshot on the list and work on that
10074 // element straight in the list to avoid excessive copying later
10075 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10076 if (FAILED(rc)) throw rc;
10077 }
10078
10079// if (mType == IsSessionMachine)
10080// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10081
10082 }
10083 catch (HRESULT err)
10084 {
10085 /* we assume that error info is set by the thrower */
10086 rc = err;
10087 }
10088 catch (...)
10089 {
10090 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10091 }
10092
10093 return rc;
10094}
10095
10096/**
10097 * Saves the VM hardware configuration. It is assumed that the
10098 * given node is empty.
10099 *
10100 * @param data Reference to the settings object for the hardware config.
10101 * @param pDbg Pointer to the settings object for the debugging config
10102 * which happens to live in mHWData.
10103 * @param pAutostart Pointer to the settings object for the autostart config
10104 * which happens to live in mHWData.
10105 */
10106HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10107 settings::Autostart *pAutostart)
10108{
10109 HRESULT rc = S_OK;
10110
10111 try
10112 {
10113 /* The hardware version attribute (optional).
10114 Automatically upgrade from 1 to current default hardware version
10115 when there is no saved state. (ugly!) */
10116 if ( mHWData->mHWVersion == "1"
10117 && mSSData->strStateFilePath.isEmpty()
10118 )
10119 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10120
10121 data.strVersion = mHWData->mHWVersion;
10122 data.uuid = mHWData->mHardwareUUID;
10123
10124 // CPU
10125 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10126 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10127 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10128 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10129 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10130 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10131 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10132 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10133 data.fPAE = !!mHWData->mPAEEnabled;
10134 data.enmLongMode = mHWData->mLongMode;
10135 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10136 data.fAPIC = !!mHWData->mAPIC;
10137 data.fX2APIC = !!mHWData->mX2APIC;
10138 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10139 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10140 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10141 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10142 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10143 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10144 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10145 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10146 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10147 data.cCPUs = mHWData->mCPUCount;
10148 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10149 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10150 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10151 data.strCpuProfile = mHWData->mCpuProfile;
10152
10153 data.llCpus.clear();
10154 if (data.fCpuHotPlug)
10155 {
10156 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10157 {
10158 if (mHWData->mCPUAttached[idx])
10159 {
10160 settings::Cpu cpu;
10161 cpu.ulId = idx;
10162 data.llCpus.push_back(cpu);
10163 }
10164 }
10165 }
10166
10167 /* Standard and Extended CPUID leafs. */
10168 data.llCpuIdLeafs.clear();
10169 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10170
10171 // memory
10172 data.ulMemorySizeMB = mHWData->mMemorySize;
10173 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10174
10175 // firmware
10176 data.firmwareType = mHWData->mFirmwareType;
10177
10178 // HID
10179 data.pointingHIDType = mHWData->mPointingHIDType;
10180 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10181
10182 // chipset
10183 data.chipsetType = mHWData->mChipsetType;
10184
10185 // iommu
10186 data.iommuType = mHWData->mIommuType;
10187
10188 // paravirt
10189 data.paravirtProvider = mHWData->mParavirtProvider;
10190 data.strParavirtDebug = mHWData->mParavirtDebug;
10191
10192 // emulated USB card reader
10193 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10194
10195 // HPET
10196 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10197
10198 // boot order
10199 data.mapBootOrder.clear();
10200 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10201 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10202
10203 /* VRDEServer settings (optional) */
10204 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10205 if (FAILED(rc)) throw rc;
10206
10207 /* BIOS settings (required) */
10208 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10209 if (FAILED(rc)) throw rc;
10210
10211 /* Trusted Platform Module settings (required) */
10212 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10213 if (FAILED(rc)) throw rc;
10214
10215 /* NVRAM settings (required) */
10216 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10217 if (FAILED(rc)) throw rc;
10218
10219 /* Recording settings (required) */
10220 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10221 if (FAILED(rc)) throw rc;
10222
10223 /* GraphicsAdapter settings (required) */
10224 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10225 if (FAILED(rc)) throw rc;
10226
10227 /* USB Controller (required) */
10228 data.usbSettings.llUSBControllers.clear();
10229 for (USBControllerList::const_iterator
10230 it = mUSBControllers->begin();
10231 it != mUSBControllers->end();
10232 ++it)
10233 {
10234 ComObjPtr<USBController> ctrl = *it;
10235 settings::USBController settingsCtrl;
10236
10237 settingsCtrl.strName = ctrl->i_getName();
10238 settingsCtrl.enmType = ctrl->i_getControllerType();
10239
10240 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10241 }
10242
10243 /* USB device filters (required) */
10244 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10245 if (FAILED(rc)) throw rc;
10246
10247 /* Network adapters (required) */
10248 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10249 data.llNetworkAdapters.clear();
10250 /* Write out only the nominal number of network adapters for this
10251 * chipset type. Since Machine::commit() hasn't been called there
10252 * may be extra NIC settings in the vector. */
10253 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10254 {
10255 settings::NetworkAdapter nic;
10256 nic.ulSlot = (uint32_t)slot;
10257 /* paranoia check... must not be NULL, but must not crash either. */
10258 if (mNetworkAdapters[slot])
10259 {
10260 if (mNetworkAdapters[slot]->i_hasDefaults())
10261 continue;
10262
10263 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10264 if (FAILED(rc)) throw rc;
10265
10266 data.llNetworkAdapters.push_back(nic);
10267 }
10268 }
10269
10270 /* Serial ports */
10271 data.llSerialPorts.clear();
10272 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10273 {
10274 if (mSerialPorts[slot]->i_hasDefaults())
10275 continue;
10276
10277 settings::SerialPort s;
10278 s.ulSlot = slot;
10279 rc = mSerialPorts[slot]->i_saveSettings(s);
10280 if (FAILED(rc)) return rc;
10281
10282 data.llSerialPorts.push_back(s);
10283 }
10284
10285 /* Parallel ports */
10286 data.llParallelPorts.clear();
10287 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10288 {
10289 if (mParallelPorts[slot]->i_hasDefaults())
10290 continue;
10291
10292 settings::ParallelPort p;
10293 p.ulSlot = slot;
10294 rc = mParallelPorts[slot]->i_saveSettings(p);
10295 if (FAILED(rc)) return rc;
10296
10297 data.llParallelPorts.push_back(p);
10298 }
10299
10300 /* Audio adapter */
10301 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10302 if (FAILED(rc)) return rc;
10303
10304 rc = i_saveStorageControllers(data.storage);
10305 if (FAILED(rc)) return rc;
10306
10307 /* Shared folders */
10308 data.llSharedFolders.clear();
10309 for (HWData::SharedFolderList::const_iterator
10310 it = mHWData->mSharedFolders.begin();
10311 it != mHWData->mSharedFolders.end();
10312 ++it)
10313 {
10314 SharedFolder *pSF = *it;
10315 AutoCaller sfCaller(pSF);
10316 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10317 settings::SharedFolder sf;
10318 sf.strName = pSF->i_getName();
10319 sf.strHostPath = pSF->i_getHostPath();
10320 sf.fWritable = !!pSF->i_isWritable();
10321 sf.fAutoMount = !!pSF->i_isAutoMounted();
10322 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10323
10324 data.llSharedFolders.push_back(sf);
10325 }
10326
10327 // clipboard
10328 data.clipboardMode = mHWData->mClipboardMode;
10329 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10330
10331 // drag'n'drop
10332 data.dndMode = mHWData->mDnDMode;
10333
10334 /* Guest */
10335 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10336
10337 // IO settings
10338 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10339 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10340
10341 /* BandwidthControl (required) */
10342 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10343 if (FAILED(rc)) throw rc;
10344
10345 /* Host PCI devices */
10346 data.pciAttachments.clear();
10347 for (HWData::PCIDeviceAssignmentList::const_iterator
10348 it = mHWData->mPCIDeviceAssignments.begin();
10349 it != mHWData->mPCIDeviceAssignments.end();
10350 ++it)
10351 {
10352 ComObjPtr<PCIDeviceAttachment> pda = *it;
10353 settings::HostPCIDeviceAttachment hpda;
10354
10355 rc = pda->i_saveSettings(hpda);
10356 if (FAILED(rc)) throw rc;
10357
10358 data.pciAttachments.push_back(hpda);
10359 }
10360
10361 // guest properties
10362 data.llGuestProperties.clear();
10363#ifdef VBOX_WITH_GUEST_PROPS
10364 for (HWData::GuestPropertyMap::const_iterator
10365 it = mHWData->mGuestProperties.begin();
10366 it != mHWData->mGuestProperties.end();
10367 ++it)
10368 {
10369 HWData::GuestProperty property = it->second;
10370
10371 /* Remove transient guest properties at shutdown unless we
10372 * are saving state. Note that restoring snapshot intentionally
10373 * keeps them, they will be removed if appropriate once the final
10374 * machine state is set (as crashes etc. need to work). */
10375 if ( ( mData->mMachineState == MachineState_PoweredOff
10376 || mData->mMachineState == MachineState_Aborted
10377 || mData->mMachineState == MachineState_Teleported)
10378 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10379 continue;
10380 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10381 prop.strName = it->first;
10382 prop.strValue = property.strValue;
10383 prop.timestamp = (uint64_t)property.mTimestamp;
10384 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10385 GuestPropWriteFlags(property.mFlags, szFlags);
10386 prop.strFlags = szFlags;
10387
10388 data.llGuestProperties.push_back(prop);
10389 }
10390
10391 /* I presume this doesn't require a backup(). */
10392 mData->mGuestPropertiesModified = FALSE;
10393#endif /* VBOX_WITH_GUEST_PROPS defined */
10394
10395 *pDbg = mHWData->mDebugging;
10396 *pAutostart = mHWData->mAutostart;
10397
10398 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10399 }
10400 catch (std::bad_alloc &)
10401 {
10402 return E_OUTOFMEMORY;
10403 }
10404
10405 AssertComRC(rc);
10406 return rc;
10407}
10408
10409/**
10410 * Saves the storage controller configuration.
10411 *
10412 * @param data storage settings.
10413 */
10414HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10415{
10416 data.llStorageControllers.clear();
10417
10418 for (StorageControllerList::const_iterator
10419 it = mStorageControllers->begin();
10420 it != mStorageControllers->end();
10421 ++it)
10422 {
10423 HRESULT rc;
10424 ComObjPtr<StorageController> pCtl = *it;
10425
10426 settings::StorageController ctl;
10427 ctl.strName = pCtl->i_getName();
10428 ctl.controllerType = pCtl->i_getControllerType();
10429 ctl.storageBus = pCtl->i_getStorageBus();
10430 ctl.ulInstance = pCtl->i_getInstance();
10431 ctl.fBootable = pCtl->i_getBootable();
10432
10433 /* Save the port count. */
10434 ULONG portCount;
10435 rc = pCtl->COMGETTER(PortCount)(&portCount);
10436 ComAssertComRCRet(rc, rc);
10437 ctl.ulPortCount = portCount;
10438
10439 /* Save fUseHostIOCache */
10440 BOOL fUseHostIOCache;
10441 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10442 ComAssertComRCRet(rc, rc);
10443 ctl.fUseHostIOCache = !!fUseHostIOCache;
10444
10445 /* save the devices now. */
10446 rc = i_saveStorageDevices(pCtl, ctl);
10447 ComAssertComRCRet(rc, rc);
10448
10449 data.llStorageControllers.push_back(ctl);
10450 }
10451
10452 return S_OK;
10453}
10454
10455/**
10456 * Saves the hard disk configuration.
10457 */
10458HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10459 settings::StorageController &data)
10460{
10461 MediumAttachmentList atts;
10462
10463 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10464 if (FAILED(rc)) return rc;
10465
10466 data.llAttachedDevices.clear();
10467 for (MediumAttachmentList::const_iterator
10468 it = atts.begin();
10469 it != atts.end();
10470 ++it)
10471 {
10472 settings::AttachedDevice dev;
10473 IMediumAttachment *iA = *it;
10474 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10475 Medium *pMedium = pAttach->i_getMedium();
10476
10477 dev.deviceType = pAttach->i_getType();
10478 dev.lPort = pAttach->i_getPort();
10479 dev.lDevice = pAttach->i_getDevice();
10480 dev.fPassThrough = pAttach->i_getPassthrough();
10481 dev.fHotPluggable = pAttach->i_getHotPluggable();
10482 if (pMedium)
10483 {
10484 if (pMedium->i_isHostDrive())
10485 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10486 else
10487 dev.uuid = pMedium->i_getId();
10488 dev.fTempEject = pAttach->i_getTempEject();
10489 dev.fNonRotational = pAttach->i_getNonRotational();
10490 dev.fDiscard = pAttach->i_getDiscard();
10491 }
10492
10493 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10494
10495 data.llAttachedDevices.push_back(dev);
10496 }
10497
10498 return S_OK;
10499}
10500
10501/**
10502 * Saves machine state settings as defined by aFlags
10503 * (SaveSTS_* values).
10504 *
10505 * @param aFlags Combination of SaveSTS_* flags.
10506 *
10507 * @note Locks objects for writing.
10508 */
10509HRESULT Machine::i_saveStateSettings(int aFlags)
10510{
10511 if (aFlags == 0)
10512 return S_OK;
10513
10514 AutoCaller autoCaller(this);
10515 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10516
10517 /* This object's write lock is also necessary to serialize file access
10518 * (prevent concurrent reads and writes) */
10519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10520
10521 HRESULT rc = S_OK;
10522
10523 Assert(mData->pMachineConfigFile);
10524
10525 try
10526 {
10527 if (aFlags & SaveSTS_CurStateModified)
10528 mData->pMachineConfigFile->fCurrentStateModified = true;
10529
10530 if (aFlags & SaveSTS_StateFilePath)
10531 {
10532 if (!mSSData->strStateFilePath.isEmpty())
10533 /* try to make the file name relative to the settings file dir */
10534 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10535 else
10536 mData->pMachineConfigFile->strStateFile.setNull();
10537 }
10538
10539 if (aFlags & SaveSTS_StateTimeStamp)
10540 {
10541 Assert( mData->mMachineState != MachineState_Aborted
10542 || mSSData->strStateFilePath.isEmpty());
10543
10544 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10545
10546 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10547/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10548 }
10549
10550 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10551 }
10552 catch (...)
10553 {
10554 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10555 }
10556
10557 return rc;
10558}
10559
10560/**
10561 * Ensures that the given medium is added to a media registry. If this machine
10562 * was created with 4.0 or later, then the machine registry is used. Otherwise
10563 * the global VirtualBox media registry is used.
10564 *
10565 * Caller must NOT hold machine lock, media tree or any medium locks!
10566 *
10567 * @param pMedium
10568 */
10569void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10570{
10571 /* Paranoia checks: do not hold machine or media tree locks. */
10572 AssertReturnVoid(!isWriteLockOnCurrentThread());
10573 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10574
10575 ComObjPtr<Medium> pBase;
10576 {
10577 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10578 pBase = pMedium->i_getBase();
10579 }
10580
10581 /* Paranoia checks: do not hold medium locks. */
10582 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10583 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10584
10585 // decide which medium registry to use now that the medium is attached:
10586 Guid uuid;
10587 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10588 if (fCanHaveOwnMediaRegistry)
10589 // machine XML is VirtualBox 4.0 or higher:
10590 uuid = i_getId(); // machine UUID
10591 else
10592 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10593
10594 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10595 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10596 if (pMedium->i_addRegistry(uuid))
10597 mParent->i_markRegistryModified(uuid);
10598
10599 /* For more complex hard disk structures it can happen that the base
10600 * medium isn't yet associated with any medium registry. Do that now. */
10601 if (pMedium != pBase)
10602 {
10603 /* Tree lock needed by Medium::addRegistry when recursing. */
10604 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10605 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10606 {
10607 treeLock.release();
10608 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10609 treeLock.acquire();
10610 }
10611 if (pBase->i_addRegistryRecursive(uuid))
10612 {
10613 treeLock.release();
10614 mParent->i_markRegistryModified(uuid);
10615 }
10616 }
10617}
10618
10619/**
10620 * Creates differencing hard disks for all normal hard disks attached to this
10621 * machine and a new set of attachments to refer to created disks.
10622 *
10623 * Used when taking a snapshot or when deleting the current state. Gets called
10624 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10625 *
10626 * This method assumes that mMediumAttachments contains the original hard disk
10627 * attachments it needs to create diffs for. On success, these attachments will
10628 * be replaced with the created diffs.
10629 *
10630 * Attachments with non-normal hard disks are left as is.
10631 *
10632 * If @a aOnline is @c false then the original hard disks that require implicit
10633 * diffs will be locked for reading. Otherwise it is assumed that they are
10634 * already locked for writing (when the VM was started). Note that in the latter
10635 * case it is responsibility of the caller to lock the newly created diffs for
10636 * writing if this method succeeds.
10637 *
10638 * @param aProgress Progress object to run (must contain at least as
10639 * many operations left as the number of hard disks
10640 * attached).
10641 * @param aWeight Weight of this operation.
10642 * @param aOnline Whether the VM was online prior to this operation.
10643 *
10644 * @note The progress object is not marked as completed, neither on success nor
10645 * on failure. This is a responsibility of the caller.
10646 *
10647 * @note Locks this object and the media tree for writing.
10648 */
10649HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10650 ULONG aWeight,
10651 bool aOnline)
10652{
10653 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10654
10655 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10656 AssertReturn(!!pProgressControl, E_INVALIDARG);
10657
10658 AutoCaller autoCaller(this);
10659 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10660
10661 AutoMultiWriteLock2 alock(this->lockHandle(),
10662 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10663
10664 /* must be in a protective state because we release the lock below */
10665 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10666 || mData->mMachineState == MachineState_OnlineSnapshotting
10667 || mData->mMachineState == MachineState_LiveSnapshotting
10668 || mData->mMachineState == MachineState_RestoringSnapshot
10669 || mData->mMachineState == MachineState_DeletingSnapshot
10670 , E_FAIL);
10671
10672 HRESULT rc = S_OK;
10673
10674 // use appropriate locked media map (online or offline)
10675 MediumLockListMap lockedMediaOffline;
10676 MediumLockListMap *lockedMediaMap;
10677 if (aOnline)
10678 lockedMediaMap = &mData->mSession.mLockedMedia;
10679 else
10680 lockedMediaMap = &lockedMediaOffline;
10681
10682 try
10683 {
10684 if (!aOnline)
10685 {
10686 /* lock all attached hard disks early to detect "in use"
10687 * situations before creating actual diffs */
10688 for (MediumAttachmentList::const_iterator
10689 it = mMediumAttachments->begin();
10690 it != mMediumAttachments->end();
10691 ++it)
10692 {
10693 MediumAttachment *pAtt = *it;
10694 if (pAtt->i_getType() == DeviceType_HardDisk)
10695 {
10696 Medium *pMedium = pAtt->i_getMedium();
10697 Assert(pMedium);
10698
10699 MediumLockList *pMediumLockList(new MediumLockList());
10700 alock.release();
10701 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10702 NULL /* pToLockWrite */,
10703 false /* fMediumLockWriteAll */,
10704 NULL,
10705 *pMediumLockList);
10706 alock.acquire();
10707 if (FAILED(rc))
10708 {
10709 delete pMediumLockList;
10710 throw rc;
10711 }
10712 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10713 if (FAILED(rc))
10714 {
10715 throw setError(rc,
10716 tr("Collecting locking information for all attached media failed"));
10717 }
10718 }
10719 }
10720
10721 /* Now lock all media. If this fails, nothing is locked. */
10722 alock.release();
10723 rc = lockedMediaMap->Lock();
10724 alock.acquire();
10725 if (FAILED(rc))
10726 {
10727 throw setError(rc,
10728 tr("Locking of attached media failed"));
10729 }
10730 }
10731
10732 /* remember the current list (note that we don't use backup() since
10733 * mMediumAttachments may be already backed up) */
10734 MediumAttachmentList atts = *mMediumAttachments.data();
10735
10736 /* start from scratch */
10737 mMediumAttachments->clear();
10738
10739 /* go through remembered attachments and create diffs for normal hard
10740 * disks and attach them */
10741 for (MediumAttachmentList::const_iterator
10742 it = atts.begin();
10743 it != atts.end();
10744 ++it)
10745 {
10746 MediumAttachment *pAtt = *it;
10747
10748 DeviceType_T devType = pAtt->i_getType();
10749 Medium *pMedium = pAtt->i_getMedium();
10750
10751 if ( devType != DeviceType_HardDisk
10752 || pMedium == NULL
10753 || pMedium->i_getType() != MediumType_Normal)
10754 {
10755 /* copy the attachment as is */
10756
10757 /** @todo the progress object created in SessionMachine::TakeSnaphot
10758 * only expects operations for hard disks. Later other
10759 * device types need to show up in the progress as well. */
10760 if (devType == DeviceType_HardDisk)
10761 {
10762 if (pMedium == NULL)
10763 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10764 aWeight); // weight
10765 else
10766 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10767 pMedium->i_getBase()->i_getName().c_str()).raw(),
10768 aWeight); // weight
10769 }
10770
10771 mMediumAttachments->push_back(pAtt);
10772 continue;
10773 }
10774
10775 /* need a diff */
10776 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10777 pMedium->i_getBase()->i_getName().c_str()).raw(),
10778 aWeight); // weight
10779
10780 Utf8Str strFullSnapshotFolder;
10781 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10782
10783 ComObjPtr<Medium> diff;
10784 diff.createObject();
10785 // store the diff in the same registry as the parent
10786 // (this cannot fail here because we can't create implicit diffs for
10787 // unregistered images)
10788 Guid uuidRegistryParent;
10789 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10790 Assert(fInRegistry); NOREF(fInRegistry);
10791 rc = diff->init(mParent,
10792 pMedium->i_getPreferredDiffFormat(),
10793 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10794 uuidRegistryParent,
10795 DeviceType_HardDisk);
10796 if (FAILED(rc)) throw rc;
10797
10798 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10799 * the push_back? Looks like we're going to release medium with the
10800 * wrong kind of lock (general issue with if we fail anywhere at all)
10801 * and an orphaned VDI in the snapshots folder. */
10802
10803 /* update the appropriate lock list */
10804 MediumLockList *pMediumLockList;
10805 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10806 AssertComRCThrowRC(rc);
10807 if (aOnline)
10808 {
10809 alock.release();
10810 /* The currently attached medium will be read-only, change
10811 * the lock type to read. */
10812 rc = pMediumLockList->Update(pMedium, false);
10813 alock.acquire();
10814 AssertComRCThrowRC(rc);
10815 }
10816
10817 /* release the locks before the potentially lengthy operation */
10818 alock.release();
10819 rc = pMedium->i_createDiffStorage(diff,
10820 pMedium->i_getPreferredDiffVariant(),
10821 pMediumLockList,
10822 NULL /* aProgress */,
10823 true /* aWait */,
10824 false /* aNotify */);
10825 alock.acquire();
10826 if (FAILED(rc)) throw rc;
10827
10828 /* actual lock list update is done in Machine::i_commitMedia */
10829
10830 rc = diff->i_addBackReference(mData->mUuid);
10831 AssertComRCThrowRC(rc);
10832
10833 /* add a new attachment */
10834 ComObjPtr<MediumAttachment> attachment;
10835 attachment.createObject();
10836 rc = attachment->init(this,
10837 diff,
10838 pAtt->i_getControllerName(),
10839 pAtt->i_getPort(),
10840 pAtt->i_getDevice(),
10841 DeviceType_HardDisk,
10842 true /* aImplicit */,
10843 false /* aPassthrough */,
10844 false /* aTempEject */,
10845 pAtt->i_getNonRotational(),
10846 pAtt->i_getDiscard(),
10847 pAtt->i_getHotPluggable(),
10848 pAtt->i_getBandwidthGroup());
10849 if (FAILED(rc)) throw rc;
10850
10851 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10852 AssertComRCThrowRC(rc);
10853 mMediumAttachments->push_back(attachment);
10854 }
10855 }
10856 catch (HRESULT aRC) { rc = aRC; }
10857
10858 /* unlock all hard disks we locked when there is no VM */
10859 if (!aOnline)
10860 {
10861 ErrorInfoKeeper eik;
10862
10863 HRESULT rc1 = lockedMediaMap->Clear();
10864 AssertComRC(rc1);
10865 }
10866
10867 return rc;
10868}
10869
10870/**
10871 * Deletes implicit differencing hard disks created either by
10872 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10873 * mMediumAttachments.
10874 *
10875 * Note that to delete hard disks created by #attachDevice() this method is
10876 * called from #i_rollbackMedia() when the changes are rolled back.
10877 *
10878 * @note Locks this object and the media tree for writing.
10879 */
10880HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10881{
10882 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10883
10884 AutoCaller autoCaller(this);
10885 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10886
10887 AutoMultiWriteLock2 alock(this->lockHandle(),
10888 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10889
10890 /* We absolutely must have backed up state. */
10891 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10892
10893 /* Check if there are any implicitly created diff images. */
10894 bool fImplicitDiffs = false;
10895 for (MediumAttachmentList::const_iterator
10896 it = mMediumAttachments->begin();
10897 it != mMediumAttachments->end();
10898 ++it)
10899 {
10900 const ComObjPtr<MediumAttachment> &pAtt = *it;
10901 if (pAtt->i_isImplicit())
10902 {
10903 fImplicitDiffs = true;
10904 break;
10905 }
10906 }
10907 /* If there is nothing to do, leave early. This saves lots of image locking
10908 * effort. It also avoids a MachineStateChanged event without real reason.
10909 * This is important e.g. when loading a VM config, because there should be
10910 * no events. Otherwise API clients can become thoroughly confused for
10911 * inaccessible VMs (the code for loading VM configs uses this method for
10912 * cleanup if the config makes no sense), as they take such events as an
10913 * indication that the VM is alive, and they would force the VM config to
10914 * be reread, leading to an endless loop. */
10915 if (!fImplicitDiffs)
10916 return S_OK;
10917
10918 HRESULT rc = S_OK;
10919 MachineState_T oldState = mData->mMachineState;
10920
10921 /* will release the lock before the potentially lengthy operation,
10922 * so protect with the special state (unless already protected) */
10923 if ( oldState != MachineState_Snapshotting
10924 && oldState != MachineState_OnlineSnapshotting
10925 && oldState != MachineState_LiveSnapshotting
10926 && oldState != MachineState_RestoringSnapshot
10927 && oldState != MachineState_DeletingSnapshot
10928 && oldState != MachineState_DeletingSnapshotOnline
10929 && oldState != MachineState_DeletingSnapshotPaused
10930 )
10931 i_setMachineState(MachineState_SettingUp);
10932
10933 // use appropriate locked media map (online or offline)
10934 MediumLockListMap lockedMediaOffline;
10935 MediumLockListMap *lockedMediaMap;
10936 if (aOnline)
10937 lockedMediaMap = &mData->mSession.mLockedMedia;
10938 else
10939 lockedMediaMap = &lockedMediaOffline;
10940
10941 try
10942 {
10943 if (!aOnline)
10944 {
10945 /* lock all attached hard disks early to detect "in use"
10946 * situations before deleting actual diffs */
10947 for (MediumAttachmentList::const_iterator
10948 it = mMediumAttachments->begin();
10949 it != mMediumAttachments->end();
10950 ++it)
10951 {
10952 MediumAttachment *pAtt = *it;
10953 if (pAtt->i_getType() == DeviceType_HardDisk)
10954 {
10955 Medium *pMedium = pAtt->i_getMedium();
10956 Assert(pMedium);
10957
10958 MediumLockList *pMediumLockList(new MediumLockList());
10959 alock.release();
10960 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10961 NULL /* pToLockWrite */,
10962 false /* fMediumLockWriteAll */,
10963 NULL,
10964 *pMediumLockList);
10965 alock.acquire();
10966
10967 if (FAILED(rc))
10968 {
10969 delete pMediumLockList;
10970 throw rc;
10971 }
10972
10973 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10974 if (FAILED(rc))
10975 throw rc;
10976 }
10977 }
10978
10979 if (FAILED(rc))
10980 throw rc;
10981 } // end of offline
10982
10983 /* Lock lists are now up to date and include implicitly created media */
10984
10985 /* Go through remembered attachments and delete all implicitly created
10986 * diffs and fix up the attachment information */
10987 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10988 MediumAttachmentList implicitAtts;
10989 for (MediumAttachmentList::const_iterator
10990 it = mMediumAttachments->begin();
10991 it != mMediumAttachments->end();
10992 ++it)
10993 {
10994 ComObjPtr<MediumAttachment> pAtt = *it;
10995 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10996 if (pMedium.isNull())
10997 continue;
10998
10999 // Implicit attachments go on the list for deletion and back references are removed.
11000 if (pAtt->i_isImplicit())
11001 {
11002 /* Deassociate and mark for deletion */
11003 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11004 rc = pMedium->i_removeBackReference(mData->mUuid);
11005 if (FAILED(rc))
11006 throw rc;
11007 implicitAtts.push_back(pAtt);
11008 continue;
11009 }
11010
11011 /* Was this medium attached before? */
11012 if (!i_findAttachment(oldAtts, pMedium))
11013 {
11014 /* no: de-associate */
11015 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11016 rc = pMedium->i_removeBackReference(mData->mUuid);
11017 if (FAILED(rc))
11018 throw rc;
11019 continue;
11020 }
11021 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11022 }
11023
11024 /* If there are implicit attachments to delete, throw away the lock
11025 * map contents (which will unlock all media) since the medium
11026 * attachments will be rolled back. Below we need to completely
11027 * recreate the lock map anyway since it is infinitely complex to
11028 * do this incrementally (would need reconstructing each attachment
11029 * change, which would be extremely hairy). */
11030 if (implicitAtts.size() != 0)
11031 {
11032 ErrorInfoKeeper eik;
11033
11034 HRESULT rc1 = lockedMediaMap->Clear();
11035 AssertComRC(rc1);
11036 }
11037
11038 /* rollback hard disk changes */
11039 mMediumAttachments.rollback();
11040
11041 MultiResult mrc(S_OK);
11042
11043 // Delete unused implicit diffs.
11044 if (implicitAtts.size() != 0)
11045 {
11046 alock.release();
11047
11048 for (MediumAttachmentList::const_iterator
11049 it = implicitAtts.begin();
11050 it != implicitAtts.end();
11051 ++it)
11052 {
11053 // Remove medium associated with this attachment.
11054 ComObjPtr<MediumAttachment> pAtt = *it;
11055 Assert(pAtt);
11056 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11057 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11058 Assert(pMedium);
11059
11060 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11061 // continue on delete failure, just collect error messages
11062 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11063 pMedium->i_getLocationFull().c_str() ));
11064 mrc = rc;
11065 }
11066 // Clear the list of deleted implicit attachments now, while not
11067 // holding the lock, as it will ultimately trigger Medium::uninit()
11068 // calls which assume that the media tree lock isn't held.
11069 implicitAtts.clear();
11070
11071 alock.acquire();
11072
11073 /* if there is a VM recreate media lock map as mentioned above,
11074 * otherwise it is a waste of time and we leave things unlocked */
11075 if (aOnline)
11076 {
11077 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11078 /* must never be NULL, but better safe than sorry */
11079 if (!pMachine.isNull())
11080 {
11081 alock.release();
11082 rc = mData->mSession.mMachine->i_lockMedia();
11083 alock.acquire();
11084 if (FAILED(rc))
11085 throw rc;
11086 }
11087 }
11088 }
11089 }
11090 catch (HRESULT aRC) {rc = aRC;}
11091
11092 if (mData->mMachineState == MachineState_SettingUp)
11093 i_setMachineState(oldState);
11094
11095 /* unlock all hard disks we locked when there is no VM */
11096 if (!aOnline)
11097 {
11098 ErrorInfoKeeper eik;
11099
11100 HRESULT rc1 = lockedMediaMap->Clear();
11101 AssertComRC(rc1);
11102 }
11103
11104 return rc;
11105}
11106
11107
11108/**
11109 * Looks through the given list of media attachments for one with the given parameters
11110 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11111 * can be searched as well if needed.
11112 *
11113 * @param ll
11114 * @param aControllerName
11115 * @param aControllerPort
11116 * @param aDevice
11117 * @return
11118 */
11119MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11120 const Utf8Str &aControllerName,
11121 LONG aControllerPort,
11122 LONG aDevice)
11123{
11124 for (MediumAttachmentList::const_iterator
11125 it = ll.begin();
11126 it != ll.end();
11127 ++it)
11128 {
11129 MediumAttachment *pAttach = *it;
11130 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11131 return pAttach;
11132 }
11133
11134 return NULL;
11135}
11136
11137/**
11138 * Looks through the given list of media attachments for one with the given parameters
11139 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11140 * can be searched as well if needed.
11141 *
11142 * @param ll
11143 * @param pMedium
11144 * @return
11145 */
11146MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11147 ComObjPtr<Medium> pMedium)
11148{
11149 for (MediumAttachmentList::const_iterator
11150 it = ll.begin();
11151 it != ll.end();
11152 ++it)
11153 {
11154 MediumAttachment *pAttach = *it;
11155 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11156 if (pMediumThis == pMedium)
11157 return pAttach;
11158 }
11159
11160 return NULL;
11161}
11162
11163/**
11164 * Looks through the given list of media attachments for one with the given parameters
11165 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11166 * can be searched as well if needed.
11167 *
11168 * @param ll
11169 * @param id
11170 * @return
11171 */
11172MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11173 Guid &id)
11174{
11175 for (MediumAttachmentList::const_iterator
11176 it = ll.begin();
11177 it != ll.end();
11178 ++it)
11179 {
11180 MediumAttachment *pAttach = *it;
11181 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11182 if (pMediumThis->i_getId() == id)
11183 return pAttach;
11184 }
11185
11186 return NULL;
11187}
11188
11189/**
11190 * Main implementation for Machine::DetachDevice. This also gets called
11191 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11192 *
11193 * @param pAttach Medium attachment to detach.
11194 * @param writeLock Machine write lock which the caller must have locked once.
11195 * This may be released temporarily in here.
11196 * @param pSnapshot If NULL, then the detachment is for the current machine.
11197 * Otherwise this is for a SnapshotMachine, and this must be
11198 * its snapshot.
11199 * @return
11200 */
11201HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11202 AutoWriteLock &writeLock,
11203 Snapshot *pSnapshot)
11204{
11205 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11206 DeviceType_T mediumType = pAttach->i_getType();
11207
11208 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11209
11210 if (pAttach->i_isImplicit())
11211 {
11212 /* attempt to implicitly delete the implicitly created diff */
11213
11214 /// @todo move the implicit flag from MediumAttachment to Medium
11215 /// and forbid any hard disk operation when it is implicit. Or maybe
11216 /// a special media state for it to make it even more simple.
11217
11218 Assert(mMediumAttachments.isBackedUp());
11219
11220 /* will release the lock before the potentially lengthy operation, so
11221 * protect with the special state */
11222 MachineState_T oldState = mData->mMachineState;
11223 i_setMachineState(MachineState_SettingUp);
11224
11225 writeLock.release();
11226
11227 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11228 true /*aWait*/,
11229 false /*aNotify*/);
11230
11231 writeLock.acquire();
11232
11233 i_setMachineState(oldState);
11234
11235 if (FAILED(rc)) return rc;
11236 }
11237
11238 i_setModified(IsModified_Storage);
11239 mMediumAttachments.backup();
11240 mMediumAttachments->remove(pAttach);
11241
11242 if (!oldmedium.isNull())
11243 {
11244 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11245 if (pSnapshot)
11246 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11247 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11248 else if (mediumType != DeviceType_HardDisk)
11249 oldmedium->i_removeBackReference(mData->mUuid);
11250 }
11251
11252 return S_OK;
11253}
11254
11255/**
11256 * Goes thru all media of the given list and
11257 *
11258 * 1) calls i_detachDevice() on each of them for this machine and
11259 * 2) adds all Medium objects found in the process to the given list,
11260 * depending on cleanupMode.
11261 *
11262 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11263 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11264 * media to the list.
11265 *
11266 * This gets called from Machine::Unregister, both for the actual Machine and
11267 * the SnapshotMachine objects that might be found in the snapshots.
11268 *
11269 * Requires caller and locking. The machine lock must be passed in because it
11270 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11271 *
11272 * @param writeLock Machine lock from top-level caller; this gets passed to
11273 * i_detachDevice.
11274 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11275 * object if called for a SnapshotMachine.
11276 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11277 * added to llMedia; if Full, then all media get added;
11278 * otherwise no media get added.
11279 * @param llMedia Caller's list to receive Medium objects which got detached so
11280 * caller can close() them, depending on cleanupMode.
11281 * @return
11282 */
11283HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11284 Snapshot *pSnapshot,
11285 CleanupMode_T cleanupMode,
11286 MediaList &llMedia)
11287{
11288 Assert(isWriteLockOnCurrentThread());
11289
11290 HRESULT rc;
11291
11292 // make a temporary list because i_detachDevice invalidates iterators into
11293 // mMediumAttachments
11294 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11295
11296 for (MediumAttachmentList::iterator
11297 it = llAttachments2.begin();
11298 it != llAttachments2.end();
11299 ++it)
11300 {
11301 ComObjPtr<MediumAttachment> &pAttach = *it;
11302 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11303
11304 if (!pMedium.isNull())
11305 {
11306 AutoCaller mac(pMedium);
11307 if (FAILED(mac.rc())) return mac.rc();
11308 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11309 DeviceType_T devType = pMedium->i_getDeviceType();
11310 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11311 && devType == DeviceType_HardDisk)
11312 || (cleanupMode == CleanupMode_Full)
11313 )
11314 {
11315 llMedia.push_back(pMedium);
11316 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11317 /* Not allowed to keep this lock as below we need the parent
11318 * medium lock, and the lock order is parent to child. */
11319 lock.release();
11320 /*
11321 * Search for medias which are not attached to any machine, but
11322 * in the chain to an attached disk. Mediums are only consided
11323 * if they are:
11324 * - have only one child
11325 * - no references to any machines
11326 * - are of normal medium type
11327 */
11328 while (!pParent.isNull())
11329 {
11330 AutoCaller mac1(pParent);
11331 if (FAILED(mac1.rc())) return mac1.rc();
11332 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11333 if (pParent->i_getChildren().size() == 1)
11334 {
11335 if ( pParent->i_getMachineBackRefCount() == 0
11336 && pParent->i_getType() == MediumType_Normal
11337 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11338 llMedia.push_back(pParent);
11339 }
11340 else
11341 break;
11342 pParent = pParent->i_getParent();
11343 }
11344 }
11345 }
11346
11347 // real machine: then we need to use the proper method
11348 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11349
11350 if (FAILED(rc))
11351 return rc;
11352 }
11353
11354 return S_OK;
11355}
11356
11357/**
11358 * Perform deferred hard disk detachments.
11359 *
11360 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11361 * changed (not backed up).
11362 *
11363 * If @a aOnline is @c true then this method will also unlock the old hard
11364 * disks for which the new implicit diffs were created and will lock these new
11365 * diffs for writing.
11366 *
11367 * @param aOnline Whether the VM was online prior to this operation.
11368 *
11369 * @note Locks this object for writing!
11370 */
11371void Machine::i_commitMedia(bool aOnline /*= false*/)
11372{
11373 AutoCaller autoCaller(this);
11374 AssertComRCReturnVoid(autoCaller.rc());
11375
11376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11377
11378 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11379
11380 HRESULT rc = S_OK;
11381
11382 /* no attach/detach operations -- nothing to do */
11383 if (!mMediumAttachments.isBackedUp())
11384 return;
11385
11386 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11387 bool fMediaNeedsLocking = false;
11388
11389 /* enumerate new attachments */
11390 for (MediumAttachmentList::const_iterator
11391 it = mMediumAttachments->begin();
11392 it != mMediumAttachments->end();
11393 ++it)
11394 {
11395 MediumAttachment *pAttach = *it;
11396
11397 pAttach->i_commit();
11398
11399 Medium *pMedium = pAttach->i_getMedium();
11400 bool fImplicit = pAttach->i_isImplicit();
11401
11402 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11403 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11404 fImplicit));
11405
11406 /** @todo convert all this Machine-based voodoo to MediumAttachment
11407 * based commit logic. */
11408 if (fImplicit)
11409 {
11410 /* convert implicit attachment to normal */
11411 pAttach->i_setImplicit(false);
11412
11413 if ( aOnline
11414 && pMedium
11415 && pAttach->i_getType() == DeviceType_HardDisk
11416 )
11417 {
11418 /* update the appropriate lock list */
11419 MediumLockList *pMediumLockList;
11420 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11421 AssertComRC(rc);
11422 if (pMediumLockList)
11423 {
11424 /* unlock if there's a need to change the locking */
11425 if (!fMediaNeedsLocking)
11426 {
11427 rc = mData->mSession.mLockedMedia.Unlock();
11428 AssertComRC(rc);
11429 fMediaNeedsLocking = true;
11430 }
11431 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11432 AssertComRC(rc);
11433 rc = pMediumLockList->Append(pMedium, true);
11434 AssertComRC(rc);
11435 }
11436 }
11437
11438 continue;
11439 }
11440
11441 if (pMedium)
11442 {
11443 /* was this medium attached before? */
11444 for (MediumAttachmentList::iterator
11445 oldIt = oldAtts.begin();
11446 oldIt != oldAtts.end();
11447 ++oldIt)
11448 {
11449 MediumAttachment *pOldAttach = *oldIt;
11450 if (pOldAttach->i_getMedium() == pMedium)
11451 {
11452 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11453
11454 /* yes: remove from old to avoid de-association */
11455 oldAtts.erase(oldIt);
11456 break;
11457 }
11458 }
11459 }
11460 }
11461
11462 /* enumerate remaining old attachments and de-associate from the
11463 * current machine state */
11464 for (MediumAttachmentList::const_iterator
11465 it = oldAtts.begin();
11466 it != oldAtts.end();
11467 ++it)
11468 {
11469 MediumAttachment *pAttach = *it;
11470 Medium *pMedium = pAttach->i_getMedium();
11471
11472 /* Detach only hard disks, since DVD/floppy media is detached
11473 * instantly in MountMedium. */
11474 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11475 {
11476 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11477
11478 /* now de-associate from the current machine state */
11479 rc = pMedium->i_removeBackReference(mData->mUuid);
11480 AssertComRC(rc);
11481
11482 if (aOnline)
11483 {
11484 /* unlock since medium is not used anymore */
11485 MediumLockList *pMediumLockList;
11486 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11487 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11488 {
11489 /* this happens for online snapshots, there the attachment
11490 * is changing, but only to a diff image created under
11491 * the old one, so there is no separate lock list */
11492 Assert(!pMediumLockList);
11493 }
11494 else
11495 {
11496 AssertComRC(rc);
11497 if (pMediumLockList)
11498 {
11499 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11500 AssertComRC(rc);
11501 }
11502 }
11503 }
11504 }
11505 }
11506
11507 /* take media locks again so that the locking state is consistent */
11508 if (fMediaNeedsLocking)
11509 {
11510 Assert(aOnline);
11511 rc = mData->mSession.mLockedMedia.Lock();
11512 AssertComRC(rc);
11513 }
11514
11515 /* commit the hard disk changes */
11516 mMediumAttachments.commit();
11517
11518 if (i_isSessionMachine())
11519 {
11520 /*
11521 * Update the parent machine to point to the new owner.
11522 * This is necessary because the stored parent will point to the
11523 * session machine otherwise and cause crashes or errors later
11524 * when the session machine gets invalid.
11525 */
11526 /** @todo Change the MediumAttachment class to behave like any other
11527 * class in this regard by creating peer MediumAttachment
11528 * objects for session machines and share the data with the peer
11529 * machine.
11530 */
11531 for (MediumAttachmentList::const_iterator
11532 it = mMediumAttachments->begin();
11533 it != mMediumAttachments->end();
11534 ++it)
11535 (*it)->i_updateParentMachine(mPeer);
11536
11537 /* attach new data to the primary machine and reshare it */
11538 mPeer->mMediumAttachments.attach(mMediumAttachments);
11539 }
11540
11541 return;
11542}
11543
11544/**
11545 * Perform deferred deletion of implicitly created diffs.
11546 *
11547 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11548 * changed (not backed up).
11549 *
11550 * @note Locks this object for writing!
11551 */
11552void Machine::i_rollbackMedia()
11553{
11554 AutoCaller autoCaller(this);
11555 AssertComRCReturnVoid(autoCaller.rc());
11556
11557 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11558 LogFlowThisFunc(("Entering rollbackMedia\n"));
11559
11560 HRESULT rc = S_OK;
11561
11562 /* no attach/detach operations -- nothing to do */
11563 if (!mMediumAttachments.isBackedUp())
11564 return;
11565
11566 /* enumerate new attachments */
11567 for (MediumAttachmentList::const_iterator
11568 it = mMediumAttachments->begin();
11569 it != mMediumAttachments->end();
11570 ++it)
11571 {
11572 MediumAttachment *pAttach = *it;
11573 /* Fix up the backrefs for DVD/floppy media. */
11574 if (pAttach->i_getType() != DeviceType_HardDisk)
11575 {
11576 Medium *pMedium = pAttach->i_getMedium();
11577 if (pMedium)
11578 {
11579 rc = pMedium->i_removeBackReference(mData->mUuid);
11580 AssertComRC(rc);
11581 }
11582 }
11583
11584 (*it)->i_rollback();
11585
11586 pAttach = *it;
11587 /* Fix up the backrefs for DVD/floppy media. */
11588 if (pAttach->i_getType() != DeviceType_HardDisk)
11589 {
11590 Medium *pMedium = pAttach->i_getMedium();
11591 if (pMedium)
11592 {
11593 rc = pMedium->i_addBackReference(mData->mUuid);
11594 AssertComRC(rc);
11595 }
11596 }
11597 }
11598
11599 /** @todo convert all this Machine-based voodoo to MediumAttachment
11600 * based rollback logic. */
11601 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11602
11603 return;
11604}
11605
11606/**
11607 * Returns true if the settings file is located in the directory named exactly
11608 * as the machine; this means, among other things, that the machine directory
11609 * should be auto-renamed.
11610 *
11611 * @param aSettingsDir if not NULL, the full machine settings file directory
11612 * name will be assigned there.
11613 *
11614 * @note Doesn't lock anything.
11615 * @note Not thread safe (must be called from this object's lock).
11616 */
11617bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11618{
11619 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11620 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11621 if (aSettingsDir)
11622 *aSettingsDir = strMachineDirName;
11623 strMachineDirName.stripPath(); // vmname
11624 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11625 strConfigFileOnly.stripPath() // vmname.vbox
11626 .stripSuffix(); // vmname
11627 /** @todo hack, make somehow use of ComposeMachineFilename */
11628 if (mUserData->s.fDirectoryIncludesUUID)
11629 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11630
11631 AssertReturn(!strMachineDirName.isEmpty(), false);
11632 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11633
11634 return strMachineDirName == strConfigFileOnly;
11635}
11636
11637/**
11638 * Discards all changes to machine settings.
11639 *
11640 * @param aNotify Whether to notify the direct session about changes or not.
11641 *
11642 * @note Locks objects for writing!
11643 */
11644void Machine::i_rollback(bool aNotify)
11645{
11646 AutoCaller autoCaller(this);
11647 AssertComRCReturn(autoCaller.rc(), (void)0);
11648
11649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11650
11651 if (!mStorageControllers.isNull())
11652 {
11653 if (mStorageControllers.isBackedUp())
11654 {
11655 /* unitialize all new devices (absent in the backed up list). */
11656 StorageControllerList *backedList = mStorageControllers.backedUpData();
11657 for (StorageControllerList::const_iterator
11658 it = mStorageControllers->begin();
11659 it != mStorageControllers->end();
11660 ++it)
11661 {
11662 if ( std::find(backedList->begin(), backedList->end(), *it)
11663 == backedList->end()
11664 )
11665 {
11666 (*it)->uninit();
11667 }
11668 }
11669
11670 /* restore the list */
11671 mStorageControllers.rollback();
11672 }
11673
11674 /* rollback any changes to devices after restoring the list */
11675 if (mData->flModifications & IsModified_Storage)
11676 {
11677 for (StorageControllerList::const_iterator
11678 it = mStorageControllers->begin();
11679 it != mStorageControllers->end();
11680 ++it)
11681 {
11682 (*it)->i_rollback();
11683 }
11684 }
11685 }
11686
11687 if (!mUSBControllers.isNull())
11688 {
11689 if (mUSBControllers.isBackedUp())
11690 {
11691 /* unitialize all new devices (absent in the backed up list). */
11692 USBControllerList *backedList = mUSBControllers.backedUpData();
11693 for (USBControllerList::const_iterator
11694 it = mUSBControllers->begin();
11695 it != mUSBControllers->end();
11696 ++it)
11697 {
11698 if ( std::find(backedList->begin(), backedList->end(), *it)
11699 == backedList->end()
11700 )
11701 {
11702 (*it)->uninit();
11703 }
11704 }
11705
11706 /* restore the list */
11707 mUSBControllers.rollback();
11708 }
11709
11710 /* rollback any changes to devices after restoring the list */
11711 if (mData->flModifications & IsModified_USB)
11712 {
11713 for (USBControllerList::const_iterator
11714 it = mUSBControllers->begin();
11715 it != mUSBControllers->end();
11716 ++it)
11717 {
11718 (*it)->i_rollback();
11719 }
11720 }
11721 }
11722
11723 mUserData.rollback();
11724
11725 mHWData.rollback();
11726
11727 if (mData->flModifications & IsModified_Storage)
11728 i_rollbackMedia();
11729
11730 if (mBIOSSettings)
11731 mBIOSSettings->i_rollback();
11732
11733 if (mTrustedPlatformModule)
11734 mTrustedPlatformModule->i_rollback();
11735
11736 if (mNvramStore)
11737 mNvramStore->i_rollback();
11738
11739 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11740 mRecordingSettings->i_rollback();
11741
11742 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11743 mGraphicsAdapter->i_rollback();
11744
11745 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11746 mVRDEServer->i_rollback();
11747
11748 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11749 mAudioAdapter->i_rollback();
11750
11751 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11752 mUSBDeviceFilters->i_rollback();
11753
11754 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11755 mBandwidthControl->i_rollback();
11756
11757 if (!mHWData.isNull())
11758 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11759 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11760 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11761 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11762
11763 if (mData->flModifications & IsModified_NetworkAdapters)
11764 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11765 if ( mNetworkAdapters[slot]
11766 && mNetworkAdapters[slot]->i_isModified())
11767 {
11768 mNetworkAdapters[slot]->i_rollback();
11769 networkAdapters[slot] = mNetworkAdapters[slot];
11770 }
11771
11772 if (mData->flModifications & IsModified_SerialPorts)
11773 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11774 if ( mSerialPorts[slot]
11775 && mSerialPorts[slot]->i_isModified())
11776 {
11777 mSerialPorts[slot]->i_rollback();
11778 serialPorts[slot] = mSerialPorts[slot];
11779 }
11780
11781 if (mData->flModifications & IsModified_ParallelPorts)
11782 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11783 if ( mParallelPorts[slot]
11784 && mParallelPorts[slot]->i_isModified())
11785 {
11786 mParallelPorts[slot]->i_rollback();
11787 parallelPorts[slot] = mParallelPorts[slot];
11788 }
11789
11790 if (aNotify)
11791 {
11792 /* inform the direct session about changes */
11793
11794 ComObjPtr<Machine> that = this;
11795 uint32_t flModifications = mData->flModifications;
11796 alock.release();
11797
11798 if (flModifications & IsModified_SharedFolders)
11799 that->i_onSharedFolderChange();
11800
11801 if (flModifications & IsModified_VRDEServer)
11802 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11803 if (flModifications & IsModified_USB)
11804 that->i_onUSBControllerChange();
11805
11806 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11807 if (networkAdapters[slot])
11808 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11809 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11810 if (serialPorts[slot])
11811 that->i_onSerialPortChange(serialPorts[slot]);
11812 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11813 if (parallelPorts[slot])
11814 that->i_onParallelPortChange(parallelPorts[slot]);
11815
11816 if (flModifications & IsModified_Storage)
11817 {
11818 for (StorageControllerList::const_iterator
11819 it = mStorageControllers->begin();
11820 it != mStorageControllers->end();
11821 ++it)
11822 {
11823 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11824 }
11825 }
11826
11827
11828#if 0
11829 if (flModifications & IsModified_BandwidthControl)
11830 that->onBandwidthControlChange();
11831#endif
11832 }
11833}
11834
11835/**
11836 * Commits all the changes to machine settings.
11837 *
11838 * Note that this operation is supposed to never fail.
11839 *
11840 * @note Locks this object and children for writing.
11841 */
11842void Machine::i_commit()
11843{
11844 AutoCaller autoCaller(this);
11845 AssertComRCReturnVoid(autoCaller.rc());
11846
11847 AutoCaller peerCaller(mPeer);
11848 AssertComRCReturnVoid(peerCaller.rc());
11849
11850 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11851
11852 /*
11853 * use safe commit to ensure Snapshot machines (that share mUserData)
11854 * will still refer to a valid memory location
11855 */
11856 mUserData.commitCopy();
11857
11858 mHWData.commit();
11859
11860 if (mMediumAttachments.isBackedUp())
11861 i_commitMedia(Global::IsOnline(mData->mMachineState));
11862
11863 mBIOSSettings->i_commit();
11864 mTrustedPlatformModule->i_commit();
11865 mNvramStore->i_commit();
11866 mRecordingSettings->i_commit();
11867 mGraphicsAdapter->i_commit();
11868 mVRDEServer->i_commit();
11869 mAudioAdapter->i_commit();
11870 mUSBDeviceFilters->i_commit();
11871 mBandwidthControl->i_commit();
11872
11873 /* Since mNetworkAdapters is a list which might have been changed (resized)
11874 * without using the Backupable<> template we need to handle the copying
11875 * of the list entries manually, including the creation of peers for the
11876 * new objects. */
11877 bool commitNetworkAdapters = false;
11878 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11879 if (mPeer)
11880 {
11881 /* commit everything, even the ones which will go away */
11882 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11883 mNetworkAdapters[slot]->i_commit();
11884 /* copy over the new entries, creating a peer and uninit the original */
11885 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11886 for (size_t slot = 0; slot < newSize; slot++)
11887 {
11888 /* look if this adapter has a peer device */
11889 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11890 if (!peer)
11891 {
11892 /* no peer means the adapter is a newly created one;
11893 * create a peer owning data this data share it with */
11894 peer.createObject();
11895 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11896 }
11897 mPeer->mNetworkAdapters[slot] = peer;
11898 }
11899 /* uninit any no longer needed network adapters */
11900 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11901 mNetworkAdapters[slot]->uninit();
11902 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11903 {
11904 if (mPeer->mNetworkAdapters[slot])
11905 mPeer->mNetworkAdapters[slot]->uninit();
11906 }
11907 /* Keep the original network adapter count until this point, so that
11908 * discarding a chipset type change will not lose settings. */
11909 mNetworkAdapters.resize(newSize);
11910 mPeer->mNetworkAdapters.resize(newSize);
11911 }
11912 else
11913 {
11914 /* we have no peer (our parent is the newly created machine);
11915 * just commit changes to the network adapters */
11916 commitNetworkAdapters = true;
11917 }
11918 if (commitNetworkAdapters)
11919 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11920 mNetworkAdapters[slot]->i_commit();
11921
11922 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11923 mSerialPorts[slot]->i_commit();
11924 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11925 mParallelPorts[slot]->i_commit();
11926
11927 bool commitStorageControllers = false;
11928
11929 if (mStorageControllers.isBackedUp())
11930 {
11931 mStorageControllers.commit();
11932
11933 if (mPeer)
11934 {
11935 /* Commit all changes to new controllers (this will reshare data with
11936 * peers for those who have peers) */
11937 StorageControllerList *newList = new StorageControllerList();
11938 for (StorageControllerList::const_iterator
11939 it = mStorageControllers->begin();
11940 it != mStorageControllers->end();
11941 ++it)
11942 {
11943 (*it)->i_commit();
11944
11945 /* look if this controller has a peer device */
11946 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11947 if (!peer)
11948 {
11949 /* no peer means the device is a newly created one;
11950 * create a peer owning data this device share it with */
11951 peer.createObject();
11952 peer->init(mPeer, *it, true /* aReshare */);
11953 }
11954 else
11955 {
11956 /* remove peer from the old list */
11957 mPeer->mStorageControllers->remove(peer);
11958 }
11959 /* and add it to the new list */
11960 newList->push_back(peer);
11961 }
11962
11963 /* uninit old peer's controllers that are left */
11964 for (StorageControllerList::const_iterator
11965 it = mPeer->mStorageControllers->begin();
11966 it != mPeer->mStorageControllers->end();
11967 ++it)
11968 {
11969 (*it)->uninit();
11970 }
11971
11972 /* attach new list of controllers to our peer */
11973 mPeer->mStorageControllers.attach(newList);
11974 }
11975 else
11976 {
11977 /* we have no peer (our parent is the newly created machine);
11978 * just commit changes to devices */
11979 commitStorageControllers = true;
11980 }
11981 }
11982 else
11983 {
11984 /* the list of controllers itself is not changed,
11985 * just commit changes to controllers themselves */
11986 commitStorageControllers = true;
11987 }
11988
11989 if (commitStorageControllers)
11990 {
11991 for (StorageControllerList::const_iterator
11992 it = mStorageControllers->begin();
11993 it != mStorageControllers->end();
11994 ++it)
11995 {
11996 (*it)->i_commit();
11997 }
11998 }
11999
12000 bool commitUSBControllers = false;
12001
12002 if (mUSBControllers.isBackedUp())
12003 {
12004 mUSBControllers.commit();
12005
12006 if (mPeer)
12007 {
12008 /* Commit all changes to new controllers (this will reshare data with
12009 * peers for those who have peers) */
12010 USBControllerList *newList = new USBControllerList();
12011 for (USBControllerList::const_iterator
12012 it = mUSBControllers->begin();
12013 it != mUSBControllers->end();
12014 ++it)
12015 {
12016 (*it)->i_commit();
12017
12018 /* look if this controller has a peer device */
12019 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12020 if (!peer)
12021 {
12022 /* no peer means the device is a newly created one;
12023 * create a peer owning data this device share it with */
12024 peer.createObject();
12025 peer->init(mPeer, *it, true /* aReshare */);
12026 }
12027 else
12028 {
12029 /* remove peer from the old list */
12030 mPeer->mUSBControllers->remove(peer);
12031 }
12032 /* and add it to the new list */
12033 newList->push_back(peer);
12034 }
12035
12036 /* uninit old peer's controllers that are left */
12037 for (USBControllerList::const_iterator
12038 it = mPeer->mUSBControllers->begin();
12039 it != mPeer->mUSBControllers->end();
12040 ++it)
12041 {
12042 (*it)->uninit();
12043 }
12044
12045 /* attach new list of controllers to our peer */
12046 mPeer->mUSBControllers.attach(newList);
12047 }
12048 else
12049 {
12050 /* we have no peer (our parent is the newly created machine);
12051 * just commit changes to devices */
12052 commitUSBControllers = true;
12053 }
12054 }
12055 else
12056 {
12057 /* the list of controllers itself is not changed,
12058 * just commit changes to controllers themselves */
12059 commitUSBControllers = true;
12060 }
12061
12062 if (commitUSBControllers)
12063 {
12064 for (USBControllerList::const_iterator
12065 it = mUSBControllers->begin();
12066 it != mUSBControllers->end();
12067 ++it)
12068 {
12069 (*it)->i_commit();
12070 }
12071 }
12072
12073 if (i_isSessionMachine())
12074 {
12075 /* attach new data to the primary machine and reshare it */
12076 mPeer->mUserData.attach(mUserData);
12077 mPeer->mHWData.attach(mHWData);
12078 /* mmMediumAttachments is reshared by fixupMedia */
12079 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12080 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12081 }
12082}
12083
12084/**
12085 * Copies all the hardware data from the given machine.
12086 *
12087 * Currently, only called when the VM is being restored from a snapshot. In
12088 * particular, this implies that the VM is not running during this method's
12089 * call.
12090 *
12091 * @note This method must be called from under this object's lock.
12092 *
12093 * @note This method doesn't call #i_commit(), so all data remains backed up and
12094 * unsaved.
12095 */
12096void Machine::i_copyFrom(Machine *aThat)
12097{
12098 AssertReturnVoid(!i_isSnapshotMachine());
12099 AssertReturnVoid(aThat->i_isSnapshotMachine());
12100
12101 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12102
12103 mHWData.assignCopy(aThat->mHWData);
12104
12105 // create copies of all shared folders (mHWData after attaching a copy
12106 // contains just references to original objects)
12107 for (HWData::SharedFolderList::iterator
12108 it = mHWData->mSharedFolders.begin();
12109 it != mHWData->mSharedFolders.end();
12110 ++it)
12111 {
12112 ComObjPtr<SharedFolder> folder;
12113 folder.createObject();
12114 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12115 AssertComRC(rc);
12116 *it = folder;
12117 }
12118
12119 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12120 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12121 mNvramStore->i_copyFrom(aThat->mNvramStore);
12122 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12123 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12124 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12125 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12126 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12127 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12128
12129 /* create private copies of all controllers */
12130 mStorageControllers.backup();
12131 mStorageControllers->clear();
12132 for (StorageControllerList::const_iterator
12133 it = aThat->mStorageControllers->begin();
12134 it != aThat->mStorageControllers->end();
12135 ++it)
12136 {
12137 ComObjPtr<StorageController> ctrl;
12138 ctrl.createObject();
12139 ctrl->initCopy(this, *it);
12140 mStorageControllers->push_back(ctrl);
12141 }
12142
12143 /* create private copies of all USB controllers */
12144 mUSBControllers.backup();
12145 mUSBControllers->clear();
12146 for (USBControllerList::const_iterator
12147 it = aThat->mUSBControllers->begin();
12148 it != aThat->mUSBControllers->end();
12149 ++it)
12150 {
12151 ComObjPtr<USBController> ctrl;
12152 ctrl.createObject();
12153 ctrl->initCopy(this, *it);
12154 mUSBControllers->push_back(ctrl);
12155 }
12156
12157 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12158 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12159 {
12160 if (mNetworkAdapters[slot].isNotNull())
12161 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12162 else
12163 {
12164 unconst(mNetworkAdapters[slot]).createObject();
12165 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12166 }
12167 }
12168 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12169 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12170 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12171 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12172}
12173
12174/**
12175 * Returns whether the given storage controller is hotplug capable.
12176 *
12177 * @returns true if the controller supports hotplugging
12178 * false otherwise.
12179 * @param enmCtrlType The controller type to check for.
12180 */
12181bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12182{
12183 ComPtr<ISystemProperties> systemProperties;
12184 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12185 if (FAILED(rc))
12186 return false;
12187
12188 BOOL aHotplugCapable = FALSE;
12189 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12190
12191 return RT_BOOL(aHotplugCapable);
12192}
12193
12194#ifdef VBOX_WITH_RESOURCE_USAGE_API
12195
12196void Machine::i_getDiskList(MediaList &list)
12197{
12198 for (MediumAttachmentList::const_iterator
12199 it = mMediumAttachments->begin();
12200 it != mMediumAttachments->end();
12201 ++it)
12202 {
12203 MediumAttachment *pAttach = *it;
12204 /* just in case */
12205 AssertContinue(pAttach);
12206
12207 AutoCaller localAutoCallerA(pAttach);
12208 if (FAILED(localAutoCallerA.rc())) continue;
12209
12210 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12211
12212 if (pAttach->i_getType() == DeviceType_HardDisk)
12213 list.push_back(pAttach->i_getMedium());
12214 }
12215}
12216
12217void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12218{
12219 AssertReturnVoid(isWriteLockOnCurrentThread());
12220 AssertPtrReturnVoid(aCollector);
12221
12222 pm::CollectorHAL *hal = aCollector->getHAL();
12223 /* Create sub metrics */
12224 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12225 "Percentage of processor time spent in user mode by the VM process.");
12226 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12227 "Percentage of processor time spent in kernel mode by the VM process.");
12228 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12229 "Size of resident portion of VM process in memory.");
12230 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12231 "Actual size of all VM disks combined.");
12232 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12233 "Network receive rate.");
12234 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12235 "Network transmit rate.");
12236 /* Create and register base metrics */
12237 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12238 cpuLoadUser, cpuLoadKernel);
12239 aCollector->registerBaseMetric(cpuLoad);
12240 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12241 ramUsageUsed);
12242 aCollector->registerBaseMetric(ramUsage);
12243 MediaList disks;
12244 i_getDiskList(disks);
12245 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12246 diskUsageUsed);
12247 aCollector->registerBaseMetric(diskUsage);
12248
12249 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12250 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12251 new pm::AggregateAvg()));
12252 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12253 new pm::AggregateMin()));
12254 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12255 new pm::AggregateMax()));
12256 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12257 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12258 new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12260 new pm::AggregateMin()));
12261 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12262 new pm::AggregateMax()));
12263
12264 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12265 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12266 new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12268 new pm::AggregateMin()));
12269 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12270 new pm::AggregateMax()));
12271
12272 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12273 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12274 new pm::AggregateAvg()));
12275 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12276 new pm::AggregateMin()));
12277 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12278 new pm::AggregateMax()));
12279
12280
12281 /* Guest metrics collector */
12282 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12283 aCollector->registerGuest(mCollectorGuest);
12284 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12285
12286 /* Create sub metrics */
12287 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12288 "Percentage of processor time spent in user mode as seen by the guest.");
12289 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12290 "Percentage of processor time spent in kernel mode as seen by the guest.");
12291 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12292 "Percentage of processor time spent idling as seen by the guest.");
12293
12294 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12295 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12296 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12297 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12298 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12299 pm::SubMetric *guestMemCache = new pm::SubMetric(
12300 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12301
12302 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12303 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12304
12305 /* Create and register base metrics */
12306 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12307 machineNetRx, machineNetTx);
12308 aCollector->registerBaseMetric(machineNetRate);
12309
12310 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12311 guestLoadUser, guestLoadKernel, guestLoadIdle);
12312 aCollector->registerBaseMetric(guestCpuLoad);
12313
12314 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12315 guestMemTotal, guestMemFree,
12316 guestMemBalloon, guestMemShared,
12317 guestMemCache, guestPagedTotal);
12318 aCollector->registerBaseMetric(guestCpuMem);
12319
12320 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12321 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12326 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12329
12330 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12331 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12336 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12346 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12349
12350 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12351 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12354
12355 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12356 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12359
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12364
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12369
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12374}
12375
12376void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12377{
12378 AssertReturnVoid(isWriteLockOnCurrentThread());
12379
12380 if (aCollector)
12381 {
12382 aCollector->unregisterMetricsFor(aMachine);
12383 aCollector->unregisterBaseMetricsFor(aMachine);
12384 }
12385}
12386
12387#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12388
12389
12390////////////////////////////////////////////////////////////////////////////////
12391
12392DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12393
12394HRESULT SessionMachine::FinalConstruct()
12395{
12396 LogFlowThisFunc(("\n"));
12397
12398 mClientToken = NULL;
12399
12400 return BaseFinalConstruct();
12401}
12402
12403void SessionMachine::FinalRelease()
12404{
12405 LogFlowThisFunc(("\n"));
12406
12407 Assert(!mClientToken);
12408 /* paranoia, should not hang around any more */
12409 if (mClientToken)
12410 {
12411 delete mClientToken;
12412 mClientToken = NULL;
12413 }
12414
12415 uninit(Uninit::Unexpected);
12416
12417 BaseFinalRelease();
12418}
12419
12420/**
12421 * @note Must be called only by Machine::LockMachine() from its own write lock.
12422 */
12423HRESULT SessionMachine::init(Machine *aMachine)
12424{
12425 LogFlowThisFuncEnter();
12426 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12427
12428 AssertReturn(aMachine, E_INVALIDARG);
12429
12430 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12431
12432 /* Enclose the state transition NotReady->InInit->Ready */
12433 AutoInitSpan autoInitSpan(this);
12434 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12435
12436 HRESULT rc = S_OK;
12437
12438 RT_ZERO(mAuthLibCtx);
12439
12440 /* create the machine client token */
12441 try
12442 {
12443 mClientToken = new ClientToken(aMachine, this);
12444 if (!mClientToken->isReady())
12445 {
12446 delete mClientToken;
12447 mClientToken = NULL;
12448 rc = E_FAIL;
12449 }
12450 }
12451 catch (std::bad_alloc &)
12452 {
12453 rc = E_OUTOFMEMORY;
12454 }
12455 if (FAILED(rc))
12456 return rc;
12457
12458 /* memorize the peer Machine */
12459 unconst(mPeer) = aMachine;
12460 /* share the parent pointer */
12461 unconst(mParent) = aMachine->mParent;
12462
12463 /* take the pointers to data to share */
12464 mData.share(aMachine->mData);
12465 mSSData.share(aMachine->mSSData);
12466
12467 mUserData.share(aMachine->mUserData);
12468 mHWData.share(aMachine->mHWData);
12469 mMediumAttachments.share(aMachine->mMediumAttachments);
12470
12471 mStorageControllers.allocate();
12472 for (StorageControllerList::const_iterator
12473 it = aMachine->mStorageControllers->begin();
12474 it != aMachine->mStorageControllers->end();
12475 ++it)
12476 {
12477 ComObjPtr<StorageController> ctl;
12478 ctl.createObject();
12479 ctl->init(this, *it);
12480 mStorageControllers->push_back(ctl);
12481 }
12482
12483 mUSBControllers.allocate();
12484 for (USBControllerList::const_iterator
12485 it = aMachine->mUSBControllers->begin();
12486 it != aMachine->mUSBControllers->end();
12487 ++it)
12488 {
12489 ComObjPtr<USBController> ctl;
12490 ctl.createObject();
12491 ctl->init(this, *it);
12492 mUSBControllers->push_back(ctl);
12493 }
12494
12495 unconst(mBIOSSettings).createObject();
12496 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12497
12498 unconst(mTrustedPlatformModule).createObject();
12499 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12500
12501 unconst(mNvramStore).createObject();
12502 mNvramStore->init(this, aMachine->mNvramStore);
12503
12504 unconst(mRecordingSettings).createObject();
12505 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12506 /* create another GraphicsAdapter object that will be mutable */
12507 unconst(mGraphicsAdapter).createObject();
12508 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12509 /* create another VRDEServer object that will be mutable */
12510 unconst(mVRDEServer).createObject();
12511 mVRDEServer->init(this, aMachine->mVRDEServer);
12512 /* create another audio adapter object that will be mutable */
12513 unconst(mAudioAdapter).createObject();
12514 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12515 /* create a list of serial ports that will be mutable */
12516 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12517 {
12518 unconst(mSerialPorts[slot]).createObject();
12519 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12520 }
12521 /* create a list of parallel ports that will be mutable */
12522 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12523 {
12524 unconst(mParallelPorts[slot]).createObject();
12525 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12526 }
12527
12528 /* create another USB device filters object that will be mutable */
12529 unconst(mUSBDeviceFilters).createObject();
12530 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12531
12532 /* create a list of network adapters that will be mutable */
12533 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12534 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12535 {
12536 unconst(mNetworkAdapters[slot]).createObject();
12537 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12538 }
12539
12540 /* create another bandwidth control object that will be mutable */
12541 unconst(mBandwidthControl).createObject();
12542 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12543
12544 /* default is to delete saved state on Saved -> PoweredOff transition */
12545 mRemoveSavedState = true;
12546
12547 /* Confirm a successful initialization when it's the case */
12548 autoInitSpan.setSucceeded();
12549
12550 miNATNetworksStarted = 0;
12551
12552 LogFlowThisFuncLeave();
12553 return rc;
12554}
12555
12556/**
12557 * Uninitializes this session object. If the reason is other than
12558 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12559 * or the client watcher code.
12560 *
12561 * @param aReason uninitialization reason
12562 *
12563 * @note Locks mParent + this object for writing.
12564 */
12565void SessionMachine::uninit(Uninit::Reason aReason)
12566{
12567 LogFlowThisFuncEnter();
12568 LogFlowThisFunc(("reason=%d\n", aReason));
12569
12570 /*
12571 * Strongly reference ourselves to prevent this object deletion after
12572 * mData->mSession.mMachine.setNull() below (which can release the last
12573 * reference and call the destructor). Important: this must be done before
12574 * accessing any members (and before AutoUninitSpan that does it as well).
12575 * This self reference will be released as the very last step on return.
12576 */
12577 ComObjPtr<SessionMachine> selfRef;
12578 if (aReason != Uninit::Unexpected)
12579 selfRef = this;
12580
12581 /* Enclose the state transition Ready->InUninit->NotReady */
12582 AutoUninitSpan autoUninitSpan(this);
12583 if (autoUninitSpan.uninitDone())
12584 {
12585 LogFlowThisFunc(("Already uninitialized\n"));
12586 LogFlowThisFuncLeave();
12587 return;
12588 }
12589
12590 if (autoUninitSpan.initFailed())
12591 {
12592 /* We've been called by init() because it's failed. It's not really
12593 * necessary (nor it's safe) to perform the regular uninit sequence
12594 * below, the following is enough.
12595 */
12596 LogFlowThisFunc(("Initialization failed.\n"));
12597 /* destroy the machine client token */
12598 if (mClientToken)
12599 {
12600 delete mClientToken;
12601 mClientToken = NULL;
12602 }
12603 uninitDataAndChildObjects();
12604 mData.free();
12605 unconst(mParent) = NULL;
12606 unconst(mPeer) = NULL;
12607 LogFlowThisFuncLeave();
12608 return;
12609 }
12610
12611 MachineState_T lastState;
12612 {
12613 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12614 lastState = mData->mMachineState;
12615 }
12616 NOREF(lastState);
12617
12618#ifdef VBOX_WITH_USB
12619 // release all captured USB devices, but do this before requesting the locks below
12620 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12621 {
12622 /* Console::captureUSBDevices() is called in the VM process only after
12623 * setting the machine state to Starting or Restoring.
12624 * Console::detachAllUSBDevices() will be called upon successful
12625 * termination. So, we need to release USB devices only if there was
12626 * an abnormal termination of a running VM.
12627 *
12628 * This is identical to SessionMachine::DetachAllUSBDevices except
12629 * for the aAbnormal argument. */
12630 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12631 AssertComRC(rc);
12632 NOREF(rc);
12633
12634 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12635 if (service)
12636 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12637 }
12638#endif /* VBOX_WITH_USB */
12639
12640 // we need to lock this object in uninit() because the lock is shared
12641 // with mPeer (as well as data we modify below). mParent lock is needed
12642 // by several calls to it.
12643 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12644
12645#ifdef VBOX_WITH_RESOURCE_USAGE_API
12646 /*
12647 * It is safe to call Machine::i_unregisterMetrics() here because
12648 * PerformanceCollector::samplerCallback no longer accesses guest methods
12649 * holding the lock.
12650 */
12651 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12652 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12653 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12654 if (mCollectorGuest)
12655 {
12656 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12657 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12658 mCollectorGuest = NULL;
12659 }
12660#endif
12661
12662 if (aReason == Uninit::Abnormal)
12663 {
12664 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12665
12666 /* reset the state to Aborted */
12667 if (mData->mMachineState != MachineState_Aborted)
12668 i_setMachineState(MachineState_Aborted);
12669 }
12670
12671 // any machine settings modified?
12672 if (mData->flModifications)
12673 {
12674 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12675 i_rollback(false /* aNotify */);
12676 }
12677
12678 mData->mSession.mPID = NIL_RTPROCESS;
12679
12680 if (aReason == Uninit::Unexpected)
12681 {
12682 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12683 * client watcher thread to update the set of machines that have open
12684 * sessions. */
12685 mParent->i_updateClientWatcher();
12686 }
12687
12688 /* uninitialize all remote controls */
12689 if (mData->mSession.mRemoteControls.size())
12690 {
12691 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12692 mData->mSession.mRemoteControls.size()));
12693
12694 /* Always restart a the beginning, since the iterator is invalidated
12695 * by using erase(). */
12696 for (Data::Session::RemoteControlList::iterator
12697 it = mData->mSession.mRemoteControls.begin();
12698 it != mData->mSession.mRemoteControls.end();
12699 it = mData->mSession.mRemoteControls.begin())
12700 {
12701 ComPtr<IInternalSessionControl> pControl = *it;
12702 mData->mSession.mRemoteControls.erase(it);
12703 multilock.release();
12704 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12705 HRESULT rc = pControl->Uninitialize();
12706 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12707 if (FAILED(rc))
12708 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12709 multilock.acquire();
12710 }
12711 mData->mSession.mRemoteControls.clear();
12712 }
12713
12714 /* Remove all references to the NAT network service. The service will stop
12715 * if all references (also from other VMs) are removed. */
12716 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12717 {
12718 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12719 {
12720 BOOL enabled;
12721 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12722 if ( FAILED(hrc)
12723 || !enabled)
12724 continue;
12725
12726 NetworkAttachmentType_T type;
12727 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12728 if ( SUCCEEDED(hrc)
12729 && type == NetworkAttachmentType_NATNetwork)
12730 {
12731 Bstr name;
12732 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12733 if (SUCCEEDED(hrc))
12734 {
12735 multilock.release();
12736 Utf8Str strName(name);
12737 LogRel(("VM '%s' stops using NAT network '%s'\n",
12738 mUserData->s.strName.c_str(), strName.c_str()));
12739 mParent->i_natNetworkRefDec(strName);
12740 multilock.acquire();
12741 }
12742 }
12743 }
12744 }
12745
12746 /*
12747 * An expected uninitialization can come only from #i_checkForDeath().
12748 * Otherwise it means that something's gone really wrong (for example,
12749 * the Session implementation has released the VirtualBox reference
12750 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12751 * etc). However, it's also possible, that the client releases the IPC
12752 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12753 * but the VirtualBox release event comes first to the server process.
12754 * This case is practically possible, so we should not assert on an
12755 * unexpected uninit, just log a warning.
12756 */
12757
12758 if (aReason == Uninit::Unexpected)
12759 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12760
12761 if (aReason != Uninit::Normal)
12762 {
12763 mData->mSession.mDirectControl.setNull();
12764 }
12765 else
12766 {
12767 /* this must be null here (see #OnSessionEnd()) */
12768 Assert(mData->mSession.mDirectControl.isNull());
12769 Assert(mData->mSession.mState == SessionState_Unlocking);
12770 Assert(!mData->mSession.mProgress.isNull());
12771 }
12772 if (mData->mSession.mProgress)
12773 {
12774 if (aReason == Uninit::Normal)
12775 mData->mSession.mProgress->i_notifyComplete(S_OK);
12776 else
12777 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12778 COM_IIDOF(ISession),
12779 getComponentName(),
12780 tr("The VM session was aborted"));
12781 mData->mSession.mProgress.setNull();
12782 }
12783
12784 if (mConsoleTaskData.mProgress)
12785 {
12786 Assert(aReason == Uninit::Abnormal);
12787 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12788 COM_IIDOF(ISession),
12789 getComponentName(),
12790 tr("The VM session was aborted"));
12791 mConsoleTaskData.mProgress.setNull();
12792 }
12793
12794 /* remove the association between the peer machine and this session machine */
12795 Assert( (SessionMachine*)mData->mSession.mMachine == this
12796 || aReason == Uninit::Unexpected);
12797
12798 /* reset the rest of session data */
12799 mData->mSession.mLockType = LockType_Null;
12800 mData->mSession.mMachine.setNull();
12801 mData->mSession.mState = SessionState_Unlocked;
12802 mData->mSession.mName.setNull();
12803
12804 /* destroy the machine client token before leaving the exclusive lock */
12805 if (mClientToken)
12806 {
12807 delete mClientToken;
12808 mClientToken = NULL;
12809 }
12810
12811 /* fire an event */
12812 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12813
12814 uninitDataAndChildObjects();
12815
12816 /* free the essential data structure last */
12817 mData.free();
12818
12819 /* release the exclusive lock before setting the below two to NULL */
12820 multilock.release();
12821
12822 unconst(mParent) = NULL;
12823 unconst(mPeer) = NULL;
12824
12825 AuthLibUnload(&mAuthLibCtx);
12826
12827 LogFlowThisFuncLeave();
12828}
12829
12830// util::Lockable interface
12831////////////////////////////////////////////////////////////////////////////////
12832
12833/**
12834 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12835 * with the primary Machine instance (mPeer).
12836 */
12837RWLockHandle *SessionMachine::lockHandle() const
12838{
12839 AssertReturn(mPeer != NULL, NULL);
12840 return mPeer->lockHandle();
12841}
12842
12843// IInternalMachineControl methods
12844////////////////////////////////////////////////////////////////////////////////
12845
12846/**
12847 * Passes collected guest statistics to performance collector object
12848 */
12849HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12850 ULONG aCpuKernel, ULONG aCpuIdle,
12851 ULONG aMemTotal, ULONG aMemFree,
12852 ULONG aMemBalloon, ULONG aMemShared,
12853 ULONG aMemCache, ULONG aPageTotal,
12854 ULONG aAllocVMM, ULONG aFreeVMM,
12855 ULONG aBalloonedVMM, ULONG aSharedVMM,
12856 ULONG aVmNetRx, ULONG aVmNetTx)
12857{
12858#ifdef VBOX_WITH_RESOURCE_USAGE_API
12859 if (mCollectorGuest)
12860 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12861 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12862 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12863 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12864
12865 return S_OK;
12866#else
12867 NOREF(aValidStats);
12868 NOREF(aCpuUser);
12869 NOREF(aCpuKernel);
12870 NOREF(aCpuIdle);
12871 NOREF(aMemTotal);
12872 NOREF(aMemFree);
12873 NOREF(aMemBalloon);
12874 NOREF(aMemShared);
12875 NOREF(aMemCache);
12876 NOREF(aPageTotal);
12877 NOREF(aAllocVMM);
12878 NOREF(aFreeVMM);
12879 NOREF(aBalloonedVMM);
12880 NOREF(aSharedVMM);
12881 NOREF(aVmNetRx);
12882 NOREF(aVmNetTx);
12883 return E_NOTIMPL;
12884#endif
12885}
12886
12887////////////////////////////////////////////////////////////////////////////////
12888//
12889// SessionMachine task records
12890//
12891////////////////////////////////////////////////////////////////////////////////
12892
12893/**
12894 * Task record for saving the machine state.
12895 */
12896class SessionMachine::SaveStateTask
12897 : public Machine::Task
12898{
12899public:
12900 SaveStateTask(SessionMachine *m,
12901 Progress *p,
12902 const Utf8Str &t,
12903 Reason_T enmReason,
12904 const Utf8Str &strStateFilePath)
12905 : Task(m, p, t),
12906 m_enmReason(enmReason),
12907 m_strStateFilePath(strStateFilePath)
12908 {}
12909
12910private:
12911 void handler()
12912 {
12913 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12914 }
12915
12916 Reason_T m_enmReason;
12917 Utf8Str m_strStateFilePath;
12918
12919 friend class SessionMachine;
12920};
12921
12922/**
12923 * Task thread implementation for SessionMachine::SaveState(), called from
12924 * SessionMachine::taskHandler().
12925 *
12926 * @note Locks this object for writing.
12927 *
12928 * @param task
12929 * @return
12930 */
12931void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12932{
12933 LogFlowThisFuncEnter();
12934
12935 AutoCaller autoCaller(this);
12936 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12937 if (FAILED(autoCaller.rc()))
12938 {
12939 /* we might have been uninitialized because the session was accidentally
12940 * closed by the client, so don't assert */
12941 HRESULT rc = setError(E_FAIL,
12942 tr("The session has been accidentally closed"));
12943 task.m_pProgress->i_notifyComplete(rc);
12944 LogFlowThisFuncLeave();
12945 return;
12946 }
12947
12948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12949
12950 HRESULT rc = S_OK;
12951
12952 try
12953 {
12954 ComPtr<IInternalSessionControl> directControl;
12955 if (mData->mSession.mLockType == LockType_VM)
12956 directControl = mData->mSession.mDirectControl;
12957 if (directControl.isNull())
12958 throw setError(VBOX_E_INVALID_VM_STATE,
12959 tr("Trying to save state without a running VM"));
12960 alock.release();
12961 BOOL fSuspendedBySave;
12962 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12963 Assert(!fSuspendedBySave);
12964 alock.acquire();
12965
12966 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12967 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12968 throw E_FAIL);
12969
12970 if (SUCCEEDED(rc))
12971 {
12972 mSSData->strStateFilePath = task.m_strStateFilePath;
12973
12974 /* save all VM settings */
12975 rc = i_saveSettings(NULL);
12976 // no need to check whether VirtualBox.xml needs saving also since
12977 // we can't have a name change pending at this point
12978 }
12979 else
12980 {
12981 // On failure, set the state to the state we had at the beginning.
12982 i_setMachineState(task.m_machineStateBackup);
12983 i_updateMachineStateOnClient();
12984
12985 // Delete the saved state file (might have been already created).
12986 // No need to check whether this is shared with a snapshot here
12987 // because we certainly created a fresh saved state file here.
12988 RTFileDelete(task.m_strStateFilePath.c_str());
12989 }
12990 }
12991 catch (HRESULT aRC) { rc = aRC; }
12992
12993 task.m_pProgress->i_notifyComplete(rc);
12994
12995 LogFlowThisFuncLeave();
12996}
12997
12998/**
12999 * @note Locks this object for writing.
13000 */
13001HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13002{
13003 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13004}
13005
13006HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13007{
13008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13009
13010 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13011 if (FAILED(rc)) return rc;
13012
13013 if ( mData->mMachineState != MachineState_Running
13014 && mData->mMachineState != MachineState_Paused
13015 )
13016 return setError(VBOX_E_INVALID_VM_STATE,
13017 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13018 Global::stringifyMachineState(mData->mMachineState));
13019
13020 ComObjPtr<Progress> pProgress;
13021 pProgress.createObject();
13022 rc = pProgress->init(i_getVirtualBox(),
13023 static_cast<IMachine *>(this) /* aInitiator */,
13024 tr("Saving the execution state of the virtual machine"),
13025 FALSE /* aCancelable */);
13026 if (FAILED(rc))
13027 return rc;
13028
13029 Utf8Str strStateFilePath;
13030 i_composeSavedStateFilename(strStateFilePath);
13031
13032 /* create and start the task on a separate thread (note that it will not
13033 * start working until we release alock) */
13034 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13035 rc = pTask->createThread();
13036 if (FAILED(rc))
13037 return rc;
13038
13039 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13040 i_setMachineState(MachineState_Saving);
13041 i_updateMachineStateOnClient();
13042
13043 pProgress.queryInterfaceTo(aProgress.asOutParam());
13044
13045 return S_OK;
13046}
13047
13048/**
13049 * @note Locks this object for writing.
13050 */
13051HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13052{
13053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13054
13055 HRESULT rc = i_checkStateDependency(MutableStateDep);
13056 if (FAILED(rc)) return rc;
13057
13058 if ( mData->mMachineState != MachineState_PoweredOff
13059 && mData->mMachineState != MachineState_Teleported
13060 && mData->mMachineState != MachineState_Aborted
13061 )
13062 return setError(VBOX_E_INVALID_VM_STATE,
13063 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13064 Global::stringifyMachineState(mData->mMachineState));
13065
13066 com::Utf8Str stateFilePathFull;
13067 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13068 if (RT_FAILURE(vrc))
13069 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13070 tr("Invalid saved state file path '%s' (%Rrc)"),
13071 aSavedStateFile.c_str(),
13072 vrc);
13073
13074 mSSData->strStateFilePath = stateFilePathFull;
13075
13076 /* The below i_setMachineState() will detect the state transition and will
13077 * update the settings file */
13078
13079 return i_setMachineState(MachineState_Saved);
13080}
13081
13082/**
13083 * @note Locks this object for writing.
13084 */
13085HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13086{
13087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13088
13089 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13090 if (FAILED(rc)) return rc;
13091
13092 if (mData->mMachineState != MachineState_Saved)
13093 return setError(VBOX_E_INVALID_VM_STATE,
13094 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13095 Global::stringifyMachineState(mData->mMachineState));
13096
13097 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13098
13099 /*
13100 * Saved -> PoweredOff transition will be detected in the SessionMachine
13101 * and properly handled.
13102 */
13103 rc = i_setMachineState(MachineState_PoweredOff);
13104 return rc;
13105}
13106
13107
13108/**
13109 * @note Locks the same as #i_setMachineState() does.
13110 */
13111HRESULT SessionMachine::updateState(MachineState_T aState)
13112{
13113 return i_setMachineState(aState);
13114}
13115
13116/**
13117 * @note Locks this object for writing.
13118 */
13119HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13120{
13121 IProgress *pProgress(aProgress);
13122
13123 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13124
13125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13126
13127 if (mData->mSession.mState != SessionState_Locked)
13128 return VBOX_E_INVALID_OBJECT_STATE;
13129
13130 if (!mData->mSession.mProgress.isNull())
13131 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13132
13133 /* If we didn't reference the NAT network service yet, add a reference to
13134 * force a start */
13135 if (miNATNetworksStarted < 1)
13136 {
13137 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13138 {
13139 BOOL enabled;
13140 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13141 if ( FAILED(hrc)
13142 || !enabled)
13143 continue;
13144
13145 NetworkAttachmentType_T type;
13146 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13147 if ( SUCCEEDED(hrc)
13148 && type == NetworkAttachmentType_NATNetwork)
13149 {
13150 Bstr name;
13151 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13152 if (SUCCEEDED(hrc))
13153 {
13154 Utf8Str strName(name);
13155 LogRel(("VM '%s' starts using NAT network '%s'\n",
13156 mUserData->s.strName.c_str(), strName.c_str()));
13157 mPeer->lockHandle()->unlockWrite();
13158 mParent->i_natNetworkRefInc(strName);
13159#ifdef RT_LOCK_STRICT
13160 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13161#else
13162 mPeer->lockHandle()->lockWrite();
13163#endif
13164 }
13165 }
13166 }
13167 miNATNetworksStarted++;
13168 }
13169
13170 LogFlowThisFunc(("returns S_OK.\n"));
13171 return S_OK;
13172}
13173
13174/**
13175 * @note Locks this object for writing.
13176 */
13177HRESULT SessionMachine::endPowerUp(LONG aResult)
13178{
13179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13180
13181 if (mData->mSession.mState != SessionState_Locked)
13182 return VBOX_E_INVALID_OBJECT_STATE;
13183
13184 /* Finalize the LaunchVMProcess progress object. */
13185 if (mData->mSession.mProgress)
13186 {
13187 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13188 mData->mSession.mProgress.setNull();
13189 }
13190
13191 if (SUCCEEDED((HRESULT)aResult))
13192 {
13193#ifdef VBOX_WITH_RESOURCE_USAGE_API
13194 /* The VM has been powered up successfully, so it makes sense
13195 * now to offer the performance metrics for a running machine
13196 * object. Doing it earlier wouldn't be safe. */
13197 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13198 mData->mSession.mPID);
13199#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13200 }
13201
13202 return S_OK;
13203}
13204
13205/**
13206 * @note Locks this object for writing.
13207 */
13208HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13209{
13210 LogFlowThisFuncEnter();
13211
13212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13213
13214 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13215 E_FAIL);
13216
13217 /* create a progress object to track operation completion */
13218 ComObjPtr<Progress> pProgress;
13219 pProgress.createObject();
13220 pProgress->init(i_getVirtualBox(),
13221 static_cast<IMachine *>(this) /* aInitiator */,
13222 tr("Stopping the virtual machine"),
13223 FALSE /* aCancelable */);
13224
13225 /* fill in the console task data */
13226 mConsoleTaskData.mLastState = mData->mMachineState;
13227 mConsoleTaskData.mProgress = pProgress;
13228
13229 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13230 i_setMachineState(MachineState_Stopping);
13231
13232 pProgress.queryInterfaceTo(aProgress.asOutParam());
13233
13234 return S_OK;
13235}
13236
13237/**
13238 * @note Locks this object for writing.
13239 */
13240HRESULT SessionMachine::endPoweringDown(LONG aResult,
13241 const com::Utf8Str &aErrMsg)
13242{
13243 HRESULT const hrcResult = (HRESULT)aResult;
13244 LogFlowThisFuncEnter();
13245
13246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13247
13248 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13249 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13250 && mConsoleTaskData.mLastState != MachineState_Null,
13251 E_FAIL);
13252
13253 /*
13254 * On failure, set the state to the state we had when BeginPoweringDown()
13255 * was called (this is expected by Console::PowerDown() and the associated
13256 * task). On success the VM process already changed the state to
13257 * MachineState_PoweredOff, so no need to do anything.
13258 */
13259 if (FAILED(hrcResult))
13260 i_setMachineState(mConsoleTaskData.mLastState);
13261
13262 /* notify the progress object about operation completion */
13263 Assert(mConsoleTaskData.mProgress);
13264 if (SUCCEEDED(hrcResult))
13265 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13266 else
13267 {
13268 if (aErrMsg.length())
13269 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13270 COM_IIDOF(ISession),
13271 getComponentName(),
13272 aErrMsg.c_str());
13273 else
13274 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13275 }
13276
13277 /* clear out the temporary saved state data */
13278 mConsoleTaskData.mLastState = MachineState_Null;
13279 mConsoleTaskData.mProgress.setNull();
13280
13281 LogFlowThisFuncLeave();
13282 return S_OK;
13283}
13284
13285
13286/**
13287 * Goes through the USB filters of the given machine to see if the given
13288 * device matches any filter or not.
13289 *
13290 * @note Locks the same as USBController::hasMatchingFilter() does.
13291 */
13292HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13293 BOOL *aMatched,
13294 ULONG *aMaskedInterfaces)
13295{
13296 LogFlowThisFunc(("\n"));
13297
13298#ifdef VBOX_WITH_USB
13299 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13300#else
13301 NOREF(aDevice);
13302 NOREF(aMaskedInterfaces);
13303 *aMatched = FALSE;
13304#endif
13305
13306 return S_OK;
13307}
13308
13309/**
13310 * @note Locks the same as Host::captureUSBDevice() does.
13311 */
13312HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13313{
13314 LogFlowThisFunc(("\n"));
13315
13316#ifdef VBOX_WITH_USB
13317 /* if captureDeviceForVM() fails, it must have set extended error info */
13318 clearError();
13319 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13320 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13321 return rc;
13322
13323 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13324 AssertReturn(service, E_FAIL);
13325 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13326#else
13327 RT_NOREF(aId, aCaptureFilename);
13328 return E_NOTIMPL;
13329#endif
13330}
13331
13332/**
13333 * @note Locks the same as Host::detachUSBDevice() does.
13334 */
13335HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13336 BOOL aDone)
13337{
13338 LogFlowThisFunc(("\n"));
13339
13340#ifdef VBOX_WITH_USB
13341 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13342 AssertReturn(service, E_FAIL);
13343 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13344#else
13345 NOREF(aId);
13346 NOREF(aDone);
13347 return E_NOTIMPL;
13348#endif
13349}
13350
13351/**
13352 * Inserts all machine filters to the USB proxy service and then calls
13353 * Host::autoCaptureUSBDevices().
13354 *
13355 * Called by Console from the VM process upon VM startup.
13356 *
13357 * @note Locks what called methods lock.
13358 */
13359HRESULT SessionMachine::autoCaptureUSBDevices()
13360{
13361 LogFlowThisFunc(("\n"));
13362
13363#ifdef VBOX_WITH_USB
13364 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13365 AssertComRC(rc);
13366 NOREF(rc);
13367
13368 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13369 AssertReturn(service, E_FAIL);
13370 return service->autoCaptureDevicesForVM(this);
13371#else
13372 return S_OK;
13373#endif
13374}
13375
13376/**
13377 * Removes all machine filters from the USB proxy service and then calls
13378 * Host::detachAllUSBDevices().
13379 *
13380 * Called by Console from the VM process upon normal VM termination or by
13381 * SessionMachine::uninit() upon abnormal VM termination (from under the
13382 * Machine/SessionMachine lock).
13383 *
13384 * @note Locks what called methods lock.
13385 */
13386HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13387{
13388 LogFlowThisFunc(("\n"));
13389
13390#ifdef VBOX_WITH_USB
13391 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13392 AssertComRC(rc);
13393 NOREF(rc);
13394
13395 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13396 AssertReturn(service, E_FAIL);
13397 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13398#else
13399 NOREF(aDone);
13400 return S_OK;
13401#endif
13402}
13403
13404/**
13405 * @note Locks this object for writing.
13406 */
13407HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13408 ComPtr<IProgress> &aProgress)
13409{
13410 LogFlowThisFuncEnter();
13411
13412 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13413 /*
13414 * We don't assert below because it might happen that a non-direct session
13415 * informs us it is closed right after we've been uninitialized -- it's ok.
13416 */
13417
13418 /* get IInternalSessionControl interface */
13419 ComPtr<IInternalSessionControl> control(aSession);
13420
13421 ComAssertRet(!control.isNull(), E_INVALIDARG);
13422
13423 /* Creating a Progress object requires the VirtualBox lock, and
13424 * thus locking it here is required by the lock order rules. */
13425 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13426
13427 if (control == mData->mSession.mDirectControl)
13428 {
13429 /* The direct session is being normally closed by the client process
13430 * ----------------------------------------------------------------- */
13431
13432 /* go to the closing state (essential for all open*Session() calls and
13433 * for #i_checkForDeath()) */
13434 Assert(mData->mSession.mState == SessionState_Locked);
13435 mData->mSession.mState = SessionState_Unlocking;
13436
13437 /* set direct control to NULL to release the remote instance */
13438 mData->mSession.mDirectControl.setNull();
13439 LogFlowThisFunc(("Direct control is set to NULL\n"));
13440
13441 if (mData->mSession.mProgress)
13442 {
13443 /* finalize the progress, someone might wait if a frontend
13444 * closes the session before powering on the VM. */
13445 mData->mSession.mProgress->notifyComplete(E_FAIL,
13446 COM_IIDOF(ISession),
13447 getComponentName(),
13448 tr("The VM session was closed before any attempt to power it on"));
13449 mData->mSession.mProgress.setNull();
13450 }
13451
13452 /* Create the progress object the client will use to wait until
13453 * #i_checkForDeath() is called to uninitialize this session object after
13454 * it releases the IPC semaphore.
13455 * Note! Because we're "reusing" mProgress here, this must be a proxy
13456 * object just like for LaunchVMProcess. */
13457 Assert(mData->mSession.mProgress.isNull());
13458 ComObjPtr<ProgressProxy> progress;
13459 progress.createObject();
13460 ComPtr<IUnknown> pPeer(mPeer);
13461 progress->init(mParent, pPeer,
13462 Bstr(tr("Closing session")).raw(),
13463 FALSE /* aCancelable */);
13464 progress.queryInterfaceTo(aProgress.asOutParam());
13465 mData->mSession.mProgress = progress;
13466 }
13467 else
13468 {
13469 /* the remote session is being normally closed */
13470 bool found = false;
13471 for (Data::Session::RemoteControlList::iterator
13472 it = mData->mSession.mRemoteControls.begin();
13473 it != mData->mSession.mRemoteControls.end();
13474 ++it)
13475 {
13476 if (control == *it)
13477 {
13478 found = true;
13479 // This MUST be erase(it), not remove(*it) as the latter
13480 // triggers a very nasty use after free due to the place where
13481 // the value "lives".
13482 mData->mSession.mRemoteControls.erase(it);
13483 break;
13484 }
13485 }
13486 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13487 E_INVALIDARG);
13488 }
13489
13490 /* signal the client watcher thread, because the client is going away */
13491 mParent->i_updateClientWatcher();
13492
13493 LogFlowThisFuncLeave();
13494 return S_OK;
13495}
13496
13497HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13498 std::vector<com::Utf8Str> &aValues,
13499 std::vector<LONG64> &aTimestamps,
13500 std::vector<com::Utf8Str> &aFlags)
13501{
13502 LogFlowThisFunc(("\n"));
13503
13504#ifdef VBOX_WITH_GUEST_PROPS
13505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13506
13507 size_t cEntries = mHWData->mGuestProperties.size();
13508 aNames.resize(cEntries);
13509 aValues.resize(cEntries);
13510 aTimestamps.resize(cEntries);
13511 aFlags.resize(cEntries);
13512
13513 size_t i = 0;
13514 for (HWData::GuestPropertyMap::const_iterator
13515 it = mHWData->mGuestProperties.begin();
13516 it != mHWData->mGuestProperties.end();
13517 ++it, ++i)
13518 {
13519 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13520 aNames[i] = it->first;
13521 aValues[i] = it->second.strValue;
13522 aTimestamps[i] = it->second.mTimestamp;
13523
13524 /* If it is NULL, keep it NULL. */
13525 if (it->second.mFlags)
13526 {
13527 GuestPropWriteFlags(it->second.mFlags, szFlags);
13528 aFlags[i] = szFlags;
13529 }
13530 else
13531 aFlags[i] = "";
13532 }
13533 return S_OK;
13534#else
13535 ReturnComNotImplemented();
13536#endif
13537}
13538
13539HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13540 const com::Utf8Str &aValue,
13541 LONG64 aTimestamp,
13542 const com::Utf8Str &aFlags)
13543{
13544 LogFlowThisFunc(("\n"));
13545
13546#ifdef VBOX_WITH_GUEST_PROPS
13547 try
13548 {
13549 /*
13550 * Convert input up front.
13551 */
13552 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13553 if (aFlags.length())
13554 {
13555 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13556 AssertRCReturn(vrc, E_INVALIDARG);
13557 }
13558
13559 /*
13560 * Now grab the object lock, validate the state and do the update.
13561 */
13562
13563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13564
13565 if (!Global::IsOnline(mData->mMachineState))
13566 {
13567 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13568 VBOX_E_INVALID_VM_STATE);
13569 }
13570
13571 i_setModified(IsModified_MachineData);
13572 mHWData.backup();
13573
13574 bool fDelete = !aValue.length();
13575 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13576 if (it != mHWData->mGuestProperties.end())
13577 {
13578 if (!fDelete)
13579 {
13580 it->second.strValue = aValue;
13581 it->second.mTimestamp = aTimestamp;
13582 it->second.mFlags = fFlags;
13583 }
13584 else
13585 mHWData->mGuestProperties.erase(it);
13586
13587 mData->mGuestPropertiesModified = TRUE;
13588 }
13589 else if (!fDelete)
13590 {
13591 HWData::GuestProperty prop;
13592 prop.strValue = aValue;
13593 prop.mTimestamp = aTimestamp;
13594 prop.mFlags = fFlags;
13595
13596 mHWData->mGuestProperties[aName] = prop;
13597 mData->mGuestPropertiesModified = TRUE;
13598 }
13599
13600 alock.release();
13601
13602 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13603 }
13604 catch (...)
13605 {
13606 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13607 }
13608 return S_OK;
13609#else
13610 ReturnComNotImplemented();
13611#endif
13612}
13613
13614
13615HRESULT SessionMachine::lockMedia()
13616{
13617 AutoMultiWriteLock2 alock(this->lockHandle(),
13618 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13619
13620 AssertReturn( mData->mMachineState == MachineState_Starting
13621 || mData->mMachineState == MachineState_Restoring
13622 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13623
13624 clearError();
13625 alock.release();
13626 return i_lockMedia();
13627}
13628
13629HRESULT SessionMachine::unlockMedia()
13630{
13631 HRESULT hrc = i_unlockMedia();
13632 return hrc;
13633}
13634
13635HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13636 ComPtr<IMediumAttachment> &aNewAttachment)
13637{
13638 // request the host lock first, since might be calling Host methods for getting host drives;
13639 // next, protect the media tree all the while we're in here, as well as our member variables
13640 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13641 this->lockHandle(),
13642 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13643
13644 IMediumAttachment *iAttach = aAttachment;
13645 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13646
13647 Utf8Str ctrlName;
13648 LONG lPort;
13649 LONG lDevice;
13650 bool fTempEject;
13651 {
13652 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13653
13654 /* Need to query the details first, as the IMediumAttachment reference
13655 * might be to the original settings, which we are going to change. */
13656 ctrlName = pAttach->i_getControllerName();
13657 lPort = pAttach->i_getPort();
13658 lDevice = pAttach->i_getDevice();
13659 fTempEject = pAttach->i_getTempEject();
13660 }
13661
13662 if (!fTempEject)
13663 {
13664 /* Remember previously mounted medium. The medium before taking the
13665 * backup is not necessarily the same thing. */
13666 ComObjPtr<Medium> oldmedium;
13667 oldmedium = pAttach->i_getMedium();
13668
13669 i_setModified(IsModified_Storage);
13670 mMediumAttachments.backup();
13671
13672 // The backup operation makes the pAttach reference point to the
13673 // old settings. Re-get the correct reference.
13674 pAttach = i_findAttachment(*mMediumAttachments.data(),
13675 ctrlName,
13676 lPort,
13677 lDevice);
13678
13679 {
13680 AutoCaller autoAttachCaller(this);
13681 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13682
13683 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13684 if (!oldmedium.isNull())
13685 oldmedium->i_removeBackReference(mData->mUuid);
13686
13687 pAttach->i_updateMedium(NULL);
13688 pAttach->i_updateEjected();
13689 }
13690
13691 i_setModified(IsModified_Storage);
13692 }
13693 else
13694 {
13695 {
13696 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13697 pAttach->i_updateEjected();
13698 }
13699 }
13700
13701 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13702
13703 return S_OK;
13704}
13705
13706HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13707 com::Utf8Str &aResult)
13708{
13709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13710
13711 HRESULT hr = S_OK;
13712
13713 if (!mAuthLibCtx.hAuthLibrary)
13714 {
13715 /* Load the external authentication library. */
13716 Bstr authLibrary;
13717 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13718
13719 Utf8Str filename = authLibrary;
13720
13721 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13722 if (RT_FAILURE(vrc))
13723 hr = setErrorBoth(E_FAIL, vrc,
13724 tr("Could not load the external authentication library '%s' (%Rrc)"),
13725 filename.c_str(), vrc);
13726 }
13727
13728 /* The auth library might need the machine lock. */
13729 alock.release();
13730
13731 if (FAILED(hr))
13732 return hr;
13733
13734 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13735 {
13736 enum VRDEAuthParams
13737 {
13738 parmUuid = 1,
13739 parmGuestJudgement,
13740 parmUser,
13741 parmPassword,
13742 parmDomain,
13743 parmClientId
13744 };
13745
13746 AuthResult result = AuthResultAccessDenied;
13747
13748 Guid uuid(aAuthParams[parmUuid]);
13749 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13750 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13751
13752 result = AuthLibAuthenticate(&mAuthLibCtx,
13753 uuid.raw(), guestJudgement,
13754 aAuthParams[parmUser].c_str(),
13755 aAuthParams[parmPassword].c_str(),
13756 aAuthParams[parmDomain].c_str(),
13757 u32ClientId);
13758
13759 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13760 size_t cbPassword = aAuthParams[parmPassword].length();
13761 if (cbPassword)
13762 {
13763 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13764 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13765 }
13766
13767 if (result == AuthResultAccessGranted)
13768 aResult = "granted";
13769 else
13770 aResult = "denied";
13771
13772 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13773 aAuthParams[parmUser].c_str(), aResult.c_str()));
13774 }
13775 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13776 {
13777 enum VRDEAuthDisconnectParams
13778 {
13779 parmUuid = 1,
13780 parmClientId
13781 };
13782
13783 Guid uuid(aAuthParams[parmUuid]);
13784 uint32_t u32ClientId = 0;
13785 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13786 }
13787 else
13788 {
13789 hr = E_INVALIDARG;
13790 }
13791
13792 return hr;
13793}
13794
13795// public methods only for internal purposes
13796/////////////////////////////////////////////////////////////////////////////
13797
13798#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13799/**
13800 * Called from the client watcher thread to check for expected or unexpected
13801 * death of the client process that has a direct session to this machine.
13802 *
13803 * On Win32 and on OS/2, this method is called only when we've got the
13804 * mutex (i.e. the client has either died or terminated normally) so it always
13805 * returns @c true (the client is terminated, the session machine is
13806 * uninitialized).
13807 *
13808 * On other platforms, the method returns @c true if the client process has
13809 * terminated normally or abnormally and the session machine was uninitialized,
13810 * and @c false if the client process is still alive.
13811 *
13812 * @note Locks this object for writing.
13813 */
13814bool SessionMachine::i_checkForDeath()
13815{
13816 Uninit::Reason reason;
13817 bool terminated = false;
13818
13819 /* Enclose autoCaller with a block because calling uninit() from under it
13820 * will deadlock. */
13821 {
13822 AutoCaller autoCaller(this);
13823 if (!autoCaller.isOk())
13824 {
13825 /* return true if not ready, to cause the client watcher to exclude
13826 * the corresponding session from watching */
13827 LogFlowThisFunc(("Already uninitialized!\n"));
13828 return true;
13829 }
13830
13831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13832
13833 /* Determine the reason of death: if the session state is Closing here,
13834 * everything is fine. Otherwise it means that the client did not call
13835 * OnSessionEnd() before it released the IPC semaphore. This may happen
13836 * either because the client process has abnormally terminated, or
13837 * because it simply forgot to call ISession::Close() before exiting. We
13838 * threat the latter also as an abnormal termination (see
13839 * Session::uninit() for details). */
13840 reason = mData->mSession.mState == SessionState_Unlocking ?
13841 Uninit::Normal :
13842 Uninit::Abnormal;
13843
13844 if (mClientToken)
13845 terminated = mClientToken->release();
13846 } /* AutoCaller block */
13847
13848 if (terminated)
13849 uninit(reason);
13850
13851 return terminated;
13852}
13853
13854void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13855{
13856 LogFlowThisFunc(("\n"));
13857
13858 strTokenId.setNull();
13859
13860 AutoCaller autoCaller(this);
13861 AssertComRCReturnVoid(autoCaller.rc());
13862
13863 Assert(mClientToken);
13864 if (mClientToken)
13865 mClientToken->getId(strTokenId);
13866}
13867#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13868IToken *SessionMachine::i_getToken()
13869{
13870 LogFlowThisFunc(("\n"));
13871
13872 AutoCaller autoCaller(this);
13873 AssertComRCReturn(autoCaller.rc(), NULL);
13874
13875 Assert(mClientToken);
13876 if (mClientToken)
13877 return mClientToken->getToken();
13878 else
13879 return NULL;
13880}
13881#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13882
13883Machine::ClientToken *SessionMachine::i_getClientToken()
13884{
13885 LogFlowThisFunc(("\n"));
13886
13887 AutoCaller autoCaller(this);
13888 AssertComRCReturn(autoCaller.rc(), NULL);
13889
13890 return mClientToken;
13891}
13892
13893
13894/**
13895 * @note Locks this object for reading.
13896 */
13897HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13898{
13899 LogFlowThisFunc(("\n"));
13900
13901 AutoCaller autoCaller(this);
13902 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13903
13904 ComPtr<IInternalSessionControl> directControl;
13905 {
13906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13907 if (mData->mSession.mLockType == LockType_VM)
13908 directControl = mData->mSession.mDirectControl;
13909 }
13910
13911 /* ignore notifications sent after #OnSessionEnd() is called */
13912 if (!directControl)
13913 return S_OK;
13914
13915 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13916}
13917
13918/**
13919 * @note Locks this object for reading.
13920 */
13921HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13922 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13923 const Utf8Str &aGuestIp, LONG aGuestPort)
13924{
13925 LogFlowThisFunc(("\n"));
13926
13927 AutoCaller autoCaller(this);
13928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13929
13930 ComPtr<IInternalSessionControl> directControl;
13931 {
13932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13933 if (mData->mSession.mLockType == LockType_VM)
13934 directControl = mData->mSession.mDirectControl;
13935 }
13936
13937 /* ignore notifications sent after #OnSessionEnd() is called */
13938 if (!directControl)
13939 return S_OK;
13940 /*
13941 * instead acting like callback we ask IVirtualBox deliver corresponding event
13942 */
13943
13944 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13945 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13946 return S_OK;
13947}
13948
13949/**
13950 * @note Locks this object for reading.
13951 */
13952HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13953{
13954 LogFlowThisFunc(("\n"));
13955
13956 AutoCaller autoCaller(this);
13957 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13958
13959 ComPtr<IInternalSessionControl> directControl;
13960 {
13961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13962 if (mData->mSession.mLockType == LockType_VM)
13963 directControl = mData->mSession.mDirectControl;
13964 }
13965
13966 /* ignore notifications sent after #OnSessionEnd() is called */
13967 if (!directControl)
13968 return S_OK;
13969
13970 return directControl->OnAudioAdapterChange(audioAdapter);
13971}
13972
13973/**
13974 * @note Locks this object for reading.
13975 */
13976HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13977{
13978 LogFlowThisFunc(("\n"));
13979
13980 AutoCaller autoCaller(this);
13981 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13982
13983 ComPtr<IInternalSessionControl> directControl;
13984 {
13985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13986 if (mData->mSession.mLockType == LockType_VM)
13987 directControl = mData->mSession.mDirectControl;
13988 }
13989
13990 /* ignore notifications sent after #OnSessionEnd() is called */
13991 if (!directControl)
13992 return S_OK;
13993
13994 return directControl->OnSerialPortChange(serialPort);
13995}
13996
13997/**
13998 * @note Locks this object for reading.
13999 */
14000HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14001{
14002 LogFlowThisFunc(("\n"));
14003
14004 AutoCaller autoCaller(this);
14005 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14006
14007 ComPtr<IInternalSessionControl> directControl;
14008 {
14009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14010 if (mData->mSession.mLockType == LockType_VM)
14011 directControl = mData->mSession.mDirectControl;
14012 }
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017
14018 return directControl->OnParallelPortChange(parallelPort);
14019}
14020
14021/**
14022 * @note Locks this object for reading.
14023 */
14024HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14025{
14026 LogFlowThisFunc(("\n"));
14027
14028 AutoCaller autoCaller(this);
14029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14030
14031 ComPtr<IInternalSessionControl> directControl;
14032 {
14033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14034 if (mData->mSession.mLockType == LockType_VM)
14035 directControl = mData->mSession.mDirectControl;
14036 }
14037
14038 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14039
14040 /* ignore notifications sent after #OnSessionEnd() is called */
14041 if (!directControl)
14042 return S_OK;
14043
14044 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14045}
14046
14047/**
14048 * @note Locks this object for reading.
14049 */
14050HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14051{
14052 LogFlowThisFunc(("\n"));
14053
14054 AutoCaller autoCaller(this);
14055 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14056
14057 ComPtr<IInternalSessionControl> directControl;
14058 {
14059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14060 if (mData->mSession.mLockType == LockType_VM)
14061 directControl = mData->mSession.mDirectControl;
14062 }
14063
14064 mParent->i_onMediumChanged(aAttachment);
14065
14066 /* ignore notifications sent after #OnSessionEnd() is called */
14067 if (!directControl)
14068 return S_OK;
14069
14070 return directControl->OnMediumChange(aAttachment, aForce);
14071}
14072
14073HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14074{
14075 LogFlowThisFunc(("\n"));
14076
14077 AutoCaller autoCaller(this);
14078 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14079
14080 ComPtr<IInternalSessionControl> directControl;
14081 {
14082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14083 if (mData->mSession.mLockType == LockType_VM)
14084 directControl = mData->mSession.mDirectControl;
14085 }
14086
14087 /* ignore notifications sent after #OnSessionEnd() is called */
14088 if (!directControl)
14089 return S_OK;
14090
14091 return directControl->OnVMProcessPriorityChange(aPriority);
14092}
14093
14094/**
14095 * @note Locks this object for reading.
14096 */
14097HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14098{
14099 LogFlowThisFunc(("\n"));
14100
14101 AutoCaller autoCaller(this);
14102 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14103
14104 ComPtr<IInternalSessionControl> directControl;
14105 {
14106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14107 if (mData->mSession.mLockType == LockType_VM)
14108 directControl = mData->mSession.mDirectControl;
14109 }
14110
14111 /* ignore notifications sent after #OnSessionEnd() is called */
14112 if (!directControl)
14113 return S_OK;
14114
14115 return directControl->OnCPUChange(aCPU, aRemove);
14116}
14117
14118HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14119{
14120 LogFlowThisFunc(("\n"));
14121
14122 AutoCaller autoCaller(this);
14123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14124
14125 ComPtr<IInternalSessionControl> directControl;
14126 {
14127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14128 if (mData->mSession.mLockType == LockType_VM)
14129 directControl = mData->mSession.mDirectControl;
14130 }
14131
14132 /* ignore notifications sent after #OnSessionEnd() is called */
14133 if (!directControl)
14134 return S_OK;
14135
14136 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14137}
14138
14139/**
14140 * @note Locks this object for reading.
14141 */
14142HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14143{
14144 LogFlowThisFunc(("\n"));
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14148
14149 ComPtr<IInternalSessionControl> directControl;
14150 {
14151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14152 if (mData->mSession.mLockType == LockType_VM)
14153 directControl = mData->mSession.mDirectControl;
14154 }
14155
14156 /* ignore notifications sent after #OnSessionEnd() is called */
14157 if (!directControl)
14158 return S_OK;
14159
14160 return directControl->OnVRDEServerChange(aRestart);
14161}
14162
14163/**
14164 * @note Locks this object for reading.
14165 */
14166HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14167{
14168 LogFlowThisFunc(("\n"));
14169
14170 AutoCaller autoCaller(this);
14171 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14172
14173 ComPtr<IInternalSessionControl> directControl;
14174 {
14175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14176 if (mData->mSession.mLockType == LockType_VM)
14177 directControl = mData->mSession.mDirectControl;
14178 }
14179
14180 /* ignore notifications sent after #OnSessionEnd() is called */
14181 if (!directControl)
14182 return S_OK;
14183
14184 return directControl->OnRecordingChange(aEnable);
14185}
14186
14187/**
14188 * @note Locks this object for reading.
14189 */
14190HRESULT SessionMachine::i_onUSBControllerChange()
14191{
14192 LogFlowThisFunc(("\n"));
14193
14194 AutoCaller autoCaller(this);
14195 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14196
14197 ComPtr<IInternalSessionControl> directControl;
14198 {
14199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14200 if (mData->mSession.mLockType == LockType_VM)
14201 directControl = mData->mSession.mDirectControl;
14202 }
14203
14204 /* ignore notifications sent after #OnSessionEnd() is called */
14205 if (!directControl)
14206 return S_OK;
14207
14208 return directControl->OnUSBControllerChange();
14209}
14210
14211/**
14212 * @note Locks this object for reading.
14213 */
14214HRESULT SessionMachine::i_onSharedFolderChange()
14215{
14216 LogFlowThisFunc(("\n"));
14217
14218 AutoCaller autoCaller(this);
14219 AssertComRCReturnRC(autoCaller.rc());
14220
14221 ComPtr<IInternalSessionControl> directControl;
14222 {
14223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14224 if (mData->mSession.mLockType == LockType_VM)
14225 directControl = mData->mSession.mDirectControl;
14226 }
14227
14228 /* ignore notifications sent after #OnSessionEnd() is called */
14229 if (!directControl)
14230 return S_OK;
14231
14232 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14233}
14234
14235/**
14236 * @note Locks this object for reading.
14237 */
14238HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14239{
14240 LogFlowThisFunc(("\n"));
14241
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturnRC(autoCaller.rc());
14244
14245 ComPtr<IInternalSessionControl> directControl;
14246 {
14247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14248 if (mData->mSession.mLockType == LockType_VM)
14249 directControl = mData->mSession.mDirectControl;
14250 }
14251
14252 /* ignore notifications sent after #OnSessionEnd() is called */
14253 if (!directControl)
14254 return S_OK;
14255
14256 return directControl->OnClipboardModeChange(aClipboardMode);
14257}
14258
14259/**
14260 * @note Locks this object for reading.
14261 */
14262HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14263{
14264 LogFlowThisFunc(("\n"));
14265
14266 AutoCaller autoCaller(this);
14267 AssertComRCReturnRC(autoCaller.rc());
14268
14269 ComPtr<IInternalSessionControl> directControl;
14270 {
14271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14272 if (mData->mSession.mLockType == LockType_VM)
14273 directControl = mData->mSession.mDirectControl;
14274 }
14275
14276 /* ignore notifications sent after #OnSessionEnd() is called */
14277 if (!directControl)
14278 return S_OK;
14279
14280 return directControl->OnClipboardFileTransferModeChange(aEnable);
14281}
14282
14283/**
14284 * @note Locks this object for reading.
14285 */
14286HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14287{
14288 LogFlowThisFunc(("\n"));
14289
14290 AutoCaller autoCaller(this);
14291 AssertComRCReturnRC(autoCaller.rc());
14292
14293 ComPtr<IInternalSessionControl> directControl;
14294 {
14295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14296 if (mData->mSession.mLockType == LockType_VM)
14297 directControl = mData->mSession.mDirectControl;
14298 }
14299
14300 /* ignore notifications sent after #OnSessionEnd() is called */
14301 if (!directControl)
14302 return S_OK;
14303
14304 return directControl->OnDnDModeChange(aDnDMode);
14305}
14306
14307/**
14308 * @note Locks this object for reading.
14309 */
14310HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14311{
14312 LogFlowThisFunc(("\n"));
14313
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14316
14317 ComPtr<IInternalSessionControl> directControl;
14318 {
14319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14320 if (mData->mSession.mLockType == LockType_VM)
14321 directControl = mData->mSession.mDirectControl;
14322 }
14323
14324 /* ignore notifications sent after #OnSessionEnd() is called */
14325 if (!directControl)
14326 return S_OK;
14327
14328 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14329}
14330
14331/**
14332 * @note Locks this object for reading.
14333 */
14334HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14335{
14336 LogFlowThisFunc(("\n"));
14337
14338 AutoCaller autoCaller(this);
14339 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14340
14341 ComPtr<IInternalSessionControl> directControl;
14342 {
14343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14344 if (mData->mSession.mLockType == LockType_VM)
14345 directControl = mData->mSession.mDirectControl;
14346 }
14347
14348 /* ignore notifications sent after #OnSessionEnd() is called */
14349 if (!directControl)
14350 return S_OK;
14351
14352 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14353}
14354
14355/**
14356 * Returns @c true if this machine's USB controller reports it has a matching
14357 * filter for the given USB device and @c false otherwise.
14358 *
14359 * @note locks this object for reading.
14360 */
14361bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14362{
14363 AutoCaller autoCaller(this);
14364 /* silently return if not ready -- this method may be called after the
14365 * direct machine session has been called */
14366 if (!autoCaller.isOk())
14367 return false;
14368
14369#ifdef VBOX_WITH_USB
14370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14371
14372 switch (mData->mMachineState)
14373 {
14374 case MachineState_Starting:
14375 case MachineState_Restoring:
14376 case MachineState_TeleportingIn:
14377 case MachineState_Paused:
14378 case MachineState_Running:
14379 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14380 * elsewhere... */
14381 alock.release();
14382 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14383 default: break;
14384 }
14385#else
14386 NOREF(aDevice);
14387 NOREF(aMaskedIfs);
14388#endif
14389 return false;
14390}
14391
14392/**
14393 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14394 */
14395HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14396 IVirtualBoxErrorInfo *aError,
14397 ULONG aMaskedIfs,
14398 const com::Utf8Str &aCaptureFilename)
14399{
14400 LogFlowThisFunc(("\n"));
14401
14402 AutoCaller autoCaller(this);
14403
14404 /* This notification may happen after the machine object has been
14405 * uninitialized (the session was closed), so don't assert. */
14406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14407
14408 ComPtr<IInternalSessionControl> directControl;
14409 {
14410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14411 if (mData->mSession.mLockType == LockType_VM)
14412 directControl = mData->mSession.mDirectControl;
14413 }
14414
14415 /* fail on notifications sent after #OnSessionEnd() is called, it is
14416 * expected by the caller */
14417 if (!directControl)
14418 return E_FAIL;
14419
14420 /* No locks should be held at this point. */
14421 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14422 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14423
14424 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14425}
14426
14427/**
14428 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14429 */
14430HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14431 IVirtualBoxErrorInfo *aError)
14432{
14433 LogFlowThisFunc(("\n"));
14434
14435 AutoCaller autoCaller(this);
14436
14437 /* This notification may happen after the machine object has been
14438 * uninitialized (the session was closed), so don't assert. */
14439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14440
14441 ComPtr<IInternalSessionControl> directControl;
14442 {
14443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14444 if (mData->mSession.mLockType == LockType_VM)
14445 directControl = mData->mSession.mDirectControl;
14446 }
14447
14448 /* fail on notifications sent after #OnSessionEnd() is called, it is
14449 * expected by the caller */
14450 if (!directControl)
14451 return E_FAIL;
14452
14453 /* No locks should be held at this point. */
14454 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14455 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14456
14457 return directControl->OnUSBDeviceDetach(aId, aError);
14458}
14459
14460// protected methods
14461/////////////////////////////////////////////////////////////////////////////
14462
14463/**
14464 * Deletes the given file if it is no longer in use by either the current machine state
14465 * (if the machine is "saved") or any of the machine's snapshots.
14466 *
14467 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14468 * but is different for each SnapshotMachine. When calling this, the order of calling this
14469 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14470 * is therefore critical. I know, it's all rather messy.
14471 *
14472 * @param strStateFile
14473 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14474 * the test for whether the saved state file is in use.
14475 */
14476void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14477 Snapshot *pSnapshotToIgnore)
14478{
14479 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14480 if ( (strStateFile.isNotEmpty())
14481 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14482 )
14483 // ... and it must also not be shared with other snapshots
14484 if ( !mData->mFirstSnapshot
14485 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14486 // this checks the SnapshotMachine's state file paths
14487 )
14488 RTFileDelete(strStateFile.c_str());
14489}
14490
14491/**
14492 * Locks the attached media.
14493 *
14494 * All attached hard disks are locked for writing and DVD/floppy are locked for
14495 * reading. Parents of attached hard disks (if any) are locked for reading.
14496 *
14497 * This method also performs accessibility check of all media it locks: if some
14498 * media is inaccessible, the method will return a failure and a bunch of
14499 * extended error info objects per each inaccessible medium.
14500 *
14501 * Note that this method is atomic: if it returns a success, all media are
14502 * locked as described above; on failure no media is locked at all (all
14503 * succeeded individual locks will be undone).
14504 *
14505 * The caller is responsible for doing the necessary state sanity checks.
14506 *
14507 * The locks made by this method must be undone by calling #unlockMedia() when
14508 * no more needed.
14509 */
14510HRESULT SessionMachine::i_lockMedia()
14511{
14512 AutoCaller autoCaller(this);
14513 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14514
14515 AutoMultiWriteLock2 alock(this->lockHandle(),
14516 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14517
14518 /* bail out if trying to lock things with already set up locking */
14519 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14520
14521 MultiResult mrc(S_OK);
14522
14523 /* Collect locking information for all medium objects attached to the VM. */
14524 for (MediumAttachmentList::const_iterator
14525 it = mMediumAttachments->begin();
14526 it != mMediumAttachments->end();
14527 ++it)
14528 {
14529 MediumAttachment *pAtt = *it;
14530 DeviceType_T devType = pAtt->i_getType();
14531 Medium *pMedium = pAtt->i_getMedium();
14532
14533 MediumLockList *pMediumLockList(new MediumLockList());
14534 // There can be attachments without a medium (floppy/dvd), and thus
14535 // it's impossible to create a medium lock list. It still makes sense
14536 // to have the empty medium lock list in the map in case a medium is
14537 // attached later.
14538 if (pMedium != NULL)
14539 {
14540 MediumType_T mediumType = pMedium->i_getType();
14541 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14542 || mediumType == MediumType_Shareable;
14543 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14544
14545 alock.release();
14546 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14547 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14548 false /* fMediumLockWriteAll */,
14549 NULL,
14550 *pMediumLockList);
14551 alock.acquire();
14552 if (FAILED(mrc))
14553 {
14554 delete pMediumLockList;
14555 mData->mSession.mLockedMedia.Clear();
14556 break;
14557 }
14558 }
14559
14560 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14561 if (FAILED(rc))
14562 {
14563 mData->mSession.mLockedMedia.Clear();
14564 mrc = setError(rc,
14565 tr("Collecting locking information for all attached media failed"));
14566 break;
14567 }
14568 }
14569
14570 if (SUCCEEDED(mrc))
14571 {
14572 /* Now lock all media. If this fails, nothing is locked. */
14573 alock.release();
14574 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14575 alock.acquire();
14576 if (FAILED(rc))
14577 {
14578 mrc = setError(rc,
14579 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14580 }
14581 }
14582
14583 return mrc;
14584}
14585
14586/**
14587 * Undoes the locks made by by #lockMedia().
14588 */
14589HRESULT SessionMachine::i_unlockMedia()
14590{
14591 AutoCaller autoCaller(this);
14592 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14593
14594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14595
14596 /* we may be holding important error info on the current thread;
14597 * preserve it */
14598 ErrorInfoKeeper eik;
14599
14600 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14601 AssertComRC(rc);
14602 return rc;
14603}
14604
14605/**
14606 * Helper to change the machine state (reimplementation).
14607 *
14608 * @note Locks this object for writing.
14609 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14610 * it can cause crashes in random places due to unexpectedly committing
14611 * the current settings. The caller is responsible for that. The call
14612 * to saveStateSettings is fine, because this method does not commit.
14613 */
14614HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14615{
14616 LogFlowThisFuncEnter();
14617 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14618
14619 AutoCaller autoCaller(this);
14620 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14621
14622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14623
14624 MachineState_T oldMachineState = mData->mMachineState;
14625
14626 AssertMsgReturn(oldMachineState != aMachineState,
14627 ("oldMachineState=%s, aMachineState=%s\n",
14628 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14629 E_FAIL);
14630
14631 HRESULT rc = S_OK;
14632
14633 int stsFlags = 0;
14634 bool deleteSavedState = false;
14635
14636 /* detect some state transitions */
14637
14638 if ( ( oldMachineState == MachineState_Saved
14639 && aMachineState == MachineState_Restoring)
14640 || ( ( oldMachineState == MachineState_PoweredOff
14641 || oldMachineState == MachineState_Teleported
14642 || oldMachineState == MachineState_Aborted
14643 )
14644 && ( aMachineState == MachineState_TeleportingIn
14645 || aMachineState == MachineState_Starting
14646 )
14647 )
14648 )
14649 {
14650 /* The EMT thread is about to start */
14651
14652 /* Nothing to do here for now... */
14653
14654 /// @todo NEWMEDIA don't let mDVDDrive and other children
14655 /// change anything when in the Starting/Restoring state
14656 }
14657 else if ( ( oldMachineState == MachineState_Running
14658 || oldMachineState == MachineState_Paused
14659 || oldMachineState == MachineState_Teleporting
14660 || oldMachineState == MachineState_OnlineSnapshotting
14661 || oldMachineState == MachineState_LiveSnapshotting
14662 || oldMachineState == MachineState_Stuck
14663 || oldMachineState == MachineState_Starting
14664 || oldMachineState == MachineState_Stopping
14665 || oldMachineState == MachineState_Saving
14666 || oldMachineState == MachineState_Restoring
14667 || oldMachineState == MachineState_TeleportingPausedVM
14668 || oldMachineState == MachineState_TeleportingIn
14669 )
14670 && ( aMachineState == MachineState_PoweredOff
14671 || aMachineState == MachineState_Saved
14672 || aMachineState == MachineState_Teleported
14673 || aMachineState == MachineState_Aborted
14674 )
14675 )
14676 {
14677 /* The EMT thread has just stopped, unlock attached media. Note that as
14678 * opposed to locking that is done from Console, we do unlocking here
14679 * because the VM process may have aborted before having a chance to
14680 * properly unlock all media it locked. */
14681
14682 unlockMedia();
14683 }
14684
14685 if (oldMachineState == MachineState_Restoring)
14686 {
14687 if (aMachineState != MachineState_Saved)
14688 {
14689 /*
14690 * delete the saved state file once the machine has finished
14691 * restoring from it (note that Console sets the state from
14692 * Restoring to Saved if the VM couldn't restore successfully,
14693 * to give the user an ability to fix an error and retry --
14694 * we keep the saved state file in this case)
14695 */
14696 deleteSavedState = true;
14697 }
14698 }
14699 else if ( oldMachineState == MachineState_Saved
14700 && ( aMachineState == MachineState_PoweredOff
14701 || aMachineState == MachineState_Aborted
14702 || aMachineState == MachineState_Teleported
14703 )
14704 )
14705 {
14706 /*
14707 * delete the saved state after SessionMachine::ForgetSavedState() is called
14708 * or if the VM process (owning a direct VM session) crashed while the
14709 * VM was Saved
14710 */
14711
14712 /// @todo (dmik)
14713 // Not sure that deleting the saved state file just because of the
14714 // client death before it attempted to restore the VM is a good
14715 // thing. But when it crashes we need to go to the Aborted state
14716 // which cannot have the saved state file associated... The only
14717 // way to fix this is to make the Aborted condition not a VM state
14718 // but a bool flag: i.e., when a crash occurs, set it to true and
14719 // change the state to PoweredOff or Saved depending on the
14720 // saved state presence.
14721
14722 deleteSavedState = true;
14723 mData->mCurrentStateModified = TRUE;
14724 stsFlags |= SaveSTS_CurStateModified;
14725 }
14726
14727 if ( aMachineState == MachineState_Starting
14728 || aMachineState == MachineState_Restoring
14729 || aMachineState == MachineState_TeleportingIn
14730 )
14731 {
14732 /* set the current state modified flag to indicate that the current
14733 * state is no more identical to the state in the
14734 * current snapshot */
14735 if (!mData->mCurrentSnapshot.isNull())
14736 {
14737 mData->mCurrentStateModified = TRUE;
14738 stsFlags |= SaveSTS_CurStateModified;
14739 }
14740 }
14741
14742 if (deleteSavedState)
14743 {
14744 if (mRemoveSavedState)
14745 {
14746 Assert(!mSSData->strStateFilePath.isEmpty());
14747
14748 // it is safe to delete the saved state file if ...
14749 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14750 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14751 // ... none of the snapshots share the saved state file
14752 )
14753 RTFileDelete(mSSData->strStateFilePath.c_str());
14754 }
14755
14756 mSSData->strStateFilePath.setNull();
14757 stsFlags |= SaveSTS_StateFilePath;
14758 }
14759
14760 /* redirect to the underlying peer machine */
14761 mPeer->i_setMachineState(aMachineState);
14762
14763 if ( oldMachineState != MachineState_RestoringSnapshot
14764 && ( aMachineState == MachineState_PoweredOff
14765 || aMachineState == MachineState_Teleported
14766 || aMachineState == MachineState_Aborted
14767 || aMachineState == MachineState_Saved))
14768 {
14769 /* the machine has stopped execution
14770 * (or the saved state file was adopted) */
14771 stsFlags |= SaveSTS_StateTimeStamp;
14772 }
14773
14774 if ( ( oldMachineState == MachineState_PoweredOff
14775 || oldMachineState == MachineState_Aborted
14776 || oldMachineState == MachineState_Teleported
14777 )
14778 && aMachineState == MachineState_Saved)
14779 {
14780 /* the saved state file was adopted */
14781 Assert(!mSSData->strStateFilePath.isEmpty());
14782 stsFlags |= SaveSTS_StateFilePath;
14783 }
14784
14785#ifdef VBOX_WITH_GUEST_PROPS
14786 if ( aMachineState == MachineState_PoweredOff
14787 || aMachineState == MachineState_Aborted
14788 || aMachineState == MachineState_Teleported)
14789 {
14790 /* Make sure any transient guest properties get removed from the
14791 * property store on shutdown. */
14792 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14793
14794 /* remove it from the settings representation */
14795 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14796 for (settings::GuestPropertiesList::iterator
14797 it = llGuestProperties.begin();
14798 it != llGuestProperties.end();
14799 /*nothing*/)
14800 {
14801 const settings::GuestProperty &prop = *it;
14802 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14803 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14804 {
14805 it = llGuestProperties.erase(it);
14806 fNeedsSaving = true;
14807 }
14808 else
14809 {
14810 ++it;
14811 }
14812 }
14813
14814 /* Additionally remove it from the HWData representation. Required to
14815 * keep everything in sync, as this is what the API keeps using. */
14816 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14817 for (HWData::GuestPropertyMap::iterator
14818 it = llHWGuestProperties.begin();
14819 it != llHWGuestProperties.end();
14820 /*nothing*/)
14821 {
14822 uint32_t fFlags = it->second.mFlags;
14823 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14824 {
14825 /* iterator where we need to continue after the erase call
14826 * (C++03 is a fact still, and it doesn't return the iterator
14827 * which would allow continuing) */
14828 HWData::GuestPropertyMap::iterator it2 = it;
14829 ++it2;
14830 llHWGuestProperties.erase(it);
14831 it = it2;
14832 fNeedsSaving = true;
14833 }
14834 else
14835 {
14836 ++it;
14837 }
14838 }
14839
14840 if (fNeedsSaving)
14841 {
14842 mData->mCurrentStateModified = TRUE;
14843 stsFlags |= SaveSTS_CurStateModified;
14844 }
14845 }
14846#endif /* VBOX_WITH_GUEST_PROPS */
14847
14848 rc = i_saveStateSettings(stsFlags);
14849
14850 if ( ( oldMachineState != MachineState_PoweredOff
14851 && oldMachineState != MachineState_Aborted
14852 && oldMachineState != MachineState_Teleported
14853 )
14854 && ( aMachineState == MachineState_PoweredOff
14855 || aMachineState == MachineState_Aborted
14856 || aMachineState == MachineState_Teleported
14857 )
14858 )
14859 {
14860 /* we've been shut down for any reason */
14861 /* no special action so far */
14862 }
14863
14864 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14865 LogFlowThisFuncLeave();
14866 return rc;
14867}
14868
14869/**
14870 * Sends the current machine state value to the VM process.
14871 *
14872 * @note Locks this object for reading, then calls a client process.
14873 */
14874HRESULT SessionMachine::i_updateMachineStateOnClient()
14875{
14876 AutoCaller autoCaller(this);
14877 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14878
14879 ComPtr<IInternalSessionControl> directControl;
14880 {
14881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14882 AssertReturn(!!mData, E_FAIL);
14883 if (mData->mSession.mLockType == LockType_VM)
14884 directControl = mData->mSession.mDirectControl;
14885
14886 /* directControl may be already set to NULL here in #OnSessionEnd()
14887 * called too early by the direct session process while there is still
14888 * some operation (like deleting the snapshot) in progress. The client
14889 * process in this case is waiting inside Session::close() for the
14890 * "end session" process object to complete, while #uninit() called by
14891 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14892 * operation to complete. For now, we accept this inconsistent behavior
14893 * and simply do nothing here. */
14894
14895 if (mData->mSession.mState == SessionState_Unlocking)
14896 return S_OK;
14897 }
14898
14899 /* ignore notifications sent after #OnSessionEnd() is called */
14900 if (!directControl)
14901 return S_OK;
14902
14903 return directControl->UpdateMachineState(mData->mMachineState);
14904}
14905
14906
14907/*static*/
14908HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14909{
14910 va_list args;
14911 va_start(args, pcszMsg);
14912 HRESULT rc = setErrorInternalV(aResultCode,
14913 getStaticClassIID(),
14914 getStaticComponentName(),
14915 pcszMsg, args,
14916 false /* aWarning */,
14917 true /* aLogIt */);
14918 va_end(args);
14919 return rc;
14920}
14921
14922
14923HRESULT Machine::updateState(MachineState_T aState)
14924{
14925 NOREF(aState);
14926 ReturnComNotImplemented();
14927}
14928
14929HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14930{
14931 NOREF(aProgress);
14932 ReturnComNotImplemented();
14933}
14934
14935HRESULT Machine::endPowerUp(LONG aResult)
14936{
14937 NOREF(aResult);
14938 ReturnComNotImplemented();
14939}
14940
14941HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14942{
14943 NOREF(aProgress);
14944 ReturnComNotImplemented();
14945}
14946
14947HRESULT Machine::endPoweringDown(LONG aResult,
14948 const com::Utf8Str &aErrMsg)
14949{
14950 NOREF(aResult);
14951 NOREF(aErrMsg);
14952 ReturnComNotImplemented();
14953}
14954
14955HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14956 BOOL *aMatched,
14957 ULONG *aMaskedInterfaces)
14958{
14959 NOREF(aDevice);
14960 NOREF(aMatched);
14961 NOREF(aMaskedInterfaces);
14962 ReturnComNotImplemented();
14963
14964}
14965
14966HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14967{
14968 NOREF(aId); NOREF(aCaptureFilename);
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14973 BOOL aDone)
14974{
14975 NOREF(aId);
14976 NOREF(aDone);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::autoCaptureUSBDevices()
14981{
14982 ReturnComNotImplemented();
14983}
14984
14985HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14986{
14987 NOREF(aDone);
14988 ReturnComNotImplemented();
14989}
14990
14991HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14992 ComPtr<IProgress> &aProgress)
14993{
14994 NOREF(aSession);
14995 NOREF(aProgress);
14996 ReturnComNotImplemented();
14997}
14998
14999HRESULT Machine::finishOnlineMergeMedium()
15000{
15001 ReturnComNotImplemented();
15002}
15003
15004HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15005 std::vector<com::Utf8Str> &aValues,
15006 std::vector<LONG64> &aTimestamps,
15007 std::vector<com::Utf8Str> &aFlags)
15008{
15009 NOREF(aNames);
15010 NOREF(aValues);
15011 NOREF(aTimestamps);
15012 NOREF(aFlags);
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15017 const com::Utf8Str &aValue,
15018 LONG64 aTimestamp,
15019 const com::Utf8Str &aFlags)
15020{
15021 NOREF(aName);
15022 NOREF(aValue);
15023 NOREF(aTimestamp);
15024 NOREF(aFlags);
15025 ReturnComNotImplemented();
15026}
15027
15028HRESULT Machine::lockMedia()
15029{
15030 ReturnComNotImplemented();
15031}
15032
15033HRESULT Machine::unlockMedia()
15034{
15035 ReturnComNotImplemented();
15036}
15037
15038HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15039 ComPtr<IMediumAttachment> &aNewAttachment)
15040{
15041 NOREF(aAttachment);
15042 NOREF(aNewAttachment);
15043 ReturnComNotImplemented();
15044}
15045
15046HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15047 ULONG aCpuUser,
15048 ULONG aCpuKernel,
15049 ULONG aCpuIdle,
15050 ULONG aMemTotal,
15051 ULONG aMemFree,
15052 ULONG aMemBalloon,
15053 ULONG aMemShared,
15054 ULONG aMemCache,
15055 ULONG aPagedTotal,
15056 ULONG aMemAllocTotal,
15057 ULONG aMemFreeTotal,
15058 ULONG aMemBalloonTotal,
15059 ULONG aMemSharedTotal,
15060 ULONG aVmNetRx,
15061 ULONG aVmNetTx)
15062{
15063 NOREF(aValidStats);
15064 NOREF(aCpuUser);
15065 NOREF(aCpuKernel);
15066 NOREF(aCpuIdle);
15067 NOREF(aMemTotal);
15068 NOREF(aMemFree);
15069 NOREF(aMemBalloon);
15070 NOREF(aMemShared);
15071 NOREF(aMemCache);
15072 NOREF(aPagedTotal);
15073 NOREF(aMemAllocTotal);
15074 NOREF(aMemFreeTotal);
15075 NOREF(aMemBalloonTotal);
15076 NOREF(aMemSharedTotal);
15077 NOREF(aVmNetRx);
15078 NOREF(aVmNetTx);
15079 ReturnComNotImplemented();
15080}
15081
15082HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15083 com::Utf8Str &aResult)
15084{
15085 NOREF(aAuthParams);
15086 NOREF(aResult);
15087 ReturnComNotImplemented();
15088}
15089
15090com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15091{
15092 com::Utf8Str strControllerName = "Unknown";
15093 switch (aBusType)
15094 {
15095 case StorageBus_IDE:
15096 {
15097 strControllerName = "IDE";
15098 break;
15099 }
15100 case StorageBus_SATA:
15101 {
15102 strControllerName = "SATA";
15103 break;
15104 }
15105 case StorageBus_SCSI:
15106 {
15107 strControllerName = "SCSI";
15108 break;
15109 }
15110 case StorageBus_Floppy:
15111 {
15112 strControllerName = "Floppy";
15113 break;
15114 }
15115 case StorageBus_SAS:
15116 {
15117 strControllerName = "SAS";
15118 break;
15119 }
15120 case StorageBus_USB:
15121 {
15122 strControllerName = "USB";
15123 break;
15124 }
15125 default:
15126 break;
15127 }
15128 return strControllerName;
15129}
15130
15131HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15132{
15133 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15134
15135 AutoCaller autoCaller(this);
15136 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15137
15138 HRESULT rc = S_OK;
15139
15140 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15141 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15142 rc = getUSBDeviceFilters(usbDeviceFilters);
15143 if (FAILED(rc)) return rc;
15144
15145 NOREF(aFlags);
15146 com::Utf8Str osTypeId;
15147 ComObjPtr<GuestOSType> osType = NULL;
15148
15149 /* Get the guest os type as a string from the VB. */
15150 rc = getOSTypeId(osTypeId);
15151 if (FAILED(rc)) return rc;
15152
15153 /* Get the os type obj that coresponds, can be used to get
15154 * the defaults for this guest OS. */
15155 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15156 if (FAILED(rc)) return rc;
15157
15158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15159
15160 /* Let the OS type select 64-bit ness. */
15161 mHWData->mLongMode = osType->i_is64Bit()
15162 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15163
15164 /* Let the OS type enable the X2APIC */
15165 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15166
15167 /* This one covers IOAPICEnabled. */
15168 mBIOSSettings->i_applyDefaults(osType);
15169
15170 /* Initialize default record settings. */
15171 mRecordingSettings->i_applyDefaults();
15172
15173 /* Initialize default BIOS settings here */
15174 /* Hardware virtualization must be ON by default */
15175 mHWData->mAPIC = true;
15176 mHWData->mHWVirtExEnabled = true;
15177
15178 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15179 if (FAILED(rc)) return rc;
15180
15181 /* Graphics stuff. */
15182 GraphicsControllerType_T graphicsController;
15183 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15184 if (FAILED(rc)) return rc;
15185
15186 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15187 if (FAILED(rc)) return rc;
15188
15189 ULONG vramSize;
15190 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15194 if (FAILED(rc)) return rc;
15195
15196 BOOL fAccelerate2DVideoEnabled;
15197 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15198 if (FAILED(rc)) return rc;
15199
15200 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15201 if (FAILED(rc)) return rc;
15202
15203 BOOL fAccelerate3DEnabled;
15204 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15205 if (FAILED(rc)) return rc;
15206
15207 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15208 if (FAILED(rc)) return rc;
15209
15210 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15211 if (FAILED(rc)) return rc;
15212
15213 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15214 if (FAILED(rc)) return rc;
15215
15216 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15217 if (FAILED(rc)) return rc;
15218
15219 BOOL mRTCUseUTC;
15220 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15221 if (FAILED(rc)) return rc;
15222
15223 setRTCUseUTC(mRTCUseUTC);
15224 if (FAILED(rc)) return rc;
15225
15226 /* the setter does more than just the assignment, so use it */
15227 ChipsetType_T enmChipsetType;
15228 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15229 if (FAILED(rc)) return rc;
15230
15231 rc = COMSETTER(ChipsetType)(enmChipsetType);
15232 if (FAILED(rc)) return rc;
15233
15234 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15235 if (FAILED(rc)) return rc;
15236
15237 /* Apply IOMMU defaults. */
15238 IommuType_T enmIommuType;
15239 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15240 if (FAILED(rc)) return rc;
15241
15242 rc = COMSETTER(IommuType)(enmIommuType);
15243 if (FAILED(rc)) return rc;
15244
15245 /* Apply network adapters defaults */
15246 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15247 mNetworkAdapters[slot]->i_applyDefaults(osType);
15248
15249 /* Apply serial port defaults */
15250 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15251 mSerialPorts[slot]->i_applyDefaults(osType);
15252
15253 /* Apply parallel port defaults - not OS dependent*/
15254 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15255 mParallelPorts[slot]->i_applyDefaults();
15256
15257 /* Audio stuff. */
15258 AudioControllerType_T audioController;
15259 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15260 if (FAILED(rc)) return rc;
15261
15262 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15263 if (FAILED(rc)) return rc;
15264
15265 AudioCodecType_T audioCodec;
15266 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15267 if (FAILED(rc)) return rc;
15268
15269 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15270 if (FAILED(rc)) return rc;
15271
15272 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15273 if (FAILED(rc)) return rc;
15274
15275 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15276 if (FAILED(rc)) return rc;
15277
15278 /* Storage Controllers */
15279 StorageControllerType_T hdStorageControllerType;
15280 StorageBus_T hdStorageBusType;
15281 StorageControllerType_T dvdStorageControllerType;
15282 StorageBus_T dvdStorageBusType;
15283 BOOL recommendedFloppy;
15284 ComPtr<IStorageController> floppyController;
15285 ComPtr<IStorageController> hdController;
15286 ComPtr<IStorageController> dvdController;
15287 Utf8Str strFloppyName, strDVDName, strHDName;
15288
15289 /* GUI auto generates controller names using bus type. Do the same*/
15290 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15291
15292 /* Floppy recommended? add one. */
15293 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15294 if (FAILED(rc)) return rc;
15295 if (recommendedFloppy)
15296 {
15297 rc = addStorageController(strFloppyName,
15298 StorageBus_Floppy,
15299 floppyController);
15300 if (FAILED(rc)) return rc;
15301 }
15302
15303 /* Setup one DVD storage controller. */
15304 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15305 if (FAILED(rc)) return rc;
15306
15307 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15308 if (FAILED(rc)) return rc;
15309
15310 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15311
15312 rc = addStorageController(strDVDName,
15313 dvdStorageBusType,
15314 dvdController);
15315 if (FAILED(rc)) return rc;
15316
15317 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15318 if (FAILED(rc)) return rc;
15319
15320 /* Setup one HDD storage controller. */
15321 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15322 if (FAILED(rc)) return rc;
15323
15324 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15325 if (FAILED(rc)) return rc;
15326
15327 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15328
15329 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15330 {
15331 rc = addStorageController(strHDName,
15332 hdStorageBusType,
15333 hdController);
15334 if (FAILED(rc)) return rc;
15335
15336 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15337 if (FAILED(rc)) return rc;
15338 }
15339 else
15340 {
15341 /* The HD controller is the same as DVD: */
15342 hdController = dvdController;
15343 }
15344
15345 /* Limit the AHCI port count if it's used because windows has trouble with
15346 * too many ports and other guest (OS X in particular) may take extra long
15347 * boot: */
15348
15349 // pParent = static_cast<Medium*>(aP)
15350 IStorageController *temp = hdController;
15351 ComObjPtr<StorageController> storageController;
15352 storageController = static_cast<StorageController *>(temp);
15353
15354 // tempHDController = aHDController;
15355 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15356 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15357 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15358 storageController->COMSETTER(PortCount)(1);
15359
15360 /* USB stuff */
15361
15362 bool ohciEnabled = false;
15363
15364 ComPtr<IUSBController> usbController;
15365 BOOL recommendedUSB3;
15366 BOOL recommendedUSB;
15367 BOOL usbProxyAvailable;
15368
15369 getUSBProxyAvailable(&usbProxyAvailable);
15370 if (FAILED(rc)) return rc;
15371
15372 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15373 if (FAILED(rc)) return rc;
15374 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15375 if (FAILED(rc)) return rc;
15376
15377 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15378 {
15379#ifdef VBOX_WITH_EXTPACK
15380 /* USB 3.0 is only available if the proper ExtPack is installed. */
15381 ExtPackManager *aManager = mParent->i_getExtPackManager();
15382 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15383 {
15384 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15385 if (FAILED(rc)) return rc;
15386
15387 /* xHci includes OHCI */
15388 ohciEnabled = true;
15389 }
15390#endif
15391 }
15392 if ( !ohciEnabled
15393 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15394 {
15395 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15396 if (FAILED(rc)) return rc;
15397 ohciEnabled = true;
15398
15399#ifdef VBOX_WITH_EXTPACK
15400 /* USB 2.0 is only available if the proper ExtPack is installed.
15401 * Note. Configuring EHCI here and providing messages about
15402 * the missing extpack isn't exactly clean, but it is a
15403 * necessary evil to patch over legacy compatability issues
15404 * introduced by the new distribution model. */
15405 ExtPackManager *manager = mParent->i_getExtPackManager();
15406 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15407 {
15408 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15409 if (FAILED(rc)) return rc;
15410 }
15411#endif
15412 }
15413
15414 /* Set recommended human interface device types: */
15415 BOOL recommendedUSBHID;
15416 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15417 if (FAILED(rc)) return rc;
15418
15419 if (recommendedUSBHID)
15420 {
15421 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15422 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15423 if (!ohciEnabled && !usbDeviceFilters.isNull())
15424 {
15425 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15426 if (FAILED(rc)) return rc;
15427 }
15428 }
15429
15430 BOOL recommendedUSBTablet;
15431 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15432 if (FAILED(rc)) return rc;
15433
15434 if (recommendedUSBTablet)
15435 {
15436 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15437 if (!ohciEnabled && !usbDeviceFilters.isNull())
15438 {
15439 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15440 if (FAILED(rc)) return rc;
15441 }
15442 }
15443 return S_OK;
15444}
15445
15446/* This isn't handled entirely by the wrapper generator yet. */
15447#ifdef VBOX_WITH_XPCOM
15448NS_DECL_CLASSINFO(SessionMachine)
15449NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15450
15451NS_DECL_CLASSINFO(SnapshotMachine)
15452NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15453#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