VirtualBox

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

Last change on this file since 82901 was 81971, checked in by vboxsync, 5 years ago

Main/Machine+GraphicsAdapter: previously missed proper removal of settings which are now in a different object

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 539.8 KB
Line 
1/* $Id: MachineImpl.cpp 81971 2019-11-18 21:35:57Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52#ifdef VBOX_WITH_CLOUD_NET
53#include "ApplianceImpl.h"
54#include "CloudGateway.h"
55#endif /* VBOX_WITH_CLOUD_NET */
56
57// generated header
58#include "VBoxEvents.h"
59
60#ifdef VBOX_WITH_USB
61# include "USBProxyService.h"
62#endif
63
64#include "AutoCaller.h"
65#include "HashedPw.h"
66#include "Performance.h"
67
68#include <iprt/asm.h>
69#include <iprt/path.h>
70#include <iprt/dir.h>
71#include <iprt/env.h>
72#include <iprt/lockvalidator.h>
73#include <iprt/process.h>
74#include <iprt/cpp/utils.h>
75#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
76#include <iprt/sha.h>
77#include <iprt/string.h>
78#include <iprt/ctype.h>
79
80#include <VBox/com/array.h>
81#include <VBox/com/list.h>
82
83#include <VBox/err.h>
84#include <VBox/param.h>
85#include <VBox/settings.h>
86#include <VBox/VMMDev.h>
87#include <VBox/vmm/ssm.h>
88
89#ifdef VBOX_WITH_GUEST_PROPS
90# include <VBox/HostServices/GuestPropertySvc.h>
91# include <VBox/com/array.h>
92#endif
93
94#ifdef VBOX_WITH_SHARED_CLIPBOARD
95# include <VBox/HostServices/VBoxClipboardSvc.h>
96#endif
97
98#include "VBox/com/MultiResult.h"
99
100#include <algorithm>
101
102#ifdef VBOX_WITH_DTRACE_R3_MAIN
103# include "dtrace/VBoxAPI.h"
104#endif
105
106#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
107# define HOSTSUFF_EXE ".exe"
108#else /* !RT_OS_WINDOWS */
109# define HOSTSUFF_EXE ""
110#endif /* !RT_OS_WINDOWS */
111
112// defines / prototypes
113/////////////////////////////////////////////////////////////////////////////
114
115/////////////////////////////////////////////////////////////////////////////
116// Machine::Data structure
117/////////////////////////////////////////////////////////////////////////////
118
119Machine::Data::Data()
120{
121 mRegistered = FALSE;
122 pMachineConfigFile = NULL;
123 /* Contains hints on what has changed when the user is using the VM (config
124 * changes, running the VM, ...). This is used to decide if a config needs
125 * to be written to disk. */
126 flModifications = 0;
127 /* VM modification usually also trigger setting the current state to
128 * "Modified". Although this is not always the case. An e.g. is the VM
129 * initialization phase or when snapshot related data is changed. The
130 * actually behavior is controlled by the following flag. */
131 m_fAllowStateModification = false;
132 mAccessible = FALSE;
133 /* mUuid is initialized in Machine::init() */
134
135 mMachineState = MachineState_PoweredOff;
136 RTTimeNow(&mLastStateChange);
137
138 mMachineStateDeps = 0;
139 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
140 mMachineStateChangePending = 0;
141
142 mCurrentStateModified = TRUE;
143 mGuestPropertiesModified = FALSE;
144
145 mSession.mPID = NIL_RTPROCESS;
146 mSession.mLockType = LockType_Null;
147 mSession.mState = SessionState_Unlocked;
148}
149
150Machine::Data::~Data()
151{
152 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
153 {
154 RTSemEventMultiDestroy(mMachineStateDepsSem);
155 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
156 }
157 if (pMachineConfigFile)
158 {
159 delete pMachineConfigFile;
160 pMachineConfigFile = NULL;
161 }
162}
163
164/////////////////////////////////////////////////////////////////////////////
165// Machine::HWData structure
166/////////////////////////////////////////////////////////////////////////////
167
168Machine::HWData::HWData()
169{
170 /* default values for a newly created machine */
171 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
172 mMemorySize = 128;
173 mCPUCount = 1;
174 mCPUHotPlugEnabled = false;
175 mMemoryBalloonSize = 0;
176 mPageFusionEnabled = false;
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188 mHWVirtExUseNativeApi = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mAPIC = true;
197 mX2APIC = false;
198 mIBPBOnVMExit = false;
199 mIBPBOnVMEntry = false;
200 mSpecCtrl = false;
201 mSpecCtrlByHost = false;
202 mL1DFlushOnSched = true;
203 mL1DFlushOnVMEntry = false;
204 mMDSClearOnSched = true;
205 mMDSClearOnVMEntry = false;
206 mNestedHWVirt = false;
207 mHPETEnabled = false;
208 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
209 mCpuIdPortabilityLevel = 0;
210 mCpuProfile = "host";
211
212 /* default boot order: floppy - DVD - HDD */
213 mBootOrder[0] = DeviceType_Floppy;
214 mBootOrder[1] = DeviceType_DVD;
215 mBootOrder[2] = DeviceType_HardDisk;
216 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
217 mBootOrder[i] = DeviceType_Null;
218
219 mClipboardMode = ClipboardMode_Disabled;
220 mClipboardFileTransfersEnabled = FALSE;
221
222 mDnDMode = DnDMode_Disabled;
223
224 mFirmwareType = FirmwareType_BIOS;
225 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
226 mPointingHIDType = PointingHIDType_PS2Mouse;
227 mChipsetType = ChipsetType_PIIX3;
228 mParavirtProvider = ParavirtProvider_Default;
229 mEmulatedUSBCardReaderEnabled = FALSE;
230
231 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
232 mCPUAttached[i] = false;
233
234 mIOCacheEnabled = true;
235 mIOCacheSize = 5; /* 5MB */
236}
237
238Machine::HWData::~HWData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine() :
250#ifdef VBOX_WITH_RESOURCE_USAGE_API
251 mCollectorGuest(NULL),
252#endif
253 mPeer(NULL),
254 mParent(NULL),
255 mSerialPorts(),
256 mParallelPorts(),
257 uRegistryNeedsSaving(0)
258{}
259
260Machine::~Machine()
261{}
262
263HRESULT Machine::FinalConstruct()
264{
265 LogFlowThisFunc(("\n"));
266 return BaseFinalConstruct();
267}
268
269void Machine::FinalRelease()
270{
271 LogFlowThisFunc(("\n"));
272 uninit();
273 BaseFinalRelease();
274}
275
276/**
277 * Initializes a new machine instance; this init() variant creates a new, empty machine.
278 * This gets called from VirtualBox::CreateMachine().
279 *
280 * @param aParent Associated parent object
281 * @param strConfigFile Local file system path to the VM settings file (can
282 * be relative to the VirtualBox config directory).
283 * @param strName name for the machine
284 * @param llGroups list of groups for the machine
285 * @param strOsType OS Type string (stored as is if aOsType is NULL).
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
290 * scheme (includes the UUID).
291 *
292 * @return Success indicator. if not S_OK, the machine object is invalid
293 */
294HRESULT Machine::init(VirtualBox *aParent,
295 const Utf8Str &strConfigFile,
296 const Utf8Str &strName,
297 const StringsList &llGroups,
298 const Utf8Str &strOsType,
299 GuestOSType *aOsType,
300 const Guid &aId,
301 bool fForceOverwrite,
302 bool fDirectoryIncludesUUID)
303{
304 LogFlowThisFuncEnter();
305 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
306
307 /* Enclose the state transition NotReady->InInit->Ready */
308 AutoInitSpan autoInitSpan(this);
309 AssertReturn(autoInitSpan.isOk(), E_FAIL);
310
311 HRESULT rc = initImpl(aParent, strConfigFile);
312 if (FAILED(rc)) return rc;
313
314 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
315 if (FAILED(rc)) return rc;
316
317 if (SUCCEEDED(rc))
318 {
319 // create an empty machine config
320 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
321
322 rc = initDataAndChildObjects();
323 }
324
325 if (SUCCEEDED(rc))
326 {
327 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
328 mData->mAccessible = TRUE;
329
330 unconst(mData->mUuid) = aId;
331
332 mUserData->s.strName = strName;
333
334 if (llGroups.size())
335 mUserData->s.llGroups = llGroups;
336
337 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
338 // the "name sync" flag determines whether the machine directory gets renamed along
339 // with the machine file; say so if the settings file name is the same as the
340 // settings file parent directory (machine directory)
341 mUserData->s.fNameSync = i_isInOwnDir();
342
343 // initialize the default snapshots folder
344 rc = COMSETTER(SnapshotFolder)(NULL);
345 AssertComRC(rc);
346
347 if (aOsType)
348 {
349 /* Store OS type */
350 mUserData->s.strOsType = aOsType->i_id();
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->i_is64Bit()
354 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
355
356 /* Let the OS type enable the X2APIC */
357 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
358 }
359 else if (!strOsType.isEmpty())
360 {
361 /* Store OS type */
362 mUserData->s.strOsType = strOsType;
363
364 /* No guest OS type object. Pick some plausible defaults which the
365 * host can handle. There's no way to know or validate anything. */
366 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
367 mHWData->mX2APIC = false;
368 }
369
370 /* Apply BIOS defaults. */
371 mBIOSSettings->i_applyDefaults(aOsType);
372
373 /* Apply record defaults. */
374 mRecordingSettings->i_applyDefaults();
375
376 /* Apply network adapters defaults */
377 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
378 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
379
380 /* Apply serial port defaults */
381 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
382 mSerialPorts[slot]->i_applyDefaults(aOsType);
383
384 /* Apply parallel port defaults */
385 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
386 mParallelPorts[slot]->i_applyDefaults();
387
388 /* At this point the changing of the current state modification
389 * flag is allowed. */
390 i_allowStateModification();
391
392 /* commit all changes made during the initialization */
393 i_commit();
394 }
395
396 /* Confirm a successful initialization when it's the case */
397 if (SUCCEEDED(rc))
398 {
399 if (mData->mAccessible)
400 autoInitSpan.setSucceeded();
401 else
402 autoInitSpan.setLimited();
403 }
404
405 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
406 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
407 mData->mRegistered,
408 mData->mAccessible,
409 rc));
410
411 LogFlowThisFuncLeave();
412
413 return rc;
414}
415
416/**
417 * Initializes a new instance with data from machine XML (formerly Init_Registered).
418 * Gets called in two modes:
419 *
420 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
421 * UUID is specified and we mark the machine as "registered";
422 *
423 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
424 * and the machine remains unregistered until RegisterMachine() is called.
425 *
426 * @param aParent Associated parent object
427 * @param strConfigFile Local file system path to the VM settings file (can
428 * be relative to the VirtualBox config directory).
429 * @param aId UUID of the machine or NULL (see above).
430 *
431 * @return Success indicator. if not S_OK, the machine object is invalid
432 */
433HRESULT Machine::initFromSettings(VirtualBox *aParent,
434 const Utf8Str &strConfigFile,
435 const Guid *aId)
436{
437 LogFlowThisFuncEnter();
438 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
439
440 /* Enclose the state transition NotReady->InInit->Ready */
441 AutoInitSpan autoInitSpan(this);
442 AssertReturn(autoInitSpan.isOk(), E_FAIL);
443
444 HRESULT rc = initImpl(aParent, strConfigFile);
445 if (FAILED(rc)) return rc;
446
447 if (aId)
448 {
449 // loading a registered VM:
450 unconst(mData->mUuid) = *aId;
451 mData->mRegistered = TRUE;
452 // now load the settings from XML:
453 rc = i_registeredInit();
454 // this calls initDataAndChildObjects() and loadSettings()
455 }
456 else
457 {
458 // opening an unregistered VM (VirtualBox::OpenMachine()):
459 rc = initDataAndChildObjects();
460
461 if (SUCCEEDED(rc))
462 {
463 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
464 mData->mAccessible = TRUE;
465
466 try
467 {
468 // load and parse machine XML; this will throw on XML or logic errors
469 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
470
471 // reject VM UUID duplicates, they can happen if someone
472 // tries to register an already known VM config again
473 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
474 true /* fPermitInaccessible */,
475 false /* aDoSetError */,
476 NULL) != VBOX_E_OBJECT_NOT_FOUND)
477 {
478 throw setError(E_FAIL,
479 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
480 mData->m_strConfigFile.c_str());
481 }
482
483 // use UUID from machine config
484 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
485
486 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
487 NULL /* puuidRegistry */);
488 if (FAILED(rc)) throw rc;
489
490 /* At this point the changing of the current state modification
491 * flag is allowed. */
492 i_allowStateModification();
493
494 i_commit();
495 }
496 catch (HRESULT err)
497 {
498 /* we assume that error info is set by the thrower */
499 rc = err;
500 }
501 catch (...)
502 {
503 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
504 }
505 }
506 }
507
508 /* Confirm a successful initialization when it's the case */
509 if (SUCCEEDED(rc))
510 {
511 if (mData->mAccessible)
512 autoInitSpan.setSucceeded();
513 else
514 {
515 autoInitSpan.setLimited();
516
517 // uninit media from this machine's media registry, or else
518 // reloading the settings will fail
519 mParent->i_unregisterMachineMedia(i_getId());
520 }
521 }
522
523 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
524 "rc=%08X\n",
525 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
526 mData->mRegistered, mData->mAccessible, rc));
527
528 LogFlowThisFuncLeave();
529
530 return rc;
531}
532
533/**
534 * Initializes a new instance from a machine config that is already in memory
535 * (import OVF case). Since we are importing, the UUID in the machine
536 * config is ignored and we always generate a fresh one.
537 *
538 * @param aParent Associated parent object.
539 * @param strName Name for the new machine; this overrides what is specified in config.
540 * @param strSettingsFilename File name of .vbox file.
541 * @param config Machine configuration loaded and parsed from XML.
542 *
543 * @return Success indicator. if not S_OK, the machine object is invalid
544 */
545HRESULT Machine::init(VirtualBox *aParent,
546 const Utf8Str &strName,
547 const Utf8Str &strSettingsFilename,
548 const settings::MachineConfigFile &config)
549{
550 LogFlowThisFuncEnter();
551
552 /* Enclose the state transition NotReady->InInit->Ready */
553 AutoInitSpan autoInitSpan(this);
554 AssertReturn(autoInitSpan.isOk(), E_FAIL);
555
556 HRESULT rc = initImpl(aParent, strSettingsFilename);
557 if (FAILED(rc)) return rc;
558
559 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
560 if (FAILED(rc)) return rc;
561
562 rc = initDataAndChildObjects();
563
564 if (SUCCEEDED(rc))
565 {
566 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
567 mData->mAccessible = TRUE;
568
569 // create empty machine config for instance data
570 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
571
572 // generate fresh UUID, ignore machine config
573 unconst(mData->mUuid).create();
574
575 rc = i_loadMachineDataFromSettings(config,
576 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
577
578 // override VM name as well, it may be different
579 mUserData->s.strName = strName;
580
581 if (SUCCEEDED(rc))
582 {
583 /* At this point the changing of the current state modification
584 * flag is allowed. */
585 i_allowStateModification();
586
587 /* commit all changes made during the initialization */
588 i_commit();
589 }
590 }
591
592 /* Confirm a successful initialization when it's the case */
593 if (SUCCEEDED(rc))
594 {
595 if (mData->mAccessible)
596 autoInitSpan.setSucceeded();
597 else
598 {
599 /* Ignore all errors from unregistering, they would destroy
600- * the more interesting error information we already have,
601- * pinpointing the issue with the VM config. */
602 ErrorInfoKeeper eik;
603
604 autoInitSpan.setLimited();
605
606 // uninit media from this machine's media registry, or else
607 // reloading the settings will fail
608 mParent->i_unregisterMachineMedia(i_getId());
609 }
610 }
611
612 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
613 "rc=%08X\n",
614 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
615 mData->mRegistered, mData->mAccessible, rc));
616
617 LogFlowThisFuncLeave();
618
619 return rc;
620}
621
622/**
623 * Shared code between the various init() implementations.
624 * @param aParent The VirtualBox object.
625 * @param strConfigFile Settings file.
626 * @return
627 */
628HRESULT Machine::initImpl(VirtualBox *aParent,
629 const Utf8Str &strConfigFile)
630{
631 LogFlowThisFuncEnter();
632
633 AssertReturn(aParent, E_INVALIDARG);
634 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
635
636 HRESULT rc = S_OK;
637
638 /* share the parent weakly */
639 unconst(mParent) = aParent;
640
641 /* allocate the essential machine data structure (the rest will be
642 * allocated later by initDataAndChildObjects() */
643 mData.allocate();
644
645 /* memorize the config file name (as provided) */
646 mData->m_strConfigFile = strConfigFile;
647
648 /* get the full file name */
649 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
650 if (RT_FAILURE(vrc1))
651 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
652 tr("Invalid machine settings file name '%s' (%Rrc)"),
653 strConfigFile.c_str(),
654 vrc1);
655
656 LogFlowThisFuncLeave();
657
658 return rc;
659}
660
661/**
662 * Tries to create a machine settings file in the path stored in the machine
663 * instance data. Used when a new machine is created to fail gracefully if
664 * the settings file could not be written (e.g. because machine dir is read-only).
665 * @return
666 */
667HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
668{
669 HRESULT rc = S_OK;
670
671 // when we create a new machine, we must be able to create the settings file
672 RTFILE f = NIL_RTFILE;
673 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
674 if ( RT_SUCCESS(vrc)
675 || vrc == VERR_SHARING_VIOLATION
676 )
677 {
678 if (RT_SUCCESS(vrc))
679 RTFileClose(f);
680 if (!fForceOverwrite)
681 rc = setError(VBOX_E_FILE_ERROR,
682 tr("Machine settings file '%s' already exists"),
683 mData->m_strConfigFileFull.c_str());
684 else
685 {
686 /* try to delete the config file, as otherwise the creation
687 * of a new settings file will fail. */
688 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
689 if (RT_FAILURE(vrc2))
690 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
691 tr("Could not delete the existing settings file '%s' (%Rrc)"),
692 mData->m_strConfigFileFull.c_str(), vrc2);
693 }
694 }
695 else if ( vrc != VERR_FILE_NOT_FOUND
696 && vrc != VERR_PATH_NOT_FOUND
697 )
698 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
699 tr("Invalid machine settings file name '%s' (%Rrc)"),
700 mData->m_strConfigFileFull.c_str(),
701 vrc);
702 return rc;
703}
704
705/**
706 * Initializes the registered machine by loading the settings file.
707 * This method is separated from #init() in order to make it possible to
708 * retry the operation after VirtualBox startup instead of refusing to
709 * startup the whole VirtualBox server in case if the settings file of some
710 * registered VM is invalid or inaccessible.
711 *
712 * @note Must be always called from this object's write lock
713 * (unless called from #init() that doesn't need any locking).
714 * @note Locks the mUSBController method for writing.
715 * @note Subclasses must not call this method.
716 */
717HRESULT Machine::i_registeredInit()
718{
719 AssertReturn(!i_isSessionMachine(), E_FAIL);
720 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
721 AssertReturn(mData->mUuid.isValid(), E_FAIL);
722 AssertReturn(!mData->mAccessible, E_FAIL);
723
724 HRESULT rc = initDataAndChildObjects();
725
726 if (SUCCEEDED(rc))
727 {
728 /* Temporarily reset the registered flag in order to let setters
729 * potentially called from loadSettings() succeed (isMutable() used in
730 * all setters will return FALSE for a Machine instance if mRegistered
731 * is TRUE). */
732 mData->mRegistered = FALSE;
733
734 try
735 {
736 // load and parse machine XML; this will throw on XML or logic errors
737 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
738
739 if (mData->mUuid != mData->pMachineConfigFile->uuid)
740 throw setError(E_FAIL,
741 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
742 mData->pMachineConfigFile->uuid.raw(),
743 mData->m_strConfigFileFull.c_str(),
744 mData->mUuid.toString().c_str(),
745 mParent->i_settingsFilePath().c_str());
746
747 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
748 NULL /* const Guid *puuidRegistry */);
749 if (FAILED(rc)) throw rc;
750 }
751 catch (HRESULT err)
752 {
753 /* we assume that error info is set by the thrower */
754 rc = err;
755 }
756 catch (...)
757 {
758 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
759 }
760
761 /* Restore the registered flag (even on failure) */
762 mData->mRegistered = TRUE;
763 }
764
765 if (SUCCEEDED(rc))
766 {
767 /* Set mAccessible to TRUE only if we successfully locked and loaded
768 * the settings file */
769 mData->mAccessible = TRUE;
770
771 /* commit all changes made during loading the settings file */
772 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
773 /// @todo r=klaus for some reason the settings loading logic backs up
774 // the settings, and therefore a commit is needed. Should probably be changed.
775 }
776 else
777 {
778 /* If the machine is registered, then, instead of returning a
779 * failure, we mark it as inaccessible and set the result to
780 * success to give it a try later */
781
782 /* fetch the current error info */
783 mData->mAccessError = com::ErrorInfo();
784 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
785
786 /* rollback all changes */
787 i_rollback(false /* aNotify */);
788
789 // uninit media from this machine's media registry, or else
790 // reloading the settings will fail
791 mParent->i_unregisterMachineMedia(i_getId());
792
793 /* uninitialize the common part to make sure all data is reset to
794 * default (null) values */
795 uninitDataAndChildObjects();
796
797 rc = S_OK;
798 }
799
800 return rc;
801}
802
803/**
804 * Uninitializes the instance.
805 * Called either from FinalRelease() or by the parent when it gets destroyed.
806 *
807 * @note The caller of this method must make sure that this object
808 * a) doesn't have active callers on the current thread and b) is not locked
809 * by the current thread; otherwise uninit() will hang either a) due to
810 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
811 * a dead-lock caused by this thread waiting for all callers on the other
812 * threads are done but preventing them from doing so by holding a lock.
813 */
814void Machine::uninit()
815{
816 LogFlowThisFuncEnter();
817
818 Assert(!isWriteLockOnCurrentThread());
819
820 Assert(!uRegistryNeedsSaving);
821 if (uRegistryNeedsSaving)
822 {
823 AutoCaller autoCaller(this);
824 if (SUCCEEDED(autoCaller.rc()))
825 {
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827 i_saveSettings(NULL, Machine::SaveS_Force);
828 }
829 }
830
831 /* Enclose the state transition Ready->InUninit->NotReady */
832 AutoUninitSpan autoUninitSpan(this);
833 if (autoUninitSpan.uninitDone())
834 return;
835
836 Assert(!i_isSnapshotMachine());
837 Assert(!i_isSessionMachine());
838 Assert(!!mData);
839
840 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
841 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
842
843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
844
845 if (!mData->mSession.mMachine.isNull())
846 {
847 /* Theoretically, this can only happen if the VirtualBox server has been
848 * terminated while there were clients running that owned open direct
849 * sessions. Since in this case we are definitely called by
850 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
851 * won't happen on the client watcher thread (because it has a
852 * VirtualBox caller for the duration of the
853 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
854 * cannot happen until the VirtualBox caller is released). This is
855 * important, because SessionMachine::uninit() cannot correctly operate
856 * after we return from this method (it expects the Machine instance is
857 * still valid). We'll call it ourselves below.
858 */
859 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
860 (SessionMachine*)mData->mSession.mMachine));
861
862 if (Global::IsOnlineOrTransient(mData->mMachineState))
863 {
864 Log1WarningThisFunc(("Setting state to Aborted!\n"));
865 /* set machine state using SessionMachine reimplementation */
866 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
867 }
868
869 /*
870 * Uninitialize SessionMachine using public uninit() to indicate
871 * an unexpected uninitialization.
872 */
873 mData->mSession.mMachine->uninit();
874 /* SessionMachine::uninit() must set mSession.mMachine to null */
875 Assert(mData->mSession.mMachine.isNull());
876 }
877
878 // uninit media from this machine's media registry, if they're still there
879 Guid uuidMachine(i_getId());
880
881 /* the lock is no more necessary (SessionMachine is uninitialized) */
882 alock.release();
883
884 /* XXX This will fail with
885 * "cannot be closed because it is still attached to 1 virtual machines"
886 * because at this point we did not call uninitDataAndChildObjects() yet
887 * and therefore also removeBackReference() for all these mediums was not called! */
888
889 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
890 mParent->i_unregisterMachineMedia(uuidMachine);
891
892 // has machine been modified?
893 if (mData->flModifications)
894 {
895 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
896 i_rollback(false /* aNotify */);
897 }
898
899 if (mData->mAccessible)
900 uninitDataAndChildObjects();
901
902 /* free the essential data structure last */
903 mData.free();
904
905 LogFlowThisFuncLeave();
906}
907
908// Wrapped IMachine properties
909/////////////////////////////////////////////////////////////////////////////
910HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
911{
912 /* mParent is constant during life time, no need to lock */
913 ComObjPtr<VirtualBox> pVirtualBox(mParent);
914 aParent = pVirtualBox;
915
916 return S_OK;
917}
918
919
920HRESULT Machine::getAccessible(BOOL *aAccessible)
921{
922 /* In some cases (medium registry related), it is necessary to be able to
923 * go through the list of all machines. Happens when an inaccessible VM
924 * has a sensible medium registry. */
925 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
927
928 HRESULT rc = S_OK;
929
930 if (!mData->mAccessible)
931 {
932 /* try to initialize the VM once more if not accessible */
933
934 AutoReinitSpan autoReinitSpan(this);
935 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
936
937#ifdef DEBUG
938 LogFlowThisFunc(("Dumping media backreferences\n"));
939 mParent->i_dumpAllBackRefs();
940#endif
941
942 if (mData->pMachineConfigFile)
943 {
944 // reset the XML file to force loadSettings() (called from i_registeredInit())
945 // to parse it again; the file might have changed
946 delete mData->pMachineConfigFile;
947 mData->pMachineConfigFile = NULL;
948 }
949
950 rc = i_registeredInit();
951
952 if (SUCCEEDED(rc) && mData->mAccessible)
953 {
954 autoReinitSpan.setSucceeded();
955
956 /* make sure interesting parties will notice the accessibility
957 * state change */
958 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
959 mParent->i_onMachineDataChange(mData->mUuid);
960 }
961 }
962
963 if (SUCCEEDED(rc))
964 *aAccessible = mData->mAccessible;
965
966 LogFlowThisFuncLeave();
967
968 return rc;
969}
970
971HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
972{
973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
974
975 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
976 {
977 /* return shortly */
978 aAccessError = NULL;
979 return S_OK;
980 }
981
982 HRESULT rc = S_OK;
983
984 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
985 rc = errorInfo.createObject();
986 if (SUCCEEDED(rc))
987 {
988 errorInfo->init(mData->mAccessError.getResultCode(),
989 mData->mAccessError.getInterfaceID().ref(),
990 Utf8Str(mData->mAccessError.getComponent()).c_str(),
991 Utf8Str(mData->mAccessError.getText()));
992 aAccessError = errorInfo;
993 }
994
995 return rc;
996}
997
998HRESULT Machine::getName(com::Utf8Str &aName)
999{
1000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 aName = mUserData->s.strName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::setName(const com::Utf8Str &aName)
1008{
1009 // prohibit setting a UUID only as the machine name, or else it can
1010 // never be found by findMachine()
1011 Guid test(aName);
1012
1013 if (test.isValid())
1014 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1015
1016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 HRESULT rc = i_checkStateDependency(MutableStateDep);
1019 if (FAILED(rc)) return rc;
1020
1021 i_setModified(IsModified_MachineData);
1022 mUserData.backup();
1023 mUserData->s.strName = aName;
1024
1025 return S_OK;
1026}
1027
1028HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1029{
1030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1031
1032 aDescription = mUserData->s.strDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1038{
1039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 // this can be done in principle in any state as it doesn't affect the VM
1042 // significantly, but play safe by not messing around while complex
1043 // activities are going on
1044 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1045 if (FAILED(rc)) return rc;
1046
1047 i_setModified(IsModified_MachineData);
1048 mUserData.backup();
1049 mUserData->s.strDescription = aDescription;
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::getId(com::Guid &aId)
1055{
1056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1057
1058 aId = mData->mUuid;
1059
1060 return S_OK;
1061}
1062
1063HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1064{
1065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1066 aGroups.resize(mUserData->s.llGroups.size());
1067 size_t i = 0;
1068 for (StringsList::const_iterator
1069 it = mUserData->s.llGroups.begin();
1070 it != mUserData->s.llGroups.end();
1071 ++it, ++i)
1072 aGroups[i] = (*it);
1073
1074 return S_OK;
1075}
1076
1077HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1078{
1079 StringsList llGroups;
1080 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1081 if (FAILED(rc))
1082 return rc;
1083
1084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1085
1086 rc = i_checkStateDependency(MutableOrSavedStateDep);
1087 if (FAILED(rc)) return rc;
1088
1089 i_setModified(IsModified_MachineData);
1090 mUserData.backup();
1091 mUserData->s.llGroups = llGroups;
1092
1093 return S_OK;
1094}
1095
1096HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1097{
1098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1099
1100 aOSTypeId = mUserData->s.strOsType;
1101
1102 return S_OK;
1103}
1104
1105HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1106{
1107 /* look up the object by Id to check it is valid */
1108 ComObjPtr<GuestOSType> pGuestOSType;
1109 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1110
1111 /* when setting, always use the "etalon" value for consistency -- lookup
1112 * by ID is case-insensitive and the input value may have different case */
1113 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1114
1115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1116
1117 HRESULT rc = i_checkStateDependency(MutableStateDep);
1118 if (FAILED(rc)) return rc;
1119
1120 i_setModified(IsModified_MachineData);
1121 mUserData.backup();
1122 mUserData->s.strOsType = osTypeId;
1123
1124 return S_OK;
1125}
1126
1127HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1128{
1129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1130
1131 *aFirmwareType = mHWData->mFirmwareType;
1132
1133 return S_OK;
1134}
1135
1136HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1137{
1138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1139
1140 HRESULT rc = i_checkStateDependency(MutableStateDep);
1141 if (FAILED(rc)) return rc;
1142
1143 i_setModified(IsModified_MachineData);
1144 mHWData.backup();
1145 mHWData->mFirmwareType = aFirmwareType;
1146 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1147 alock.release();
1148
1149 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aPointingHIDType = mHWData->mPointingHIDType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 i_setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mPointingHIDType = aPointingHIDType;
1196
1197 return S_OK;
1198}
1199
1200HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1201{
1202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1203
1204 *aChipsetType = mHWData->mChipsetType;
1205
1206 return S_OK;
1207}
1208
1209HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1210{
1211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1212
1213 HRESULT rc = i_checkStateDependency(MutableStateDep);
1214 if (FAILED(rc)) return rc;
1215
1216 if (aChipsetType != mHWData->mChipsetType)
1217 {
1218 i_setModified(IsModified_MachineData);
1219 mHWData.backup();
1220 mHWData->mChipsetType = aChipsetType;
1221
1222 // Resize network adapter array, to be finalized on commit/rollback.
1223 // We must not throw away entries yet, otherwise settings are lost
1224 // without a way to roll back.
1225 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1226 size_t oldCount = mNetworkAdapters.size();
1227 if (newCount > oldCount)
1228 {
1229 mNetworkAdapters.resize(newCount);
1230 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1231 {
1232 unconst(mNetworkAdapters[slot]).createObject();
1233 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1234 }
1235 }
1236 }
1237
1238 return S_OK;
1239}
1240
1241HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1242{
1243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1244
1245 aParavirtDebug = mHWData->mParavirtDebug;
1246 return S_OK;
1247}
1248
1249HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1250{
1251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1252
1253 HRESULT rc = i_checkStateDependency(MutableStateDep);
1254 if (FAILED(rc)) return rc;
1255
1256 /** @todo Parse/validate options? */
1257 if (aParavirtDebug != mHWData->mParavirtDebug)
1258 {
1259 i_setModified(IsModified_MachineData);
1260 mHWData.backup();
1261 mHWData->mParavirtDebug = aParavirtDebug;
1262 }
1263
1264 return S_OK;
1265}
1266
1267HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1268{
1269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1270
1271 *aParavirtProvider = mHWData->mParavirtProvider;
1272
1273 return S_OK;
1274}
1275
1276HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1277{
1278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 HRESULT rc = i_checkStateDependency(MutableStateDep);
1281 if (FAILED(rc)) return rc;
1282
1283 if (aParavirtProvider != mHWData->mParavirtProvider)
1284 {
1285 i_setModified(IsModified_MachineData);
1286 mHWData.backup();
1287 mHWData->mParavirtProvider = aParavirtProvider;
1288 }
1289
1290 return S_OK;
1291}
1292
1293HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1294{
1295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1296
1297 *aParavirtProvider = mHWData->mParavirtProvider;
1298 switch (mHWData->mParavirtProvider)
1299 {
1300 case ParavirtProvider_None:
1301 case ParavirtProvider_HyperV:
1302 case ParavirtProvider_KVM:
1303 case ParavirtProvider_Minimal:
1304 break;
1305
1306 /* Resolve dynamic provider types to the effective types. */
1307 default:
1308 {
1309 ComObjPtr<GuestOSType> pGuestOSType;
1310 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1311 pGuestOSType);
1312 if (FAILED(hrc2) || pGuestOSType.isNull())
1313 {
1314 *aParavirtProvider = ParavirtProvider_None;
1315 break;
1316 }
1317
1318 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1319 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1320
1321 switch (mHWData->mParavirtProvider)
1322 {
1323 case ParavirtProvider_Legacy:
1324 {
1325 if (fOsXGuest)
1326 *aParavirtProvider = ParavirtProvider_Minimal;
1327 else
1328 *aParavirtProvider = ParavirtProvider_None;
1329 break;
1330 }
1331
1332 case ParavirtProvider_Default:
1333 {
1334 if (fOsXGuest)
1335 *aParavirtProvider = ParavirtProvider_Minimal;
1336 else if ( mUserData->s.strOsType == "Windows10"
1337 || mUserData->s.strOsType == "Windows10_64"
1338 || mUserData->s.strOsType == "Windows81"
1339 || mUserData->s.strOsType == "Windows81_64"
1340 || mUserData->s.strOsType == "Windows8"
1341 || mUserData->s.strOsType == "Windows8_64"
1342 || mUserData->s.strOsType == "Windows7"
1343 || mUserData->s.strOsType == "Windows7_64"
1344 || mUserData->s.strOsType == "WindowsVista"
1345 || mUserData->s.strOsType == "WindowsVista_64"
1346 || mUserData->s.strOsType == "Windows2012"
1347 || mUserData->s.strOsType == "Windows2012_64"
1348 || mUserData->s.strOsType == "Windows2008"
1349 || mUserData->s.strOsType == "Windows2008_64")
1350 {
1351 *aParavirtProvider = ParavirtProvider_HyperV;
1352 }
1353 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1354 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1355 || mUserData->s.strOsType == "Linux"
1356 || mUserData->s.strOsType == "Linux_64"
1357 || mUserData->s.strOsType == "ArchLinux"
1358 || mUserData->s.strOsType == "ArchLinux_64"
1359 || mUserData->s.strOsType == "Debian"
1360 || mUserData->s.strOsType == "Debian_64"
1361 || mUserData->s.strOsType == "Fedora"
1362 || mUserData->s.strOsType == "Fedora_64"
1363 || mUserData->s.strOsType == "Gentoo"
1364 || mUserData->s.strOsType == "Gentoo_64"
1365 || mUserData->s.strOsType == "Mandriva"
1366 || mUserData->s.strOsType == "Mandriva_64"
1367 || mUserData->s.strOsType == "OpenSUSE"
1368 || mUserData->s.strOsType == "OpenSUSE_64"
1369 || mUserData->s.strOsType == "Oracle"
1370 || mUserData->s.strOsType == "Oracle_64"
1371 || mUserData->s.strOsType == "RedHat"
1372 || mUserData->s.strOsType == "RedHat_64"
1373 || mUserData->s.strOsType == "Turbolinux"
1374 || mUserData->s.strOsType == "Turbolinux_64"
1375 || mUserData->s.strOsType == "Ubuntu"
1376 || mUserData->s.strOsType == "Ubuntu_64"
1377 || mUserData->s.strOsType == "Xandros"
1378 || mUserData->s.strOsType == "Xandros_64")
1379 {
1380 *aParavirtProvider = ParavirtProvider_KVM;
1381 }
1382 else
1383 *aParavirtProvider = ParavirtProvider_None;
1384 break;
1385 }
1386
1387 default: AssertFailedBreak(); /* Shut up MSC. */
1388 }
1389 break;
1390 }
1391 }
1392
1393 Assert( *aParavirtProvider == ParavirtProvider_None
1394 || *aParavirtProvider == ParavirtProvider_Minimal
1395 || *aParavirtProvider == ParavirtProvider_HyperV
1396 || *aParavirtProvider == ParavirtProvider_KVM);
1397 return S_OK;
1398}
1399
1400HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1401{
1402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1403
1404 aHardwareVersion = mHWData->mHWVersion;
1405
1406 return S_OK;
1407}
1408
1409HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1410{
1411 /* check known version */
1412 Utf8Str hwVersion = aHardwareVersion;
1413 if ( hwVersion.compare("1") != 0
1414 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1415 return setError(E_INVALIDARG,
1416 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1417
1418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 HRESULT rc = i_checkStateDependency(MutableStateDep);
1421 if (FAILED(rc)) return rc;
1422
1423 i_setModified(IsModified_MachineData);
1424 mHWData.backup();
1425 mHWData->mHWVersion = aHardwareVersion;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1431{
1432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 if (!mHWData->mHardwareUUID.isZero())
1435 aHardwareUUID = mHWData->mHardwareUUID;
1436 else
1437 aHardwareUUID = mData->mUuid;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1443{
1444 if (!aHardwareUUID.isValid())
1445 return E_INVALIDARG;
1446
1447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 HRESULT rc = i_checkStateDependency(MutableStateDep);
1450 if (FAILED(rc)) return rc;
1451
1452 i_setModified(IsModified_MachineData);
1453 mHWData.backup();
1454 if (aHardwareUUID == mData->mUuid)
1455 mHWData->mHardwareUUID.clear();
1456 else
1457 mHWData->mHardwareUUID = aHardwareUUID;
1458
1459 return S_OK;
1460}
1461
1462HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1463{
1464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1465
1466 *aMemorySize = mHWData->mMemorySize;
1467
1468 return S_OK;
1469}
1470
1471HRESULT Machine::setMemorySize(ULONG aMemorySize)
1472{
1473 /* check RAM limits */
1474 if ( aMemorySize < MM_RAM_MIN_IN_MB
1475 || aMemorySize > MM_RAM_MAX_IN_MB
1476 )
1477 return setError(E_INVALIDARG,
1478 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1479 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1480
1481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1482
1483 HRESULT rc = i_checkStateDependency(MutableStateDep);
1484 if (FAILED(rc)) return rc;
1485
1486 i_setModified(IsModified_MachineData);
1487 mHWData.backup();
1488 mHWData->mMemorySize = aMemorySize;
1489
1490 return S_OK;
1491}
1492
1493HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1494{
1495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
1497 *aCPUCount = mHWData->mCPUCount;
1498
1499 return S_OK;
1500}
1501
1502HRESULT Machine::setCPUCount(ULONG aCPUCount)
1503{
1504 /* check CPU limits */
1505 if ( aCPUCount < SchemaDefs::MinCPUCount
1506 || aCPUCount > SchemaDefs::MaxCPUCount
1507 )
1508 return setError(E_INVALIDARG,
1509 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1510 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1511
1512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1513
1514 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1515 if (mHWData->mCPUHotPlugEnabled)
1516 {
1517 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1518 {
1519 if (mHWData->mCPUAttached[idx])
1520 return setError(E_INVALIDARG,
1521 tr("There is still a CPU attached to socket %lu."
1522 "Detach the CPU before removing the socket"),
1523 aCPUCount, idx+1);
1524 }
1525 }
1526
1527 HRESULT rc = i_checkStateDependency(MutableStateDep);
1528 if (FAILED(rc)) return rc;
1529
1530 i_setModified(IsModified_MachineData);
1531 mHWData.backup();
1532 mHWData->mCPUCount = aCPUCount;
1533
1534 return S_OK;
1535}
1536
1537HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1538{
1539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1540
1541 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1542
1543 return S_OK;
1544}
1545
1546HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1547{
1548 HRESULT rc = S_OK;
1549
1550 /* check throttle limits */
1551 if ( aCPUExecutionCap < 1
1552 || aCPUExecutionCap > 100
1553 )
1554 return setError(E_INVALIDARG,
1555 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1556 aCPUExecutionCap, 1, 100);
1557
1558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
1560 alock.release();
1561 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1562 alock.acquire();
1563 if (FAILED(rc)) return rc;
1564
1565 i_setModified(IsModified_MachineData);
1566 mHWData.backup();
1567 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1568
1569 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1570 if (Global::IsOnline(mData->mMachineState))
1571 i_saveSettings(NULL);
1572
1573 return S_OK;
1574}
1575
1576HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1577{
1578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1579
1580 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1581
1582 return S_OK;
1583}
1584
1585HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1586{
1587 HRESULT rc = S_OK;
1588
1589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1590
1591 rc = i_checkStateDependency(MutableStateDep);
1592 if (FAILED(rc)) return rc;
1593
1594 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1595 {
1596 if (aCPUHotPlugEnabled)
1597 {
1598 i_setModified(IsModified_MachineData);
1599 mHWData.backup();
1600
1601 /* Add the amount of CPUs currently attached */
1602 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1603 mHWData->mCPUAttached[i] = true;
1604 }
1605 else
1606 {
1607 /*
1608 * We can disable hotplug only if the amount of maximum CPUs is equal
1609 * to the amount of attached CPUs
1610 */
1611 unsigned cCpusAttached = 0;
1612 unsigned iHighestId = 0;
1613
1614 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1615 {
1616 if (mHWData->mCPUAttached[i])
1617 {
1618 cCpusAttached++;
1619 iHighestId = i;
1620 }
1621 }
1622
1623 if ( (cCpusAttached != mHWData->mCPUCount)
1624 || (iHighestId >= mHWData->mCPUCount))
1625 return setError(E_INVALIDARG,
1626 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1627
1628 i_setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 }
1631 }
1632
1633 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1634
1635 return rc;
1636}
1637
1638HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1639{
1640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1643
1644 return S_OK;
1645}
1646
1647HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1648{
1649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1650
1651 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1652 if (SUCCEEDED(hrc))
1653 {
1654 i_setModified(IsModified_MachineData);
1655 mHWData.backup();
1656 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1657 }
1658 return hrc;
1659}
1660
1661HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1662{
1663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1664 aCPUProfile = mHWData->mCpuProfile;
1665 return S_OK;
1666}
1667
1668HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1669{
1670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1671 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1672 if (SUCCEEDED(hrc))
1673 {
1674 i_setModified(IsModified_MachineData);
1675 mHWData.backup();
1676 /* Empty equals 'host'. */
1677 if (aCPUProfile.isNotEmpty())
1678 mHWData->mCpuProfile = aCPUProfile;
1679 else
1680 mHWData->mCpuProfile = "host";
1681 }
1682 return hrc;
1683}
1684
1685HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1686{
1687#ifdef VBOX_WITH_USB_CARDREADER
1688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689
1690 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1691
1692 return S_OK;
1693#else
1694 NOREF(aEmulatedUSBCardReaderEnabled);
1695 return E_NOTIMPL;
1696#endif
1697}
1698
1699HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1700{
1701#ifdef VBOX_WITH_USB_CARDREADER
1702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1703
1704 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1705 if (FAILED(rc)) return rc;
1706
1707 i_setModified(IsModified_MachineData);
1708 mHWData.backup();
1709 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1710
1711 return S_OK;
1712#else
1713 NOREF(aEmulatedUSBCardReaderEnabled);
1714 return E_NOTIMPL;
1715#endif
1716}
1717
1718HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1719{
1720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 *aHPETEnabled = mHWData->mHPETEnabled;
1723
1724 return S_OK;
1725}
1726
1727HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1728{
1729 HRESULT rc = S_OK;
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732
1733 rc = i_checkStateDependency(MutableStateDep);
1734 if (FAILED(rc)) return rc;
1735
1736 i_setModified(IsModified_MachineData);
1737 mHWData.backup();
1738
1739 mHWData->mHPETEnabled = aHPETEnabled;
1740
1741 return rc;
1742}
1743
1744/** @todo this method should not be public */
1745HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1750
1751 return S_OK;
1752}
1753
1754/**
1755 * Set the memory balloon size.
1756 *
1757 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1758 * we have to make sure that we never call IGuest from here.
1759 */
1760HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1761{
1762 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1763#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1764 /* check limits */
1765 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1766 return setError(E_INVALIDARG,
1767 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1768 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1769
1770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1771
1772 i_setModified(IsModified_MachineData);
1773 mHWData.backup();
1774 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1775
1776 return S_OK;
1777#else
1778 NOREF(aMemoryBalloonSize);
1779 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1780#endif
1781}
1782
1783HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1784{
1785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1788 return S_OK;
1789}
1790
1791HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1792{
1793#ifdef VBOX_WITH_PAGE_SHARING
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1797 i_setModified(IsModified_MachineData);
1798 mHWData.backup();
1799 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1800 return S_OK;
1801#else
1802 NOREF(aPageFusionEnabled);
1803 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1804#endif
1805}
1806
1807HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1808{
1809 /* mBIOSSettings is constant during life time, no need to lock */
1810 aBIOSSettings = mBIOSSettings;
1811
1812 return S_OK;
1813}
1814
1815HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1816{
1817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1818
1819 aRecordingSettings = mRecordingSettings;
1820
1821 return S_OK;
1822}
1823
1824HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1825{
1826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 aGraphicsAdapter = mGraphicsAdapter;
1829
1830 return S_OK;
1831}
1832
1833HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1834{
1835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1836
1837 switch (aProperty)
1838 {
1839 case CPUPropertyType_PAE:
1840 *aValue = mHWData->mPAEEnabled;
1841 break;
1842
1843 case CPUPropertyType_LongMode:
1844 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1845 *aValue = TRUE;
1846 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1847 *aValue = FALSE;
1848#if HC_ARCH_BITS == 64
1849 else
1850 *aValue = TRUE;
1851#else
1852 else
1853 {
1854 *aValue = FALSE;
1855
1856 ComObjPtr<GuestOSType> pGuestOSType;
1857 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1858 pGuestOSType);
1859 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1860 {
1861 if (pGuestOSType->i_is64Bit())
1862 {
1863 ComObjPtr<Host> pHost = mParent->i_host();
1864 alock.release();
1865
1866 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1867 if (FAILED(hrc2))
1868 *aValue = FALSE;
1869 }
1870 }
1871 }
1872#endif
1873 break;
1874
1875 case CPUPropertyType_TripleFaultReset:
1876 *aValue = mHWData->mTripleFaultReset;
1877 break;
1878
1879 case CPUPropertyType_APIC:
1880 *aValue = mHWData->mAPIC;
1881 break;
1882
1883 case CPUPropertyType_X2APIC:
1884 *aValue = mHWData->mX2APIC;
1885 break;
1886
1887 case CPUPropertyType_IBPBOnVMExit:
1888 *aValue = mHWData->mIBPBOnVMExit;
1889 break;
1890
1891 case CPUPropertyType_IBPBOnVMEntry:
1892 *aValue = mHWData->mIBPBOnVMEntry;
1893 break;
1894
1895 case CPUPropertyType_SpecCtrl:
1896 *aValue = mHWData->mSpecCtrl;
1897 break;
1898
1899 case CPUPropertyType_SpecCtrlByHost:
1900 *aValue = mHWData->mSpecCtrlByHost;
1901 break;
1902
1903 case CPUPropertyType_HWVirt:
1904 *aValue = mHWData->mNestedHWVirt;
1905 break;
1906
1907 case CPUPropertyType_L1DFlushOnEMTScheduling:
1908 *aValue = mHWData->mL1DFlushOnSched;
1909 break;
1910
1911 case CPUPropertyType_L1DFlushOnVMEntry:
1912 *aValue = mHWData->mL1DFlushOnVMEntry;
1913 break;
1914
1915 case CPUPropertyType_MDSClearOnEMTScheduling:
1916 *aValue = mHWData->mMDSClearOnSched;
1917 break;
1918
1919 case CPUPropertyType_MDSClearOnVMEntry:
1920 *aValue = mHWData->mMDSClearOnVMEntry;
1921 break;
1922
1923 default:
1924 return E_INVALIDARG;
1925 }
1926 return S_OK;
1927}
1928
1929HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1930{
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 HRESULT rc = i_checkStateDependency(MutableStateDep);
1934 if (FAILED(rc)) return rc;
1935
1936 switch (aProperty)
1937 {
1938 case CPUPropertyType_PAE:
1939 i_setModified(IsModified_MachineData);
1940 mHWData.backup();
1941 mHWData->mPAEEnabled = !!aValue;
1942 break;
1943
1944 case CPUPropertyType_LongMode:
1945 i_setModified(IsModified_MachineData);
1946 mHWData.backup();
1947 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1948 break;
1949
1950 case CPUPropertyType_TripleFaultReset:
1951 i_setModified(IsModified_MachineData);
1952 mHWData.backup();
1953 mHWData->mTripleFaultReset = !!aValue;
1954 break;
1955
1956 case CPUPropertyType_APIC:
1957 if (mHWData->mX2APIC)
1958 aValue = TRUE;
1959 i_setModified(IsModified_MachineData);
1960 mHWData.backup();
1961 mHWData->mAPIC = !!aValue;
1962 break;
1963
1964 case CPUPropertyType_X2APIC:
1965 i_setModified(IsModified_MachineData);
1966 mHWData.backup();
1967 mHWData->mX2APIC = !!aValue;
1968 if (aValue)
1969 mHWData->mAPIC = !!aValue;
1970 break;
1971
1972 case CPUPropertyType_IBPBOnVMExit:
1973 i_setModified(IsModified_MachineData);
1974 mHWData.backup();
1975 mHWData->mIBPBOnVMExit = !!aValue;
1976 break;
1977
1978 case CPUPropertyType_IBPBOnVMEntry:
1979 i_setModified(IsModified_MachineData);
1980 mHWData.backup();
1981 mHWData->mIBPBOnVMEntry = !!aValue;
1982 break;
1983
1984 case CPUPropertyType_SpecCtrl:
1985 i_setModified(IsModified_MachineData);
1986 mHWData.backup();
1987 mHWData->mSpecCtrl = !!aValue;
1988 break;
1989
1990 case CPUPropertyType_SpecCtrlByHost:
1991 i_setModified(IsModified_MachineData);
1992 mHWData.backup();
1993 mHWData->mSpecCtrlByHost = !!aValue;
1994 break;
1995
1996 case CPUPropertyType_HWVirt:
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mNestedHWVirt = !!aValue;
2000 break;
2001
2002 case CPUPropertyType_L1DFlushOnEMTScheduling:
2003 i_setModified(IsModified_MachineData);
2004 mHWData.backup();
2005 mHWData->mL1DFlushOnSched = !!aValue;
2006 break;
2007
2008 case CPUPropertyType_L1DFlushOnVMEntry:
2009 i_setModified(IsModified_MachineData);
2010 mHWData.backup();
2011 mHWData->mL1DFlushOnVMEntry = !!aValue;
2012 break;
2013
2014 case CPUPropertyType_MDSClearOnEMTScheduling:
2015 i_setModified(IsModified_MachineData);
2016 mHWData.backup();
2017 mHWData->mMDSClearOnSched = !!aValue;
2018 break;
2019
2020 case CPUPropertyType_MDSClearOnVMEntry:
2021 i_setModified(IsModified_MachineData);
2022 mHWData.backup();
2023 mHWData->mMDSClearOnVMEntry = !!aValue;
2024 break;
2025
2026 default:
2027 return E_INVALIDARG;
2028 }
2029 return S_OK;
2030}
2031
2032HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2033 ULONG *aValEcx, ULONG *aValEdx)
2034{
2035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2036 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2037 {
2038 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2039 it != mHWData->mCpuIdLeafList.end();
2040 ++it)
2041 {
2042 if (aOrdinal == 0)
2043 {
2044 const settings::CpuIdLeaf &rLeaf= *it;
2045 *aIdx = rLeaf.idx;
2046 *aSubIdx = rLeaf.idxSub;
2047 *aValEax = rLeaf.uEax;
2048 *aValEbx = rLeaf.uEbx;
2049 *aValEcx = rLeaf.uEcx;
2050 *aValEdx = rLeaf.uEdx;
2051 return S_OK;
2052 }
2053 aOrdinal--;
2054 }
2055 }
2056 return E_INVALIDARG;
2057}
2058
2059HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2060{
2061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2062
2063 /*
2064 * Search the list.
2065 */
2066 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2067 {
2068 const settings::CpuIdLeaf &rLeaf= *it;
2069 if ( rLeaf.idx == aIdx
2070 && ( aSubIdx == UINT32_MAX
2071 || rLeaf.idxSub == aSubIdx) )
2072 {
2073 *aValEax = rLeaf.uEax;
2074 *aValEbx = rLeaf.uEbx;
2075 *aValEcx = rLeaf.uEcx;
2076 *aValEdx = rLeaf.uEdx;
2077 return S_OK;
2078 }
2079 }
2080
2081 return E_INVALIDARG;
2082}
2083
2084
2085HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2086{
2087 /*
2088 * Validate input before taking locks and checking state.
2089 */
2090 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2091 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2092 if ( aIdx >= UINT32_C(0x20)
2093 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2094 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2095 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2096
2097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2098 HRESULT rc = i_checkStateDependency(MutableStateDep);
2099 if (FAILED(rc)) return rc;
2100
2101 /*
2102 * Impose a maximum number of leaves.
2103 */
2104 if (mHWData->mCpuIdLeafList.size() > 256)
2105 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2106
2107 /*
2108 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2109 */
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112
2113 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2114 {
2115 settings::CpuIdLeaf &rLeaf= *it;
2116 if ( rLeaf.idx == aIdx
2117 && ( aSubIdx == UINT32_MAX
2118 || rLeaf.idxSub == aSubIdx) )
2119 it = mHWData->mCpuIdLeafList.erase(it);
2120 else
2121 ++it;
2122 }
2123
2124 settings::CpuIdLeaf NewLeaf;
2125 NewLeaf.idx = aIdx;
2126 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2127 NewLeaf.uEax = aValEax;
2128 NewLeaf.uEbx = aValEbx;
2129 NewLeaf.uEcx = aValEcx;
2130 NewLeaf.uEdx = aValEdx;
2131 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2132 return S_OK;
2133}
2134
2135HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2136{
2137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2138
2139 HRESULT rc = i_checkStateDependency(MutableStateDep);
2140 if (FAILED(rc)) return rc;
2141
2142 /*
2143 * Do the removal.
2144 */
2145 bool fModified = mHWData.isBackedUp();
2146 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2147 {
2148 settings::CpuIdLeaf &rLeaf= *it;
2149 if ( rLeaf.idx == aIdx
2150 && ( aSubIdx == UINT32_MAX
2151 || rLeaf.idxSub == aSubIdx) )
2152 {
2153 if (!fModified)
2154 {
2155 fModified = true;
2156 i_setModified(IsModified_MachineData);
2157 mHWData.backup();
2158 // Start from the beginning, since mHWData.backup() creates
2159 // a new list, causing iterator mixup. This makes sure that
2160 // the settings are not unnecessarily marked as modified,
2161 // at the price of extra list walking.
2162 it = mHWData->mCpuIdLeafList.begin();
2163 }
2164 else
2165 it = mHWData->mCpuIdLeafList.erase(it);
2166 }
2167 else
2168 ++it;
2169 }
2170
2171 return S_OK;
2172}
2173
2174HRESULT Machine::removeAllCPUIDLeaves()
2175{
2176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2177
2178 HRESULT rc = i_checkStateDependency(MutableStateDep);
2179 if (FAILED(rc)) return rc;
2180
2181 if (mHWData->mCpuIdLeafList.size() > 0)
2182 {
2183 i_setModified(IsModified_MachineData);
2184 mHWData.backup();
2185
2186 mHWData->mCpuIdLeafList.clear();
2187 }
2188
2189 return S_OK;
2190}
2191HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2192{
2193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2194
2195 switch(aProperty)
2196 {
2197 case HWVirtExPropertyType_Enabled:
2198 *aValue = mHWData->mHWVirtExEnabled;
2199 break;
2200
2201 case HWVirtExPropertyType_VPID:
2202 *aValue = mHWData->mHWVirtExVPIDEnabled;
2203 break;
2204
2205 case HWVirtExPropertyType_NestedPaging:
2206 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2207 break;
2208
2209 case HWVirtExPropertyType_UnrestrictedExecution:
2210 *aValue = mHWData->mHWVirtExUXEnabled;
2211 break;
2212
2213 case HWVirtExPropertyType_LargePages:
2214 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2215#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2216 *aValue = FALSE;
2217#endif
2218 break;
2219
2220 case HWVirtExPropertyType_Force:
2221 *aValue = mHWData->mHWVirtExForceEnabled;
2222 break;
2223
2224 case HWVirtExPropertyType_UseNativeApi:
2225 *aValue = mHWData->mHWVirtExUseNativeApi;
2226 break;
2227
2228 default:
2229 return E_INVALIDARG;
2230 }
2231 return S_OK;
2232}
2233
2234HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2235{
2236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2237
2238 HRESULT rc = i_checkStateDependency(MutableStateDep);
2239 if (FAILED(rc)) return rc;
2240
2241 switch (aProperty)
2242 {
2243 case HWVirtExPropertyType_Enabled:
2244 i_setModified(IsModified_MachineData);
2245 mHWData.backup();
2246 mHWData->mHWVirtExEnabled = !!aValue;
2247 break;
2248
2249 case HWVirtExPropertyType_VPID:
2250 i_setModified(IsModified_MachineData);
2251 mHWData.backup();
2252 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2253 break;
2254
2255 case HWVirtExPropertyType_NestedPaging:
2256 i_setModified(IsModified_MachineData);
2257 mHWData.backup();
2258 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2259 break;
2260
2261 case HWVirtExPropertyType_UnrestrictedExecution:
2262 i_setModified(IsModified_MachineData);
2263 mHWData.backup();
2264 mHWData->mHWVirtExUXEnabled = !!aValue;
2265 break;
2266
2267 case HWVirtExPropertyType_LargePages:
2268 i_setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2271 break;
2272
2273 case HWVirtExPropertyType_Force:
2274 i_setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mHWVirtExForceEnabled = !!aValue;
2277 break;
2278
2279 case HWVirtExPropertyType_UseNativeApi:
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mHWVirtExUseNativeApi = !!aValue;
2283 break;
2284
2285 default:
2286 return E_INVALIDARG;
2287 }
2288
2289 return S_OK;
2290}
2291
2292HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2293{
2294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2297
2298 return S_OK;
2299}
2300
2301HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2302{
2303 /** @todo (r=dmik):
2304 * 1. Allow to change the name of the snapshot folder containing snapshots
2305 * 2. Rename the folder on disk instead of just changing the property
2306 * value (to be smart and not to leave garbage). Note that it cannot be
2307 * done here because the change may be rolled back. Thus, the right
2308 * place is #saveSettings().
2309 */
2310
2311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2312
2313 HRESULT rc = i_checkStateDependency(MutableStateDep);
2314 if (FAILED(rc)) return rc;
2315
2316 if (!mData->mCurrentSnapshot.isNull())
2317 return setError(E_FAIL,
2318 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2319
2320 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2321
2322 if (strSnapshotFolder.isEmpty())
2323 strSnapshotFolder = "Snapshots";
2324 int vrc = i_calculateFullPath(strSnapshotFolder,
2325 strSnapshotFolder);
2326 if (RT_FAILURE(vrc))
2327 return setErrorBoth(E_FAIL, vrc,
2328 tr("Invalid snapshot folder '%s' (%Rrc)"),
2329 strSnapshotFolder.c_str(), vrc);
2330
2331 i_setModified(IsModified_MachineData);
2332 mUserData.backup();
2333
2334 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2335
2336 return S_OK;
2337}
2338
2339HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2340{
2341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2342
2343 aMediumAttachments.resize(mMediumAttachments->size());
2344 size_t i = 0;
2345 for (MediumAttachmentList::const_iterator
2346 it = mMediumAttachments->begin();
2347 it != mMediumAttachments->end();
2348 ++it, ++i)
2349 aMediumAttachments[i] = *it;
2350
2351 return S_OK;
2352}
2353
2354HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2355{
2356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2357
2358 Assert(!!mVRDEServer);
2359
2360 aVRDEServer = mVRDEServer;
2361
2362 return S_OK;
2363}
2364
2365HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2366{
2367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 aAudioAdapter = mAudioAdapter;
2370
2371 return S_OK;
2372}
2373
2374HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2375{
2376#ifdef VBOX_WITH_VUSB
2377 clearError();
2378 MultiResult rc(S_OK);
2379
2380# ifdef VBOX_WITH_USB
2381 rc = mParent->i_host()->i_checkUSBProxyService();
2382 if (FAILED(rc)) return rc;
2383# endif
2384
2385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2386
2387 aUSBControllers.resize(mUSBControllers->size());
2388 size_t i = 0;
2389 for (USBControllerList::const_iterator
2390 it = mUSBControllers->begin();
2391 it != mUSBControllers->end();
2392 ++it, ++i)
2393 aUSBControllers[i] = *it;
2394
2395 return S_OK;
2396#else
2397 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2398 * extended error info to indicate that USB is simply not available
2399 * (w/o treating it as a failure), for example, as in OSE */
2400 NOREF(aUSBControllers);
2401 ReturnComNotImplemented();
2402#endif /* VBOX_WITH_VUSB */
2403}
2404
2405HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2406{
2407#ifdef VBOX_WITH_VUSB
2408 clearError();
2409 MultiResult rc(S_OK);
2410
2411# ifdef VBOX_WITH_USB
2412 rc = mParent->i_host()->i_checkUSBProxyService();
2413 if (FAILED(rc)) return rc;
2414# endif
2415
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 aUSBDeviceFilters = mUSBDeviceFilters;
2419 return rc;
2420#else
2421 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2422 * extended error info to indicate that USB is simply not available
2423 * (w/o treating it as a failure), for example, as in OSE */
2424 NOREF(aUSBDeviceFilters);
2425 ReturnComNotImplemented();
2426#endif /* VBOX_WITH_VUSB */
2427}
2428
2429HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2430{
2431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2432
2433 aSettingsFilePath = mData->m_strConfigFileFull;
2434
2435 return S_OK;
2436}
2437
2438HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2439{
2440 RT_NOREF(aSettingsFilePath);
2441 ReturnComNotImplemented();
2442}
2443
2444HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2445{
2446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2447
2448 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2449 if (FAILED(rc)) return rc;
2450
2451 if (!mData->pMachineConfigFile->fileExists())
2452 // this is a new machine, and no config file exists yet:
2453 *aSettingsModified = TRUE;
2454 else
2455 *aSettingsModified = (mData->flModifications != 0);
2456
2457 return S_OK;
2458}
2459
2460HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2461{
2462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2463
2464 *aSessionState = mData->mSession.mState;
2465
2466 return S_OK;
2467}
2468
2469HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2470{
2471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2472
2473 aSessionName = mData->mSession.mName;
2474
2475 return S_OK;
2476}
2477
2478HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2479{
2480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 *aSessionPID = mData->mSession.mPID;
2483
2484 return S_OK;
2485}
2486
2487HRESULT Machine::getState(MachineState_T *aState)
2488{
2489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 *aState = mData->mMachineState;
2492 Assert(mData->mMachineState != MachineState_Null);
2493
2494 return S_OK;
2495}
2496
2497HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2498{
2499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2500
2501 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2502
2503 return S_OK;
2504}
2505
2506HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2507{
2508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 aStateFilePath = mSSData->strStateFilePath;
2511
2512 return S_OK;
2513}
2514
2515HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2516{
2517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2518
2519 i_getLogFolder(aLogFolder);
2520
2521 return S_OK;
2522}
2523
2524HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2525{
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 aCurrentSnapshot = mData->mCurrentSnapshot;
2529
2530 return S_OK;
2531}
2532
2533HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2534{
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2538 ? 0
2539 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2540
2541 return S_OK;
2542}
2543
2544HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2545{
2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 /* Note: for machines with no snapshots, we always return FALSE
2549 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2550 * reasons :) */
2551
2552 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2553 ? FALSE
2554 : mData->mCurrentStateModified;
2555
2556 return S_OK;
2557}
2558
2559HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2560{
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 aSharedFolders.resize(mHWData->mSharedFolders.size());
2564 size_t i = 0;
2565 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2566 it = mHWData->mSharedFolders.begin();
2567 it != mHWData->mSharedFolders.end();
2568 ++it, ++i)
2569 aSharedFolders[i] = *it;
2570
2571 return S_OK;
2572}
2573
2574HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 *aClipboardMode = mHWData->mClipboardMode;
2579
2580 return S_OK;
2581}
2582
2583HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2584{
2585 HRESULT rc = S_OK;
2586
2587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589 alock.release();
2590 rc = i_onClipboardModeChange(aClipboardMode);
2591 alock.acquire();
2592 if (FAILED(rc)) return rc;
2593
2594 i_setModified(IsModified_MachineData);
2595 mHWData.backup();
2596 mHWData->mClipboardMode = aClipboardMode;
2597
2598 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2599 if (Global::IsOnline(mData->mMachineState))
2600 i_saveSettings(NULL);
2601
2602 return S_OK;
2603}
2604
2605HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2606{
2607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2610
2611 return S_OK;
2612}
2613
2614HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2615{
2616 HRESULT rc = S_OK;
2617
2618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2619
2620 alock.release();
2621 rc = i_onClipboardFileTransferModeChange(aEnabled);
2622 alock.acquire();
2623 if (FAILED(rc)) return rc;
2624
2625 i_setModified(IsModified_MachineData);
2626 mHWData.backup();
2627 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2628
2629 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2630 if (Global::IsOnline(mData->mMachineState))
2631 i_saveSettings(NULL);
2632
2633 return S_OK;
2634}
2635
2636HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2637{
2638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2639
2640 *aDnDMode = mHWData->mDnDMode;
2641
2642 return S_OK;
2643}
2644
2645HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2646{
2647 HRESULT rc = S_OK;
2648
2649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 alock.release();
2652 rc = i_onDnDModeChange(aDnDMode);
2653
2654 alock.acquire();
2655 if (FAILED(rc)) return rc;
2656
2657 i_setModified(IsModified_MachineData);
2658 mHWData.backup();
2659 mHWData->mDnDMode = aDnDMode;
2660
2661 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2662 if (Global::IsOnline(mData->mMachineState))
2663 i_saveSettings(NULL);
2664
2665 return S_OK;
2666}
2667
2668HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2669{
2670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2671
2672 aStorageControllers.resize(mStorageControllers->size());
2673 size_t i = 0;
2674 for (StorageControllerList::const_iterator
2675 it = mStorageControllers->begin();
2676 it != mStorageControllers->end();
2677 ++it, ++i)
2678 aStorageControllers[i] = *it;
2679
2680 return S_OK;
2681}
2682
2683HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2684{
2685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2686
2687 *aEnabled = mUserData->s.fTeleporterEnabled;
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2693{
2694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 /* Only allow it to be set to true when PoweredOff or Aborted.
2697 (Clearing it is always permitted.) */
2698 if ( aTeleporterEnabled
2699 && mData->mRegistered
2700 && ( !i_isSessionMachine()
2701 || ( mData->mMachineState != MachineState_PoweredOff
2702 && mData->mMachineState != MachineState_Teleported
2703 && mData->mMachineState != MachineState_Aborted
2704 )
2705 )
2706 )
2707 return setError(VBOX_E_INVALID_VM_STATE,
2708 tr("The machine is not powered off (state is %s)"),
2709 Global::stringifyMachineState(mData->mMachineState));
2710
2711 i_setModified(IsModified_MachineData);
2712 mUserData.backup();
2713 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2714
2715 return S_OK;
2716}
2717
2718HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2719{
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2728{
2729 if (aTeleporterPort >= _64K)
2730 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2731
2732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2735 if (FAILED(rc)) return rc;
2736
2737 i_setModified(IsModified_MachineData);
2738 mUserData.backup();
2739 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2740
2741 return S_OK;
2742}
2743
2744HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2745{
2746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2749
2750 return S_OK;
2751}
2752
2753HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2754{
2755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2756
2757 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2758 if (FAILED(rc)) return rc;
2759
2760 i_setModified(IsModified_MachineData);
2761 mUserData.backup();
2762 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2763
2764 return S_OK;
2765}
2766
2767HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2768{
2769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2770 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2771
2772 return S_OK;
2773}
2774
2775HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2776{
2777 /*
2778 * Hash the password first.
2779 */
2780 com::Utf8Str aT = aTeleporterPassword;
2781
2782 if (!aT.isEmpty())
2783 {
2784 if (VBoxIsPasswordHashed(&aT))
2785 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2786 VBoxHashPassword(&aT);
2787 }
2788
2789 /*
2790 * Do the update.
2791 */
2792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2793 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2794 if (SUCCEEDED(hrc))
2795 {
2796 i_setModified(IsModified_MachineData);
2797 mUserData.backup();
2798 mUserData->s.strTeleporterPassword = aT;
2799 }
2800
2801 return hrc;
2802}
2803
2804HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2814{
2815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 /* Only allow it to be set to true when PoweredOff or Aborted.
2818 (Clearing it is always permitted.) */
2819 if ( aRTCUseUTC
2820 && mData->mRegistered
2821 && ( !i_isSessionMachine()
2822 || ( mData->mMachineState != MachineState_PoweredOff
2823 && mData->mMachineState != MachineState_Teleported
2824 && mData->mMachineState != MachineState_Aborted
2825 )
2826 )
2827 )
2828 return setError(VBOX_E_INVALID_VM_STATE,
2829 tr("The machine is not powered off (state is %s)"),
2830 Global::stringifyMachineState(mData->mMachineState));
2831
2832 i_setModified(IsModified_MachineData);
2833 mUserData.backup();
2834 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2844
2845 return S_OK;
2846}
2847
2848HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2849{
2850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 HRESULT rc = i_checkStateDependency(MutableStateDep);
2853 if (FAILED(rc)) return rc;
2854
2855 i_setModified(IsModified_MachineData);
2856 mHWData.backup();
2857 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2858
2859 return S_OK;
2860}
2861
2862HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2863{
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 *aIOCacheSize = mHWData->mIOCacheSize;
2867
2868 return S_OK;
2869}
2870
2871HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2872{
2873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 HRESULT rc = i_checkStateDependency(MutableStateDep);
2876 if (FAILED(rc)) return rc;
2877
2878 i_setModified(IsModified_MachineData);
2879 mHWData.backup();
2880 mHWData->mIOCacheSize = aIOCacheSize;
2881
2882 return S_OK;
2883}
2884
2885
2886/**
2887 * @note Locks objects!
2888 */
2889HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2890 LockType_T aLockType)
2891{
2892 /* check the session state */
2893 SessionState_T state;
2894 HRESULT rc = aSession->COMGETTER(State)(&state);
2895 if (FAILED(rc)) return rc;
2896
2897 if (state != SessionState_Unlocked)
2898 return setError(VBOX_E_INVALID_OBJECT_STATE,
2899 tr("The given session is busy"));
2900
2901 // get the client's IInternalSessionControl interface
2902 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2903 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2904 E_INVALIDARG);
2905
2906 // session name (only used in some code paths)
2907 Utf8Str strSessionName;
2908
2909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 if (!mData->mRegistered)
2912 return setError(E_UNEXPECTED,
2913 tr("The machine '%s' is not registered"),
2914 mUserData->s.strName.c_str());
2915
2916 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2917
2918 SessionState_T oldState = mData->mSession.mState;
2919 /* Hack: in case the session is closing and there is a progress object
2920 * which allows waiting for the session to be closed, take the opportunity
2921 * and do a limited wait (max. 1 second). This helps a lot when the system
2922 * is busy and thus session closing can take a little while. */
2923 if ( mData->mSession.mState == SessionState_Unlocking
2924 && mData->mSession.mProgress)
2925 {
2926 alock.release();
2927 mData->mSession.mProgress->WaitForCompletion(1000);
2928 alock.acquire();
2929 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2930 }
2931
2932 // try again now
2933 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2934 // (i.e. session machine exists)
2935 && (aLockType == LockType_Shared) // caller wants a shared link to the
2936 // existing session that holds the write lock:
2937 )
2938 {
2939 // OK, share the session... we are now dealing with three processes:
2940 // 1) VBoxSVC (where this code runs);
2941 // 2) process C: the caller's client process (who wants a shared session);
2942 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2943
2944 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2945 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2946 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2947 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2948 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2949
2950 /*
2951 * Release the lock before calling the client process. It's safe here
2952 * since the only thing to do after we get the lock again is to add
2953 * the remote control to the list (which doesn't directly influence
2954 * anything).
2955 */
2956 alock.release();
2957
2958 // get the console of the session holding the write lock (this is a remote call)
2959 ComPtr<IConsole> pConsoleW;
2960 if (mData->mSession.mLockType == LockType_VM)
2961 {
2962 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2963 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2964 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2965 if (FAILED(rc))
2966 // the failure may occur w/o any error info (from RPC), so provide one
2967 return setError(VBOX_E_VM_ERROR,
2968 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
2969 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2970 }
2971
2972 // share the session machine and W's console with the caller's session
2973 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2974 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2975 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2976
2977 if (FAILED(rc))
2978 // the failure may occur w/o any error info (from RPC), so provide one
2979 return setError(VBOX_E_VM_ERROR,
2980 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
2981 alock.acquire();
2982
2983 // need to revalidate the state after acquiring the lock again
2984 if (mData->mSession.mState != SessionState_Locked)
2985 {
2986 pSessionControl->Uninitialize();
2987 return setError(VBOX_E_INVALID_SESSION_STATE,
2988 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2989 mUserData->s.strName.c_str());
2990 }
2991
2992 // add the caller's session to the list
2993 mData->mSession.mRemoteControls.push_back(pSessionControl);
2994 }
2995 else if ( mData->mSession.mState == SessionState_Locked
2996 || mData->mSession.mState == SessionState_Unlocking
2997 )
2998 {
2999 // sharing not permitted, or machine still unlocking:
3000 return setError(VBOX_E_INVALID_OBJECT_STATE,
3001 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3002 mUserData->s.strName.c_str());
3003 }
3004 else
3005 {
3006 // machine is not locked: then write-lock the machine (create the session machine)
3007
3008 // must not be busy
3009 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3010
3011 // get the caller's session PID
3012 RTPROCESS pid = NIL_RTPROCESS;
3013 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3014 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3015 Assert(pid != NIL_RTPROCESS);
3016
3017 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3018
3019 if (fLaunchingVMProcess)
3020 {
3021 if (mData->mSession.mPID == NIL_RTPROCESS)
3022 {
3023 // two or more clients racing for a lock, the one which set the
3024 // session state to Spawning will win, the others will get an
3025 // error as we can't decide here if waiting a little would help
3026 // (only for shared locks this would avoid an error)
3027 return setError(VBOX_E_INVALID_OBJECT_STATE,
3028 tr("The machine '%s' already has a lock request pending"),
3029 mUserData->s.strName.c_str());
3030 }
3031
3032 // this machine is awaiting for a spawning session to be opened:
3033 // then the calling process must be the one that got started by
3034 // LaunchVMProcess()
3035
3036 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3037 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3038
3039#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3040 /* Hardened windows builds spawns three processes when a VM is
3041 launched, the 3rd one is the one that will end up here. */
3042 RTPROCESS ppid;
3043 int rc = RTProcQueryParent(pid, &ppid);
3044 if (RT_SUCCESS(rc))
3045 rc = RTProcQueryParent(ppid, &ppid);
3046 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3047 || rc == VERR_ACCESS_DENIED)
3048 {
3049 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3050 mData->mSession.mPID = pid;
3051 }
3052#endif
3053
3054 if (mData->mSession.mPID != pid)
3055 return setError(E_ACCESSDENIED,
3056 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3057 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3058 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3059 }
3060
3061 // create the mutable SessionMachine from the current machine
3062 ComObjPtr<SessionMachine> sessionMachine;
3063 sessionMachine.createObject();
3064 rc = sessionMachine->init(this);
3065 AssertComRC(rc);
3066
3067 /* NOTE: doing return from this function after this point but
3068 * before the end is forbidden since it may call SessionMachine::uninit()
3069 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3070 * lock while still holding the Machine lock in alock so that a deadlock
3071 * is possible due to the wrong lock order. */
3072
3073 if (SUCCEEDED(rc))
3074 {
3075 /*
3076 * Set the session state to Spawning to protect against subsequent
3077 * attempts to open a session and to unregister the machine after
3078 * we release the lock.
3079 */
3080 SessionState_T origState = mData->mSession.mState;
3081 mData->mSession.mState = SessionState_Spawning;
3082
3083#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3084 /* Get the client token ID to be passed to the client process */
3085 Utf8Str strTokenId;
3086 sessionMachine->i_getTokenId(strTokenId);
3087 Assert(!strTokenId.isEmpty());
3088#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3089 /* Get the client token to be passed to the client process */
3090 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3091 /* The token is now "owned" by pToken, fix refcount */
3092 if (!pToken.isNull())
3093 pToken->Release();
3094#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3095
3096 /*
3097 * Release the lock before calling the client process -- it will call
3098 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3099 * because the state is Spawning, so that LaunchVMProcess() and
3100 * LockMachine() calls will fail. This method, called before we
3101 * acquire the lock again, will fail because of the wrong PID.
3102 *
3103 * Note that mData->mSession.mRemoteControls accessed outside
3104 * the lock may not be modified when state is Spawning, so it's safe.
3105 */
3106 alock.release();
3107
3108 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3109#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3110 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3111#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3112 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3113 /* Now the token is owned by the client process. */
3114 pToken.setNull();
3115#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3116 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3117
3118 /* The failure may occur w/o any error info (from RPC), so provide one */
3119 if (FAILED(rc))
3120 setError(VBOX_E_VM_ERROR,
3121 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3122
3123 // get session name, either to remember or to compare against
3124 // the already known session name.
3125 {
3126 Bstr bstrSessionName;
3127 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3128 if (SUCCEEDED(rc2))
3129 strSessionName = bstrSessionName;
3130 }
3131
3132 if ( SUCCEEDED(rc)
3133 && fLaunchingVMProcess
3134 )
3135 {
3136 /* complete the remote session initialization */
3137
3138 /* get the console from the direct session */
3139 ComPtr<IConsole> console;
3140 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3141 ComAssertComRC(rc);
3142
3143 if (SUCCEEDED(rc) && !console)
3144 {
3145 ComAssert(!!console);
3146 rc = E_FAIL;
3147 }
3148
3149 /* assign machine & console to the remote session */
3150 if (SUCCEEDED(rc))
3151 {
3152 /*
3153 * after LaunchVMProcess(), the first and the only
3154 * entry in remoteControls is that remote session
3155 */
3156 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3157 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3158 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3159
3160 /* The failure may occur w/o any error info (from RPC), so provide one */
3161 if (FAILED(rc))
3162 setError(VBOX_E_VM_ERROR,
3163 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3164 }
3165
3166 if (FAILED(rc))
3167 pSessionControl->Uninitialize();
3168 }
3169
3170 /* acquire the lock again */
3171 alock.acquire();
3172
3173 /* Restore the session state */
3174 mData->mSession.mState = origState;
3175 }
3176
3177 // finalize spawning anyway (this is why we don't return on errors above)
3178 if (fLaunchingVMProcess)
3179 {
3180 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3181 /* Note that the progress object is finalized later */
3182 /** @todo Consider checking mData->mSession.mProgress for cancellation
3183 * around here. */
3184
3185 /* We don't reset mSession.mPID here because it is necessary for
3186 * SessionMachine::uninit() to reap the child process later. */
3187
3188 if (FAILED(rc))
3189 {
3190 /* Close the remote session, remove the remote control from the list
3191 * and reset session state to Closed (@note keep the code in sync
3192 * with the relevant part in checkForSpawnFailure()). */
3193
3194 Assert(mData->mSession.mRemoteControls.size() == 1);
3195 if (mData->mSession.mRemoteControls.size() == 1)
3196 {
3197 ErrorInfoKeeper eik;
3198 mData->mSession.mRemoteControls.front()->Uninitialize();
3199 }
3200
3201 mData->mSession.mRemoteControls.clear();
3202 mData->mSession.mState = SessionState_Unlocked;
3203 }
3204 }
3205 else
3206 {
3207 /* memorize PID of the directly opened session */
3208 if (SUCCEEDED(rc))
3209 mData->mSession.mPID = pid;
3210 }
3211
3212 if (SUCCEEDED(rc))
3213 {
3214 mData->mSession.mLockType = aLockType;
3215 /* memorize the direct session control and cache IUnknown for it */
3216 mData->mSession.mDirectControl = pSessionControl;
3217 mData->mSession.mState = SessionState_Locked;
3218 if (!fLaunchingVMProcess)
3219 mData->mSession.mName = strSessionName;
3220 /* associate the SessionMachine with this Machine */
3221 mData->mSession.mMachine = sessionMachine;
3222
3223 /* request an IUnknown pointer early from the remote party for later
3224 * identity checks (it will be internally cached within mDirectControl
3225 * at least on XPCOM) */
3226 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3227 NOREF(unk);
3228 }
3229
3230 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3231 * would break the lock order */
3232 alock.release();
3233
3234 /* uninitialize the created session machine on failure */
3235 if (FAILED(rc))
3236 sessionMachine->uninit();
3237 }
3238
3239 if (SUCCEEDED(rc))
3240 {
3241 /*
3242 * tell the client watcher thread to update the set of
3243 * machines that have open sessions
3244 */
3245 mParent->i_updateClientWatcher();
3246
3247 if (oldState != SessionState_Locked)
3248 /* fire an event */
3249 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3250 }
3251
3252 return rc;
3253}
3254
3255/**
3256 * @note Locks objects!
3257 */
3258HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3259 const com::Utf8Str &aName,
3260 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3261 ComPtr<IProgress> &aProgress)
3262{
3263 Utf8Str strFrontend(aName);
3264 /* "emergencystop" doesn't need the session, so skip the checks/interface
3265 * retrieval. This code doesn't quite fit in here, but introducing a
3266 * special API method would be even more effort, and would require explicit
3267 * support by every API client. It's better to hide the feature a bit. */
3268 if (strFrontend != "emergencystop")
3269 CheckComArgNotNull(aSession);
3270
3271 HRESULT rc = S_OK;
3272 if (strFrontend.isEmpty())
3273 {
3274 Bstr bstrFrontend;
3275 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3276 if (FAILED(rc))
3277 return rc;
3278 strFrontend = bstrFrontend;
3279 if (strFrontend.isEmpty())
3280 {
3281 ComPtr<ISystemProperties> systemProperties;
3282 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3283 if (FAILED(rc))
3284 return rc;
3285 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3286 if (FAILED(rc))
3287 return rc;
3288 strFrontend = bstrFrontend;
3289 }
3290 /* paranoia - emergencystop is not a valid default */
3291 if (strFrontend == "emergencystop")
3292 strFrontend = Utf8Str::Empty;
3293 }
3294 /* default frontend: Qt GUI */
3295 if (strFrontend.isEmpty())
3296 strFrontend = "GUI/Qt";
3297
3298 if (strFrontend != "emergencystop")
3299 {
3300 /* check the session state */
3301 SessionState_T state;
3302 rc = aSession->COMGETTER(State)(&state);
3303 if (FAILED(rc))
3304 return rc;
3305
3306 if (state != SessionState_Unlocked)
3307 return setError(VBOX_E_INVALID_OBJECT_STATE,
3308 tr("The given session is busy"));
3309
3310 /* get the IInternalSessionControl interface */
3311 ComPtr<IInternalSessionControl> control(aSession);
3312 ComAssertMsgRet(!control.isNull(),
3313 ("No IInternalSessionControl interface"),
3314 E_INVALIDARG);
3315
3316 /* get the teleporter enable state for the progress object init. */
3317 BOOL fTeleporterEnabled;
3318 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3319 if (FAILED(rc))
3320 return rc;
3321
3322 /* create a progress object */
3323 ComObjPtr<ProgressProxy> progress;
3324 progress.createObject();
3325 rc = progress->init(mParent,
3326 static_cast<IMachine*>(this),
3327 Bstr(tr("Starting VM")).raw(),
3328 TRUE /* aCancelable */,
3329 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3330 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3331 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3332 2 /* uFirstOperationWeight */,
3333 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3334
3335 if (SUCCEEDED(rc))
3336 {
3337#ifdef VBOX_WITH_CLOUD_NET
3338 i_connectToCloudNetwork(progress);
3339#endif /* VBOX_WITH_CLOUD_NET */
3340
3341 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3342 if (SUCCEEDED(rc))
3343 {
3344 aProgress = progress;
3345
3346 /* signal the client watcher thread */
3347 mParent->i_updateClientWatcher();
3348
3349 /* fire an event */
3350 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3351 }
3352 }
3353 }
3354 else
3355 {
3356 /* no progress object - either instant success or failure */
3357 aProgress = NULL;
3358
3359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3360
3361 if (mData->mSession.mState != SessionState_Locked)
3362 return setError(VBOX_E_INVALID_OBJECT_STATE,
3363 tr("The machine '%s' is not locked by a session"),
3364 mUserData->s.strName.c_str());
3365
3366 /* must have a VM process associated - do not kill normal API clients
3367 * with an open session */
3368 if (!Global::IsOnline(mData->mMachineState))
3369 return setError(VBOX_E_INVALID_OBJECT_STATE,
3370 tr("The machine '%s' does not have a VM process"),
3371 mUserData->s.strName.c_str());
3372
3373 /* forcibly terminate the VM process */
3374 if (mData->mSession.mPID != NIL_RTPROCESS)
3375 RTProcTerminate(mData->mSession.mPID);
3376
3377 /* signal the client watcher thread, as most likely the client has
3378 * been terminated */
3379 mParent->i_updateClientWatcher();
3380 }
3381
3382 return rc;
3383}
3384
3385HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3386{
3387 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3388 return setError(E_INVALIDARG,
3389 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3390 aPosition, SchemaDefs::MaxBootPosition);
3391
3392 if (aDevice == DeviceType_USB)
3393 return setError(E_NOTIMPL,
3394 tr("Booting from USB device is currently not supported"));
3395
3396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3397
3398 HRESULT rc = i_checkStateDependency(MutableStateDep);
3399 if (FAILED(rc)) return rc;
3400
3401 i_setModified(IsModified_MachineData);
3402 mHWData.backup();
3403 mHWData->mBootOrder[aPosition - 1] = aDevice;
3404
3405 return S_OK;
3406}
3407
3408HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3409{
3410 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3411 return setError(E_INVALIDARG,
3412 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3413 aPosition, SchemaDefs::MaxBootPosition);
3414
3415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3416
3417 *aDevice = mHWData->mBootOrder[aPosition - 1];
3418
3419 return S_OK;
3420}
3421
3422HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3423 LONG aControllerPort,
3424 LONG aDevice,
3425 DeviceType_T aType,
3426 const ComPtr<IMedium> &aMedium)
3427{
3428 IMedium *aM = aMedium;
3429 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3430 aName.c_str(), aControllerPort, aDevice, aType, aM));
3431
3432 // request the host lock first, since might be calling Host methods for getting host drives;
3433 // next, protect the media tree all the while we're in here, as well as our member variables
3434 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3435 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3436
3437 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3438 if (FAILED(rc)) return rc;
3439
3440 /// @todo NEWMEDIA implicit machine registration
3441 if (!mData->mRegistered)
3442 return setError(VBOX_E_INVALID_OBJECT_STATE,
3443 tr("Cannot attach storage devices to an unregistered machine"));
3444
3445 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3446
3447 /* Check for an existing controller. */
3448 ComObjPtr<StorageController> ctl;
3449 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3450 if (FAILED(rc)) return rc;
3451
3452 StorageControllerType_T ctrlType;
3453 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3454 if (FAILED(rc))
3455 return setError(E_FAIL,
3456 tr("Could not get type of controller '%s'"),
3457 aName.c_str());
3458
3459 bool fSilent = false;
3460 Utf8Str strReconfig;
3461
3462 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3463 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3464 if ( mData->mMachineState == MachineState_Paused
3465 && strReconfig == "1")
3466 fSilent = true;
3467
3468 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3469 bool fHotplug = false;
3470 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3471 fHotplug = true;
3472
3473 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3474 return setError(VBOX_E_INVALID_VM_STATE,
3475 tr("Controller '%s' does not support hotplugging"),
3476 aName.c_str());
3477
3478 // check that the port and device are not out of range
3479 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3480 if (FAILED(rc)) return rc;
3481
3482 /* check if the device slot is already busy */
3483 MediumAttachment *pAttachTemp;
3484 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3485 aName,
3486 aControllerPort,
3487 aDevice)))
3488 {
3489 Medium *pMedium = pAttachTemp->i_getMedium();
3490 if (pMedium)
3491 {
3492 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3493 return setError(VBOX_E_OBJECT_IN_USE,
3494 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3495 pMedium->i_getLocationFull().c_str(),
3496 aControllerPort,
3497 aDevice,
3498 aName.c_str());
3499 }
3500 else
3501 return setError(VBOX_E_OBJECT_IN_USE,
3502 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3503 aControllerPort, aDevice, aName.c_str());
3504 }
3505
3506 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3507 if (aMedium && medium.isNull())
3508 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3509
3510 AutoCaller mediumCaller(medium);
3511 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3512
3513 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3514
3515 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3516 && !medium.isNull()
3517 )
3518 return setError(VBOX_E_OBJECT_IN_USE,
3519 tr("Medium '%s' is already attached to this virtual machine"),
3520 medium->i_getLocationFull().c_str());
3521
3522 if (!medium.isNull())
3523 {
3524 MediumType_T mtype = medium->i_getType();
3525 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3526 // For DVDs it's not written to the config file, so needs no global config
3527 // version bump. For floppies it's a new attribute "type", which is ignored
3528 // by older VirtualBox version, so needs no global config version bump either.
3529 // For hard disks this type is not accepted.
3530 if (mtype == MediumType_MultiAttach)
3531 {
3532 // This type is new with VirtualBox 4.0 and therefore requires settings
3533 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3534 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3535 // two reasons: The medium type is a property of the media registry tree, which
3536 // can reside in the global config file (for pre-4.0 media); we would therefore
3537 // possibly need to bump the global config version. We don't want to do that though
3538 // because that might make downgrading to pre-4.0 impossible.
3539 // As a result, we can only use these two new types if the medium is NOT in the
3540 // global registry:
3541 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3542 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3543 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3544 )
3545 return setError(VBOX_E_INVALID_OBJECT_STATE,
3546 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3547 "to machines that were created with VirtualBox 4.0 or later"),
3548 medium->i_getLocationFull().c_str());
3549 }
3550 }
3551
3552 bool fIndirect = false;
3553 if (!medium.isNull())
3554 fIndirect = medium->i_isReadOnly();
3555 bool associate = true;
3556
3557 do
3558 {
3559 if ( aType == DeviceType_HardDisk
3560 && mMediumAttachments.isBackedUp())
3561 {
3562 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3563
3564 /* check if the medium was attached to the VM before we started
3565 * changing attachments in which case the attachment just needs to
3566 * be restored */
3567 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3568 {
3569 AssertReturn(!fIndirect, E_FAIL);
3570
3571 /* see if it's the same bus/channel/device */
3572 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3573 {
3574 /* the simplest case: restore the whole attachment
3575 * and return, nothing else to do */
3576 mMediumAttachments->push_back(pAttachTemp);
3577
3578 /* Reattach the medium to the VM. */
3579 if (fHotplug || fSilent)
3580 {
3581 mediumLock.release();
3582 treeLock.release();
3583 alock.release();
3584
3585 MediumLockList *pMediumLockList(new MediumLockList());
3586
3587 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3588 medium /* pToLockWrite */,
3589 false /* fMediumLockWriteAll */,
3590 NULL,
3591 *pMediumLockList);
3592 alock.acquire();
3593 if (FAILED(rc))
3594 delete pMediumLockList;
3595 else
3596 {
3597 mData->mSession.mLockedMedia.Unlock();
3598 alock.release();
3599 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3600 mData->mSession.mLockedMedia.Lock();
3601 alock.acquire();
3602 }
3603 alock.release();
3604
3605 if (SUCCEEDED(rc))
3606 {
3607 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3608 /* Remove lock list in case of error. */
3609 if (FAILED(rc))
3610 {
3611 mData->mSession.mLockedMedia.Unlock();
3612 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3613 mData->mSession.mLockedMedia.Lock();
3614 }
3615 }
3616 }
3617
3618 return S_OK;
3619 }
3620
3621 /* bus/channel/device differ; we need a new attachment object,
3622 * but don't try to associate it again */
3623 associate = false;
3624 break;
3625 }
3626 }
3627
3628 /* go further only if the attachment is to be indirect */
3629 if (!fIndirect)
3630 break;
3631
3632 /* perform the so called smart attachment logic for indirect
3633 * attachments. Note that smart attachment is only applicable to base
3634 * hard disks. */
3635
3636 if (medium->i_getParent().isNull())
3637 {
3638 /* first, investigate the backup copy of the current hard disk
3639 * attachments to make it possible to re-attach existing diffs to
3640 * another device slot w/o losing their contents */
3641 if (mMediumAttachments.isBackedUp())
3642 {
3643 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3644
3645 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3646 uint32_t foundLevel = 0;
3647
3648 for (MediumAttachmentList::const_iterator
3649 it = oldAtts.begin();
3650 it != oldAtts.end();
3651 ++it)
3652 {
3653 uint32_t level = 0;
3654 MediumAttachment *pAttach = *it;
3655 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3656 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3657 if (pMedium.isNull())
3658 continue;
3659
3660 if (pMedium->i_getBase(&level) == medium)
3661 {
3662 /* skip the hard disk if its currently attached (we
3663 * cannot attach the same hard disk twice) */
3664 if (i_findAttachment(*mMediumAttachments.data(),
3665 pMedium))
3666 continue;
3667
3668 /* matched device, channel and bus (i.e. attached to the
3669 * same place) will win and immediately stop the search;
3670 * otherwise the attachment that has the youngest
3671 * descendant of medium will be used
3672 */
3673 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3674 {
3675 /* the simplest case: restore the whole attachment
3676 * and return, nothing else to do */
3677 mMediumAttachments->push_back(*it);
3678
3679 /* Reattach the medium to the VM. */
3680 if (fHotplug || fSilent)
3681 {
3682 mediumLock.release();
3683 treeLock.release();
3684 alock.release();
3685
3686 MediumLockList *pMediumLockList(new MediumLockList());
3687
3688 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3689 medium /* pToLockWrite */,
3690 false /* fMediumLockWriteAll */,
3691 NULL,
3692 *pMediumLockList);
3693 alock.acquire();
3694 if (FAILED(rc))
3695 delete pMediumLockList;
3696 else
3697 {
3698 mData->mSession.mLockedMedia.Unlock();
3699 alock.release();
3700 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3701 mData->mSession.mLockedMedia.Lock();
3702 alock.acquire();
3703 }
3704 alock.release();
3705
3706 if (SUCCEEDED(rc))
3707 {
3708 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3709 /* Remove lock list in case of error. */
3710 if (FAILED(rc))
3711 {
3712 mData->mSession.mLockedMedia.Unlock();
3713 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3714 mData->mSession.mLockedMedia.Lock();
3715 }
3716 }
3717 }
3718
3719 return S_OK;
3720 }
3721 else if ( foundIt == oldAtts.end()
3722 || level > foundLevel /* prefer younger */
3723 )
3724 {
3725 foundIt = it;
3726 foundLevel = level;
3727 }
3728 }
3729 }
3730
3731 if (foundIt != oldAtts.end())
3732 {
3733 /* use the previously attached hard disk */
3734 medium = (*foundIt)->i_getMedium();
3735 mediumCaller.attach(medium);
3736 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3737 mediumLock.attach(medium);
3738 /* not implicit, doesn't require association with this VM */
3739 fIndirect = false;
3740 associate = false;
3741 /* go right to the MediumAttachment creation */
3742 break;
3743 }
3744 }
3745
3746 /* must give up the medium lock and medium tree lock as below we
3747 * go over snapshots, which needs a lock with higher lock order. */
3748 mediumLock.release();
3749 treeLock.release();
3750
3751 /* then, search through snapshots for the best diff in the given
3752 * hard disk's chain to base the new diff on */
3753
3754 ComObjPtr<Medium> base;
3755 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3756 while (snap)
3757 {
3758 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3759
3760 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3761
3762 MediumAttachment *pAttachFound = NULL;
3763 uint32_t foundLevel = 0;
3764
3765 for (MediumAttachmentList::const_iterator
3766 it = snapAtts.begin();
3767 it != snapAtts.end();
3768 ++it)
3769 {
3770 MediumAttachment *pAttach = *it;
3771 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3772 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3773 if (pMedium.isNull())
3774 continue;
3775
3776 uint32_t level = 0;
3777 if (pMedium->i_getBase(&level) == medium)
3778 {
3779 /* matched device, channel and bus (i.e. attached to the
3780 * same place) will win and immediately stop the search;
3781 * otherwise the attachment that has the youngest
3782 * descendant of medium will be used
3783 */
3784 if ( pAttach->i_getDevice() == aDevice
3785 && pAttach->i_getPort() == aControllerPort
3786 && pAttach->i_getControllerName() == aName
3787 )
3788 {
3789 pAttachFound = pAttach;
3790 break;
3791 }
3792 else if ( !pAttachFound
3793 || level > foundLevel /* prefer younger */
3794 )
3795 {
3796 pAttachFound = pAttach;
3797 foundLevel = level;
3798 }
3799 }
3800 }
3801
3802 if (pAttachFound)
3803 {
3804 base = pAttachFound->i_getMedium();
3805 break;
3806 }
3807
3808 snap = snap->i_getParent();
3809 }
3810
3811 /* re-lock medium tree and the medium, as we need it below */
3812 treeLock.acquire();
3813 mediumLock.acquire();
3814
3815 /* found a suitable diff, use it as a base */
3816 if (!base.isNull())
3817 {
3818 medium = base;
3819 mediumCaller.attach(medium);
3820 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3821 mediumLock.attach(medium);
3822 }
3823 }
3824
3825 Utf8Str strFullSnapshotFolder;
3826 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3827
3828 ComObjPtr<Medium> diff;
3829 diff.createObject();
3830 // store this diff in the same registry as the parent
3831 Guid uuidRegistryParent;
3832 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3833 {
3834 // parent image has no registry: this can happen if we're attaching a new immutable
3835 // image that has not yet been attached (medium then points to the base and we're
3836 // creating the diff image for the immutable, and the parent is not yet registered);
3837 // put the parent in the machine registry then
3838 mediumLock.release();
3839 treeLock.release();
3840 alock.release();
3841 i_addMediumToRegistry(medium);
3842 alock.acquire();
3843 treeLock.acquire();
3844 mediumLock.acquire();
3845 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3846 }
3847 rc = diff->init(mParent,
3848 medium->i_getPreferredDiffFormat(),
3849 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3850 uuidRegistryParent,
3851 DeviceType_HardDisk);
3852 if (FAILED(rc)) return rc;
3853
3854 /* Apply the normal locking logic to the entire chain. */
3855 MediumLockList *pMediumLockList(new MediumLockList());
3856 mediumLock.release();
3857 treeLock.release();
3858 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3859 diff /* pToLockWrite */,
3860 false /* fMediumLockWriteAll */,
3861 medium,
3862 *pMediumLockList);
3863 treeLock.acquire();
3864 mediumLock.acquire();
3865 if (SUCCEEDED(rc))
3866 {
3867 mediumLock.release();
3868 treeLock.release();
3869 rc = pMediumLockList->Lock();
3870 treeLock.acquire();
3871 mediumLock.acquire();
3872 if (FAILED(rc))
3873 setError(rc,
3874 tr("Could not lock medium when creating diff '%s'"),
3875 diff->i_getLocationFull().c_str());
3876 else
3877 {
3878 /* will release the lock before the potentially lengthy
3879 * operation, so protect with the special state */
3880 MachineState_T oldState = mData->mMachineState;
3881 i_setMachineState(MachineState_SettingUp);
3882
3883 mediumLock.release();
3884 treeLock.release();
3885 alock.release();
3886
3887 rc = medium->i_createDiffStorage(diff,
3888 medium->i_getPreferredDiffVariant(),
3889 pMediumLockList,
3890 NULL /* aProgress */,
3891 true /* aWait */,
3892 false /* aNotify */);
3893
3894 alock.acquire();
3895 treeLock.acquire();
3896 mediumLock.acquire();
3897
3898 i_setMachineState(oldState);
3899 }
3900 }
3901
3902 /* Unlock the media and free the associated memory. */
3903 delete pMediumLockList;
3904
3905 if (FAILED(rc)) return rc;
3906
3907 /* use the created diff for the actual attachment */
3908 medium = diff;
3909 mediumCaller.attach(medium);
3910 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3911 mediumLock.attach(medium);
3912 }
3913 while (0);
3914
3915 ComObjPtr<MediumAttachment> attachment;
3916 attachment.createObject();
3917 rc = attachment->init(this,
3918 medium,
3919 aName,
3920 aControllerPort,
3921 aDevice,
3922 aType,
3923 fIndirect,
3924 false /* fPassthrough */,
3925 false /* fTempEject */,
3926 false /* fNonRotational */,
3927 false /* fDiscard */,
3928 fHotplug /* fHotPluggable */,
3929 Utf8Str::Empty);
3930 if (FAILED(rc)) return rc;
3931
3932 if (associate && !medium.isNull())
3933 {
3934 // as the last step, associate the medium to the VM
3935 rc = medium->i_addBackReference(mData->mUuid);
3936 // here we can fail because of Deleting, or being in process of creating a Diff
3937 if (FAILED(rc)) return rc;
3938
3939 mediumLock.release();
3940 treeLock.release();
3941 alock.release();
3942 i_addMediumToRegistry(medium);
3943 alock.acquire();
3944 treeLock.acquire();
3945 mediumLock.acquire();
3946 }
3947
3948 /* success: finally remember the attachment */
3949 i_setModified(IsModified_Storage);
3950 mMediumAttachments.backup();
3951 mMediumAttachments->push_back(attachment);
3952
3953 mediumLock.release();
3954 treeLock.release();
3955 alock.release();
3956
3957 if (fHotplug || fSilent)
3958 {
3959 if (!medium.isNull())
3960 {
3961 MediumLockList *pMediumLockList(new MediumLockList());
3962
3963 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3964 medium /* pToLockWrite */,
3965 false /* fMediumLockWriteAll */,
3966 NULL,
3967 *pMediumLockList);
3968 alock.acquire();
3969 if (FAILED(rc))
3970 delete pMediumLockList;
3971 else
3972 {
3973 mData->mSession.mLockedMedia.Unlock();
3974 alock.release();
3975 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3976 mData->mSession.mLockedMedia.Lock();
3977 alock.acquire();
3978 }
3979 alock.release();
3980 }
3981
3982 if (SUCCEEDED(rc))
3983 {
3984 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3985 /* Remove lock list in case of error. */
3986 if (FAILED(rc))
3987 {
3988 mData->mSession.mLockedMedia.Unlock();
3989 mData->mSession.mLockedMedia.Remove(attachment);
3990 mData->mSession.mLockedMedia.Lock();
3991 }
3992 }
3993 }
3994
3995 /* Save modified registries, but skip this machine as it's the caller's
3996 * job to save its settings like all other settings changes. */
3997 mParent->i_unmarkRegistryModified(i_getId());
3998 mParent->i_saveModifiedRegistries();
3999
4000 if (SUCCEEDED(rc))
4001 {
4002 if (fIndirect && medium != aM)
4003 mParent->i_onMediumConfigChanged(medium);
4004 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4005 }
4006
4007 return rc;
4008}
4009
4010HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4011 LONG aDevice)
4012{
4013 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4014 aName.c_str(), aControllerPort, aDevice));
4015
4016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4017
4018 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4019 if (FAILED(rc)) return rc;
4020
4021 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4022
4023 /* Check for an existing controller. */
4024 ComObjPtr<StorageController> ctl;
4025 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4026 if (FAILED(rc)) return rc;
4027
4028 StorageControllerType_T ctrlType;
4029 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4030 if (FAILED(rc))
4031 return setError(E_FAIL,
4032 tr("Could not get type of controller '%s'"),
4033 aName.c_str());
4034
4035 bool fSilent = false;
4036 Utf8Str strReconfig;
4037
4038 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4039 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4040 if ( mData->mMachineState == MachineState_Paused
4041 && strReconfig == "1")
4042 fSilent = true;
4043
4044 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4045 bool fHotplug = false;
4046 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4047 fHotplug = true;
4048
4049 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4050 return setError(VBOX_E_INVALID_VM_STATE,
4051 tr("Controller '%s' does not support hotplugging"),
4052 aName.c_str());
4053
4054 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4055 aName,
4056 aControllerPort,
4057 aDevice);
4058 if (!pAttach)
4059 return setError(VBOX_E_OBJECT_NOT_FOUND,
4060 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4061 aDevice, aControllerPort, aName.c_str());
4062
4063 if (fHotplug && !pAttach->i_getHotPluggable())
4064 return setError(VBOX_E_NOT_SUPPORTED,
4065 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4066 aDevice, aControllerPort, aName.c_str());
4067
4068 /*
4069 * The VM has to detach the device before we delete any implicit diffs.
4070 * If this fails we can roll back without loosing data.
4071 */
4072 if (fHotplug || fSilent)
4073 {
4074 alock.release();
4075 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4076 alock.acquire();
4077 }
4078 if (FAILED(rc)) return rc;
4079
4080 /* If we are here everything went well and we can delete the implicit now. */
4081 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4082
4083 alock.release();
4084
4085 /* Save modified registries, but skip this machine as it's the caller's
4086 * job to save its settings like all other settings changes. */
4087 mParent->i_unmarkRegistryModified(i_getId());
4088 mParent->i_saveModifiedRegistries();
4089
4090 if (SUCCEEDED(rc))
4091 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4092
4093 return rc;
4094}
4095
4096HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4097 LONG aDevice, BOOL aPassthrough)
4098{
4099 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4100 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4101
4102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4103
4104 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4105 if (FAILED(rc)) return rc;
4106
4107 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4108
4109 /* Check for an existing controller. */
4110 ComObjPtr<StorageController> ctl;
4111 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4112 if (FAILED(rc)) return rc;
4113
4114 StorageControllerType_T ctrlType;
4115 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4116 if (FAILED(rc))
4117 return setError(E_FAIL,
4118 tr("Could not get type of controller '%s'"),
4119 aName.c_str());
4120
4121 bool fSilent = false;
4122 Utf8Str strReconfig;
4123
4124 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4125 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4126 if ( mData->mMachineState == MachineState_Paused
4127 && strReconfig == "1")
4128 fSilent = true;
4129
4130 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4131 bool fHotplug = false;
4132 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4133 fHotplug = true;
4134
4135 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4136 return setError(VBOX_E_INVALID_VM_STATE,
4137 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4138 aName.c_str());
4139
4140 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4141 aName,
4142 aControllerPort,
4143 aDevice);
4144 if (!pAttach)
4145 return setError(VBOX_E_OBJECT_NOT_FOUND,
4146 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4147 aDevice, aControllerPort, aName.c_str());
4148
4149
4150 i_setModified(IsModified_Storage);
4151 mMediumAttachments.backup();
4152
4153 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4154
4155 if (pAttach->i_getType() != DeviceType_DVD)
4156 return setError(E_INVALIDARG,
4157 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4158 aDevice, aControllerPort, aName.c_str());
4159
4160 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4161
4162 pAttach->i_updatePassthrough(!!aPassthrough);
4163
4164 attLock.release();
4165 alock.release();
4166 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4167 if (SUCCEEDED(rc) && fValueChanged)
4168 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4169
4170 return rc;
4171}
4172
4173HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4174 LONG aDevice, BOOL aTemporaryEject)
4175{
4176
4177 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4178 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4179
4180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4181
4182 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4183 if (FAILED(rc)) return rc;
4184
4185 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4186 aName,
4187 aControllerPort,
4188 aDevice);
4189 if (!pAttach)
4190 return setError(VBOX_E_OBJECT_NOT_FOUND,
4191 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4192 aDevice, aControllerPort, aName.c_str());
4193
4194
4195 i_setModified(IsModified_Storage);
4196 mMediumAttachments.backup();
4197
4198 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4199
4200 if (pAttach->i_getType() != DeviceType_DVD)
4201 return setError(E_INVALIDARG,
4202 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4203 aDevice, aControllerPort, aName.c_str());
4204 pAttach->i_updateTempEject(!!aTemporaryEject);
4205
4206 return S_OK;
4207}
4208
4209HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4210 LONG aDevice, BOOL aNonRotational)
4211{
4212
4213 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4214 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4215
4216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4217
4218 HRESULT rc = i_checkStateDependency(MutableStateDep);
4219 if (FAILED(rc)) return rc;
4220
4221 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4222
4223 if (Global::IsOnlineOrTransient(mData->mMachineState))
4224 return setError(VBOX_E_INVALID_VM_STATE,
4225 tr("Invalid machine state: %s"),
4226 Global::stringifyMachineState(mData->mMachineState));
4227
4228 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4229 aName,
4230 aControllerPort,
4231 aDevice);
4232 if (!pAttach)
4233 return setError(VBOX_E_OBJECT_NOT_FOUND,
4234 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4235 aDevice, aControllerPort, aName.c_str());
4236
4237
4238 i_setModified(IsModified_Storage);
4239 mMediumAttachments.backup();
4240
4241 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4242
4243 if (pAttach->i_getType() != DeviceType_HardDisk)
4244 return setError(E_INVALIDARG,
4245 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"),
4246 aDevice, aControllerPort, aName.c_str());
4247 pAttach->i_updateNonRotational(!!aNonRotational);
4248
4249 return S_OK;
4250}
4251
4252HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4253 LONG aDevice, BOOL aDiscard)
4254{
4255
4256 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4257 aName.c_str(), aControllerPort, aDevice, aDiscard));
4258
4259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4260
4261 HRESULT rc = i_checkStateDependency(MutableStateDep);
4262 if (FAILED(rc)) return rc;
4263
4264 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4265
4266 if (Global::IsOnlineOrTransient(mData->mMachineState))
4267 return setError(VBOX_E_INVALID_VM_STATE,
4268 tr("Invalid machine state: %s"),
4269 Global::stringifyMachineState(mData->mMachineState));
4270
4271 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4272 aName,
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4278 aDevice, aControllerPort, aName.c_str());
4279
4280
4281 i_setModified(IsModified_Storage);
4282 mMediumAttachments.backup();
4283
4284 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4285
4286 if (pAttach->i_getType() != DeviceType_HardDisk)
4287 return setError(E_INVALIDARG,
4288 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"),
4289 aDevice, aControllerPort, aName.c_str());
4290 pAttach->i_updateDiscard(!!aDiscard);
4291
4292 return S_OK;
4293}
4294
4295HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4296 LONG aDevice, BOOL aHotPluggable)
4297{
4298 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4299 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4300
4301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4302
4303 HRESULT rc = i_checkStateDependency(MutableStateDep);
4304 if (FAILED(rc)) return rc;
4305
4306 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4307
4308 if (Global::IsOnlineOrTransient(mData->mMachineState))
4309 return setError(VBOX_E_INVALID_VM_STATE,
4310 tr("Invalid machine state: %s"),
4311 Global::stringifyMachineState(mData->mMachineState));
4312
4313 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4314 aName,
4315 aControllerPort,
4316 aDevice);
4317 if (!pAttach)
4318 return setError(VBOX_E_OBJECT_NOT_FOUND,
4319 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4320 aDevice, aControllerPort, aName.c_str());
4321
4322 /* Check for an existing controller. */
4323 ComObjPtr<StorageController> ctl;
4324 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4325 if (FAILED(rc)) return rc;
4326
4327 StorageControllerType_T ctrlType;
4328 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4329 if (FAILED(rc))
4330 return setError(E_FAIL,
4331 tr("Could not get type of controller '%s'"),
4332 aName.c_str());
4333
4334 if (!i_isControllerHotplugCapable(ctrlType))
4335 return setError(VBOX_E_NOT_SUPPORTED,
4336 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4337 aName.c_str());
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_Floppy)
4345 return setError(E_INVALIDARG,
4346 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"),
4347 aDevice, aControllerPort, aName.c_str());
4348 pAttach->i_updateHotPluggable(!!aHotPluggable);
4349
4350 return S_OK;
4351}
4352
4353HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4354 LONG aDevice)
4355{
4356 int rc = S_OK;
4357 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4358 aName.c_str(), aControllerPort, aDevice));
4359
4360 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4361
4362 return rc;
4363}
4364
4365HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4366 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4367{
4368 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4369 aName.c_str(), aControllerPort, aDevice));
4370
4371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4374 if (FAILED(rc)) return rc;
4375
4376 if (Global::IsOnlineOrTransient(mData->mMachineState))
4377 return setError(VBOX_E_INVALID_VM_STATE,
4378 tr("Invalid machine state: %s"),
4379 Global::stringifyMachineState(mData->mMachineState));
4380
4381 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4382 aName,
4383 aControllerPort,
4384 aDevice);
4385 if (!pAttach)
4386 return setError(VBOX_E_OBJECT_NOT_FOUND,
4387 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4388 aDevice, aControllerPort, aName.c_str());
4389
4390
4391 i_setModified(IsModified_Storage);
4392 mMediumAttachments.backup();
4393
4394 IBandwidthGroup *iB = aBandwidthGroup;
4395 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4396 if (aBandwidthGroup && group.isNull())
4397 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4398
4399 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4400
4401 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4402 if (strBandwidthGroupOld.isNotEmpty())
4403 {
4404 /* Get the bandwidth group object and release it - this must not fail. */
4405 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4406 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4407 Assert(SUCCEEDED(rc));
4408
4409 pBandwidthGroupOld->i_release();
4410 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4411 }
4412
4413 if (!group.isNull())
4414 {
4415 group->i_reference();
4416 pAttach->i_updateBandwidthGroup(group->i_getName());
4417 }
4418
4419 return S_OK;
4420}
4421
4422HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4423 LONG aControllerPort,
4424 LONG aDevice,
4425 DeviceType_T aType)
4426{
4427 HRESULT rc = S_OK;
4428
4429 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4430 aName.c_str(), aControllerPort, aDevice, aType));
4431
4432 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4433
4434 return rc;
4435}
4436
4437
4438HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4439 LONG aControllerPort,
4440 LONG aDevice,
4441 BOOL aForce)
4442{
4443 int rc = S_OK;
4444 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4445 aName.c_str(), aControllerPort, aForce));
4446
4447 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4448
4449 return rc;
4450}
4451
4452HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4453 LONG aControllerPort,
4454 LONG aDevice,
4455 const ComPtr<IMedium> &aMedium,
4456 BOOL aForce)
4457{
4458 int rc = S_OK;
4459 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4460 aName.c_str(), aControllerPort, aDevice, aForce));
4461
4462 // request the host lock first, since might be calling Host methods for getting host drives;
4463 // next, protect the media tree all the while we're in here, as well as our member variables
4464 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4465 this->lockHandle(),
4466 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4467
4468 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4469 aName,
4470 aControllerPort,
4471 aDevice);
4472 if (pAttach.isNull())
4473 return setError(VBOX_E_OBJECT_NOT_FOUND,
4474 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4475 aDevice, aControllerPort, aName.c_str());
4476
4477 /* Remember previously mounted medium. The medium before taking the
4478 * backup is not necessarily the same thing. */
4479 ComObjPtr<Medium> oldmedium;
4480 oldmedium = pAttach->i_getMedium();
4481
4482 IMedium *iM = aMedium;
4483 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4484 if (aMedium && pMedium.isNull())
4485 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4486
4487 AutoCaller mediumCaller(pMedium);
4488 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4489
4490 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4491 if (pMedium)
4492 {
4493 DeviceType_T mediumType = pAttach->i_getType();
4494 switch (mediumType)
4495 {
4496 case DeviceType_DVD:
4497 case DeviceType_Floppy:
4498 break;
4499
4500 default:
4501 return setError(VBOX_E_INVALID_OBJECT_STATE,
4502 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4503 aControllerPort,
4504 aDevice,
4505 aName.c_str());
4506 }
4507 }
4508
4509 i_setModified(IsModified_Storage);
4510 mMediumAttachments.backup();
4511
4512 {
4513 // The backup operation makes the pAttach reference point to the
4514 // old settings. Re-get the correct reference.
4515 pAttach = i_findAttachment(*mMediumAttachments.data(),
4516 aName,
4517 aControllerPort,
4518 aDevice);
4519 if (!oldmedium.isNull())
4520 oldmedium->i_removeBackReference(mData->mUuid);
4521 if (!pMedium.isNull())
4522 {
4523 pMedium->i_addBackReference(mData->mUuid);
4524
4525 mediumLock.release();
4526 multiLock.release();
4527 i_addMediumToRegistry(pMedium);
4528 multiLock.acquire();
4529 mediumLock.acquire();
4530 }
4531
4532 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4533 pAttach->i_updateMedium(pMedium);
4534 }
4535
4536 i_setModified(IsModified_Storage);
4537
4538 mediumLock.release();
4539 multiLock.release();
4540 rc = i_onMediumChange(pAttach, aForce);
4541 multiLock.acquire();
4542 mediumLock.acquire();
4543
4544 /* On error roll back this change only. */
4545 if (FAILED(rc))
4546 {
4547 if (!pMedium.isNull())
4548 pMedium->i_removeBackReference(mData->mUuid);
4549 pAttach = i_findAttachment(*mMediumAttachments.data(),
4550 aName,
4551 aControllerPort,
4552 aDevice);
4553 /* If the attachment is gone in the meantime, bail out. */
4554 if (pAttach.isNull())
4555 return rc;
4556 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4557 if (!oldmedium.isNull())
4558 oldmedium->i_addBackReference(mData->mUuid);
4559 pAttach->i_updateMedium(oldmedium);
4560 }
4561
4562 mediumLock.release();
4563 multiLock.release();
4564
4565 /* Save modified registries, but skip this machine as it's the caller's
4566 * job to save its settings like all other settings changes. */
4567 mParent->i_unmarkRegistryModified(i_getId());
4568 mParent->i_saveModifiedRegistries();
4569
4570 return rc;
4571}
4572HRESULT Machine::getMedium(const com::Utf8Str &aName,
4573 LONG aControllerPort,
4574 LONG aDevice,
4575 ComPtr<IMedium> &aMedium)
4576{
4577 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4578 aName.c_str(), aControllerPort, aDevice));
4579
4580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4581
4582 aMedium = NULL;
4583
4584 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4585 aName,
4586 aControllerPort,
4587 aDevice);
4588 if (pAttach.isNull())
4589 return setError(VBOX_E_OBJECT_NOT_FOUND,
4590 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4591 aDevice, aControllerPort, aName.c_str());
4592
4593 aMedium = pAttach->i_getMedium();
4594
4595 return S_OK;
4596}
4597
4598HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4599{
4600
4601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4602
4603 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4604
4605 return S_OK;
4606}
4607
4608HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4609{
4610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4611
4612 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4613
4614 return S_OK;
4615}
4616
4617HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4618{
4619 /* Do not assert if slot is out of range, just return the advertised
4620 status. testdriver/vbox.py triggers this in logVmInfo. */
4621 if (aSlot >= mNetworkAdapters.size())
4622 return setError(E_INVALIDARG,
4623 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4624 aSlot, mNetworkAdapters.size());
4625
4626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4627
4628 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4629
4630 return S_OK;
4631}
4632
4633HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4634{
4635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4636
4637 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4638 size_t i = 0;
4639 for (settings::StringsMap::const_iterator
4640 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4641 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4642 ++it, ++i)
4643 aKeys[i] = it->first;
4644
4645 return S_OK;
4646}
4647
4648 /**
4649 * @note Locks this object for reading.
4650 */
4651HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4652 com::Utf8Str &aValue)
4653{
4654 /* start with nothing found */
4655 aValue = "";
4656
4657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4658
4659 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4660 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4661 // found:
4662 aValue = it->second; // source is a Utf8Str
4663
4664 /* return the result to caller (may be empty) */
4665 return S_OK;
4666}
4667
4668 /**
4669 * @note Locks mParent for writing + this object for writing.
4670 */
4671HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4672{
4673 /* Because control characters in aKey have caused problems in the settings
4674 * they are rejected unless the key should be deleted. */
4675 if (!aValue.isEmpty())
4676 {
4677 for (size_t i = 0; i < aKey.length(); ++i)
4678 {
4679 char ch = aKey[i];
4680 if (RTLocCIsCntrl(ch))
4681 return E_INVALIDARG;
4682 }
4683 }
4684
4685 Utf8Str strOldValue; // empty
4686
4687 // locking note: we only hold the read lock briefly to look up the old value,
4688 // then release it and call the onExtraCanChange callbacks. There is a small
4689 // chance of a race insofar as the callback might be called twice if two callers
4690 // change the same key at the same time, but that's a much better solution
4691 // than the deadlock we had here before. The actual changing of the extradata
4692 // is then performed under the write lock and race-free.
4693
4694 // look up the old value first; if nothing has changed then we need not do anything
4695 {
4696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4697
4698 // For snapshots don't even think about allowing changes, extradata
4699 // is global for a machine, so there is nothing snapshot specific.
4700 if (i_isSnapshotMachine())
4701 return setError(VBOX_E_INVALID_VM_STATE,
4702 tr("Cannot set extradata for a snapshot"));
4703
4704 // check if the right IMachine instance is used
4705 if (mData->mRegistered && !i_isSessionMachine())
4706 return setError(VBOX_E_INVALID_VM_STATE,
4707 tr("Cannot set extradata for an immutable machine"));
4708
4709 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4710 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4711 strOldValue = it->second;
4712 }
4713
4714 bool fChanged;
4715 if ((fChanged = (strOldValue != aValue)))
4716 {
4717 // ask for permission from all listeners outside the locks;
4718 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4719 // lock to copy the list of callbacks to invoke
4720 Bstr error;
4721 Bstr bstrValue(aValue);
4722
4723 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4724 {
4725 const char *sep = error.isEmpty() ? "" : ": ";
4726 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4727 return setError(E_ACCESSDENIED,
4728 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4729 aKey.c_str(),
4730 aValue.c_str(),
4731 sep,
4732 error.raw());
4733 }
4734
4735 // data is changing and change not vetoed: then write it out under the lock
4736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4737
4738 if (aValue.isEmpty())
4739 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4740 else
4741 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4742 // creates a new key if needed
4743
4744 bool fNeedsGlobalSaveSettings = false;
4745 // This saving of settings is tricky: there is no "old state" for the
4746 // extradata items at all (unlike all other settings), so the old/new
4747 // settings comparison would give a wrong result!
4748 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4749
4750 if (fNeedsGlobalSaveSettings)
4751 {
4752 // save the global settings; for that we should hold only the VirtualBox lock
4753 alock.release();
4754 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4755 mParent->i_saveSettings();
4756 }
4757 }
4758
4759 // fire notification outside the lock
4760 if (fChanged)
4761 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4762
4763 return S_OK;
4764}
4765
4766HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4767{
4768 aProgress = NULL;
4769 NOREF(aSettingsFilePath);
4770 ReturnComNotImplemented();
4771}
4772
4773HRESULT Machine::saveSettings()
4774{
4775 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4776
4777 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4778 if (FAILED(rc)) return rc;
4779
4780 /* the settings file path may never be null */
4781 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4782
4783 /* save all VM data excluding snapshots */
4784 bool fNeedsGlobalSaveSettings = false;
4785 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4786 mlock.release();
4787
4788 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4789 {
4790 // save the global settings; for that we should hold only the VirtualBox lock
4791 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4792 rc = mParent->i_saveSettings();
4793 }
4794
4795 return rc;
4796}
4797
4798
4799HRESULT Machine::discardSettings()
4800{
4801 /*
4802 * We need to take the machine list lock here as well as the machine one
4803 * or we'll get into trouble should any media stuff require rolling back.
4804 *
4805 * Details:
4806 *
4807 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4808 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4809 * 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]
4810 * 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
4811 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4812 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4813 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4814 * 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
4815 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4816 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4817 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4818 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4819 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4820 * 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]
4821 * 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] (*)
4822 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4823 * 0:005> k
4824 * # Child-SP RetAddr Call Site
4825 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4826 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4827 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4828 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4829 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4830 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4831 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4832 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4833 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4834 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4835 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4836 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4837 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4838 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4839 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4840 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4841 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4842 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4843 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4844 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4845 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4846 *
4847 */
4848 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4850
4851 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4852 if (FAILED(rc)) return rc;
4853
4854 /*
4855 * during this rollback, the session will be notified if data has
4856 * been actually changed
4857 */
4858 i_rollback(true /* aNotify */);
4859
4860 return S_OK;
4861}
4862
4863/** @note Locks objects! */
4864HRESULT Machine::unregister(AutoCaller &autoCaller,
4865 CleanupMode_T aCleanupMode,
4866 std::vector<ComPtr<IMedium> > &aMedia)
4867{
4868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4869
4870 Guid id(i_getId());
4871
4872 if (mData->mSession.mState != SessionState_Unlocked)
4873 return setError(VBOX_E_INVALID_OBJECT_STATE,
4874 tr("Cannot unregister the machine '%s' while it is locked"),
4875 mUserData->s.strName.c_str());
4876
4877 // wait for state dependents to drop to zero
4878 i_ensureNoStateDependencies();
4879
4880 if (!mData->mAccessible)
4881 {
4882 // inaccessible machines can only be unregistered; uninitialize ourselves
4883 // here because currently there may be no unregistered that are inaccessible
4884 // (this state combination is not supported). Note releasing the caller and
4885 // leaving the lock before calling uninit()
4886 alock.release();
4887 autoCaller.release();
4888
4889 uninit();
4890
4891 mParent->i_unregisterMachine(this, id);
4892 // calls VirtualBox::i_saveSettings()
4893
4894 return S_OK;
4895 }
4896
4897 HRESULT rc = S_OK;
4898 mData->llFilesToDelete.clear();
4899
4900 if (!mSSData->strStateFilePath.isEmpty())
4901 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4902
4903 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4904 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4905 mData->llFilesToDelete.push_back(strNVRAMFile);
4906
4907 // This list collects the medium objects from all medium attachments
4908 // which we will detach from the machine and its snapshots, in a specific
4909 // order which allows for closing all media without getting "media in use"
4910 // errors, simply by going through the list from the front to the back:
4911 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4912 // and must be closed before the parent media from the snapshots, or closing the parents
4913 // will fail because they still have children);
4914 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4915 // the root ("first") snapshot of the machine.
4916 MediaList llMedia;
4917
4918 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4919 && mMediumAttachments->size()
4920 )
4921 {
4922 // we have media attachments: detach them all and add the Medium objects to our list
4923 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4924 }
4925
4926 if (mData->mFirstSnapshot)
4927 {
4928 // add the media from the medium attachments of the snapshots to llMedia
4929 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4930 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4931 // into the children first
4932
4933 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4934 MachineState_T oldState = mData->mMachineState;
4935 mData->mMachineState = MachineState_DeletingSnapshot;
4936
4937 // make a copy of the first snapshot reference so the refcount does not
4938 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4939 // (would hang due to the AutoCaller voodoo)
4940 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4941
4942 // GO!
4943 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4944
4945 mData->mMachineState = oldState;
4946 }
4947
4948 if (FAILED(rc))
4949 {
4950 i_rollbackMedia();
4951 return rc;
4952 }
4953
4954 // commit all the media changes made above
4955 i_commitMedia();
4956
4957 mData->mRegistered = false;
4958
4959 // machine lock no longer needed
4960 alock.release();
4961
4962 /* Make sure that the settings of the current VM are not saved, because
4963 * they are rather crippled at this point to meet the cleanup expectations
4964 * and there's no point destroying the VM config on disk just because. */
4965 mParent->i_unmarkRegistryModified(id);
4966
4967 // return media to caller
4968 aMedia.resize(llMedia.size());
4969 size_t i = 0;
4970 for (MediaList::const_iterator
4971 it = llMedia.begin();
4972 it != llMedia.end();
4973 ++it, ++i)
4974 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4975
4976 mParent->i_unregisterMachine(this, id);
4977 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4978
4979 return S_OK;
4980}
4981
4982/**
4983 * Task record for deleting a machine config.
4984 */
4985class Machine::DeleteConfigTask
4986 : public Machine::Task
4987{
4988public:
4989 DeleteConfigTask(Machine *m,
4990 Progress *p,
4991 const Utf8Str &t,
4992 const RTCList<ComPtr<IMedium> > &llMediums,
4993 const StringsList &llFilesToDelete)
4994 : Task(m, p, t),
4995 m_llMediums(llMediums),
4996 m_llFilesToDelete(llFilesToDelete)
4997 {}
4998
4999private:
5000 void handler()
5001 {
5002 try
5003 {
5004 m_pMachine->i_deleteConfigHandler(*this);
5005 }
5006 catch (...)
5007 {
5008 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5009 }
5010 }
5011
5012 RTCList<ComPtr<IMedium> > m_llMediums;
5013 StringsList m_llFilesToDelete;
5014
5015 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5016};
5017
5018/**
5019 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5020 * SessionMachine::taskHandler().
5021 *
5022 * @note Locks this object for writing.
5023 *
5024 * @param task
5025 * @return
5026 */
5027void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5028{
5029 LogFlowThisFuncEnter();
5030
5031 AutoCaller autoCaller(this);
5032 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5033 if (FAILED(autoCaller.rc()))
5034 {
5035 /* we might have been uninitialized because the session was accidentally
5036 * closed by the client, so don't assert */
5037 HRESULT rc = setError(E_FAIL,
5038 tr("The session has been accidentally closed"));
5039 task.m_pProgress->i_notifyComplete(rc);
5040 LogFlowThisFuncLeave();
5041 return;
5042 }
5043
5044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5045
5046 HRESULT rc = S_OK;
5047
5048 try
5049 {
5050 ULONG uLogHistoryCount = 3;
5051 ComPtr<ISystemProperties> systemProperties;
5052 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5053 if (FAILED(rc)) throw rc;
5054
5055 if (!systemProperties.isNull())
5056 {
5057 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5058 if (FAILED(rc)) throw rc;
5059 }
5060
5061 MachineState_T oldState = mData->mMachineState;
5062 i_setMachineState(MachineState_SettingUp);
5063 alock.release();
5064 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5065 {
5066 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5067 {
5068 AutoCaller mac(pMedium);
5069 if (FAILED(mac.rc())) throw mac.rc();
5070 Utf8Str strLocation = pMedium->i_getLocationFull();
5071 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5072 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5073 if (FAILED(rc)) throw rc;
5074 }
5075 if (pMedium->i_isMediumFormatFile())
5076 {
5077 ComPtr<IProgress> pProgress2;
5078 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5079 if (FAILED(rc)) throw rc;
5080 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5081 if (FAILED(rc)) throw rc;
5082 }
5083
5084 /* Close the medium, deliberately without checking the return
5085 * code, and without leaving any trace in the error info, as
5086 * a failure here is a very minor issue, which shouldn't happen
5087 * as above we even managed to delete the medium. */
5088 {
5089 ErrorInfoKeeper eik;
5090 pMedium->Close();
5091 }
5092 }
5093 i_setMachineState(oldState);
5094 alock.acquire();
5095
5096 // delete the files pushed on the task list by Machine::Delete()
5097 // (this includes saved states of the machine and snapshots and
5098 // medium storage files from the IMedium list passed in, and the
5099 // machine XML file)
5100 for (StringsList::const_iterator
5101 it = task.m_llFilesToDelete.begin();
5102 it != task.m_llFilesToDelete.end();
5103 ++it)
5104 {
5105 const Utf8Str &strFile = *it;
5106 LogFunc(("Deleting file %s\n", strFile.c_str()));
5107 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5108 if (FAILED(rc)) throw rc;
5109
5110 int vrc = RTFileDelete(strFile.c_str());
5111 if (RT_FAILURE(vrc))
5112 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5113 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5114 }
5115
5116 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5117 if (FAILED(rc)) throw rc;
5118
5119 /* delete the settings only when the file actually exists */
5120 if (mData->pMachineConfigFile->fileExists())
5121 {
5122 /* Delete any backup or uncommitted XML files. Ignore failures.
5123 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5124 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5125 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5126 RTFileDelete(otherXml.c_str());
5127 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5128 RTFileDelete(otherXml.c_str());
5129
5130 /* delete the Logs folder, nothing important should be left
5131 * there (we don't check for errors because the user might have
5132 * some private files there that we don't want to delete) */
5133 Utf8Str logFolder;
5134 getLogFolder(logFolder);
5135 Assert(logFolder.length());
5136 if (RTDirExists(logFolder.c_str()))
5137 {
5138 /* Delete all VBox.log[.N] files from the Logs folder
5139 * (this must be in sync with the rotation logic in
5140 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5141 * files that may have been created by the GUI. */
5142 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5143 logFolder.c_str(), RTPATH_DELIMITER);
5144 RTFileDelete(log.c_str());
5145 log = Utf8StrFmt("%s%cVBox.png",
5146 logFolder.c_str(), RTPATH_DELIMITER);
5147 RTFileDelete(log.c_str());
5148 for (int i = uLogHistoryCount; i > 0; i--)
5149 {
5150 log = Utf8StrFmt("%s%cVBox.log.%d",
5151 logFolder.c_str(), RTPATH_DELIMITER, i);
5152 RTFileDelete(log.c_str());
5153 log = Utf8StrFmt("%s%cVBox.png.%d",
5154 logFolder.c_str(), RTPATH_DELIMITER, i);
5155 RTFileDelete(log.c_str());
5156 }
5157#if defined(RT_OS_WINDOWS)
5158 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5159 RTFileDelete(log.c_str());
5160 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5161 RTFileDelete(log.c_str());
5162#endif
5163
5164 RTDirRemove(logFolder.c_str());
5165 }
5166
5167 /* delete the Snapshots folder, nothing important should be left
5168 * there (we don't check for errors because the user might have
5169 * some private files there that we don't want to delete) */
5170 Utf8Str strFullSnapshotFolder;
5171 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5172 Assert(!strFullSnapshotFolder.isEmpty());
5173 if (RTDirExists(strFullSnapshotFolder.c_str()))
5174 RTDirRemove(strFullSnapshotFolder.c_str());
5175
5176 // delete the directory that contains the settings file, but only
5177 // if it matches the VM name
5178 Utf8Str settingsDir;
5179 if (i_isInOwnDir(&settingsDir))
5180 RTDirRemove(settingsDir.c_str());
5181 }
5182
5183 alock.release();
5184
5185 mParent->i_saveModifiedRegistries();
5186 }
5187 catch (HRESULT aRC) { rc = aRC; }
5188
5189 task.m_pProgress->i_notifyComplete(rc);
5190
5191 LogFlowThisFuncLeave();
5192}
5193
5194HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5195{
5196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5197
5198 HRESULT rc = i_checkStateDependency(MutableStateDep);
5199 if (FAILED(rc)) return rc;
5200
5201 if (mData->mRegistered)
5202 return setError(VBOX_E_INVALID_VM_STATE,
5203 tr("Cannot delete settings of a registered machine"));
5204
5205 // collect files to delete
5206 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5207 // machine config file
5208 if (mData->pMachineConfigFile->fileExists())
5209 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5210 // backup of machine config file
5211 Utf8Str strTmp(mData->m_strConfigFileFull);
5212 strTmp.append("-prev");
5213 if (RTFileExists(strTmp.c_str()))
5214 llFilesToDelete.push_back(strTmp);
5215
5216 RTCList<ComPtr<IMedium> > llMediums;
5217 for (size_t i = 0; i < aMedia.size(); ++i)
5218 {
5219 IMedium *pIMedium(aMedia[i]);
5220 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5221 if (pMedium.isNull())
5222 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5223 SafeArray<BSTR> ids;
5224 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5225 if (FAILED(rc)) return rc;
5226 /* At this point the medium should not have any back references
5227 * anymore. If it has it is attached to another VM and *must* not
5228 * deleted. */
5229 if (ids.size() < 1)
5230 llMediums.append(pMedium);
5231 }
5232
5233 ComObjPtr<Progress> pProgress;
5234 pProgress.createObject();
5235 rc = pProgress->init(i_getVirtualBox(),
5236 static_cast<IMachine*>(this) /* aInitiator */,
5237 tr("Deleting files"),
5238 true /* fCancellable */,
5239 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5240 tr("Collecting file inventory"));
5241 if (FAILED(rc))
5242 return rc;
5243
5244 /* create and start the task on a separate thread (note that it will not
5245 * start working until we release alock) */
5246 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5247 rc = pTask->createThread();
5248 pTask = NULL;
5249 if (FAILED(rc))
5250 return rc;
5251
5252 pProgress.queryInterfaceTo(aProgress.asOutParam());
5253
5254 LogFlowFuncLeave();
5255
5256 return S_OK;
5257}
5258
5259HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5260{
5261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5262
5263 ComObjPtr<Snapshot> pSnapshot;
5264 HRESULT rc;
5265
5266 if (aNameOrId.isEmpty())
5267 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5268 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5269 else
5270 {
5271 Guid uuid(aNameOrId);
5272 if (uuid.isValid())
5273 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5274 else
5275 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5276 }
5277 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5278
5279 return rc;
5280}
5281
5282HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5283 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5284{
5285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5286
5287 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5288 if (FAILED(rc)) return rc;
5289
5290 ComObjPtr<SharedFolder> sharedFolder;
5291 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5292 if (SUCCEEDED(rc))
5293 return setError(VBOX_E_OBJECT_IN_USE,
5294 tr("Shared folder named '%s' already exists"),
5295 aName.c_str());
5296
5297 sharedFolder.createObject();
5298 rc = sharedFolder->init(i_getMachine(),
5299 aName,
5300 aHostPath,
5301 !!aWritable,
5302 !!aAutomount,
5303 aAutoMountPoint,
5304 true /* fFailOnError */);
5305 if (FAILED(rc)) return rc;
5306
5307 i_setModified(IsModified_SharedFolders);
5308 mHWData.backup();
5309 mHWData->mSharedFolders.push_back(sharedFolder);
5310
5311 /* inform the direct session if any */
5312 alock.release();
5313 i_onSharedFolderChange();
5314
5315 return S_OK;
5316}
5317
5318HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5319{
5320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5321
5322 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5323 if (FAILED(rc)) return rc;
5324
5325 ComObjPtr<SharedFolder> sharedFolder;
5326 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5327 if (FAILED(rc)) return rc;
5328
5329 i_setModified(IsModified_SharedFolders);
5330 mHWData.backup();
5331 mHWData->mSharedFolders.remove(sharedFolder);
5332
5333 /* inform the direct session if any */
5334 alock.release();
5335 i_onSharedFolderChange();
5336
5337 return S_OK;
5338}
5339
5340HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5341{
5342 /* start with No */
5343 *aCanShow = FALSE;
5344
5345 ComPtr<IInternalSessionControl> directControl;
5346 {
5347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5348
5349 if (mData->mSession.mState != SessionState_Locked)
5350 return setError(VBOX_E_INVALID_VM_STATE,
5351 tr("Machine is not locked for session (session state: %s)"),
5352 Global::stringifySessionState(mData->mSession.mState));
5353
5354 if (mData->mSession.mLockType == LockType_VM)
5355 directControl = mData->mSession.mDirectControl;
5356 }
5357
5358 /* ignore calls made after #OnSessionEnd() is called */
5359 if (!directControl)
5360 return S_OK;
5361
5362 LONG64 dummy;
5363 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5364}
5365
5366HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5367{
5368 ComPtr<IInternalSessionControl> directControl;
5369 {
5370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5371
5372 if (mData->mSession.mState != SessionState_Locked)
5373 return setError(E_FAIL,
5374 tr("Machine is not locked for session (session state: %s)"),
5375 Global::stringifySessionState(mData->mSession.mState));
5376
5377 if (mData->mSession.mLockType == LockType_VM)
5378 directControl = mData->mSession.mDirectControl;
5379 }
5380
5381 /* ignore calls made after #OnSessionEnd() is called */
5382 if (!directControl)
5383 return S_OK;
5384
5385 BOOL dummy;
5386 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5387}
5388
5389#ifdef VBOX_WITH_GUEST_PROPS
5390/**
5391 * Look up a guest property in VBoxSVC's internal structures.
5392 */
5393HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5394 com::Utf8Str &aValue,
5395 LONG64 *aTimestamp,
5396 com::Utf8Str &aFlags) const
5397{
5398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5399
5400 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5401 if (it != mHWData->mGuestProperties.end())
5402 {
5403 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5404 aValue = it->second.strValue;
5405 *aTimestamp = it->second.mTimestamp;
5406 GuestPropWriteFlags(it->second.mFlags, szFlags);
5407 aFlags = Utf8Str(szFlags);
5408 }
5409
5410 return S_OK;
5411}
5412
5413/**
5414 * Query the VM that a guest property belongs to for the property.
5415 * @returns E_ACCESSDENIED if the VM process is not available or not
5416 * currently handling queries and the lookup should then be done in
5417 * VBoxSVC.
5418 */
5419HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5420 com::Utf8Str &aValue,
5421 LONG64 *aTimestamp,
5422 com::Utf8Str &aFlags) const
5423{
5424 HRESULT rc = S_OK;
5425 Bstr bstrValue;
5426 Bstr bstrFlags;
5427
5428 ComPtr<IInternalSessionControl> directControl;
5429 {
5430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5431 if (mData->mSession.mLockType == LockType_VM)
5432 directControl = mData->mSession.mDirectControl;
5433 }
5434
5435 /* ignore calls made after #OnSessionEnd() is called */
5436 if (!directControl)
5437 rc = E_ACCESSDENIED;
5438 else
5439 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5440 0 /* accessMode */,
5441 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5442
5443 aValue = bstrValue;
5444 aFlags = bstrFlags;
5445
5446 return rc;
5447}
5448#endif // VBOX_WITH_GUEST_PROPS
5449
5450HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5451 com::Utf8Str &aValue,
5452 LONG64 *aTimestamp,
5453 com::Utf8Str &aFlags)
5454{
5455#ifndef VBOX_WITH_GUEST_PROPS
5456 ReturnComNotImplemented();
5457#else // VBOX_WITH_GUEST_PROPS
5458
5459 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5460
5461 if (rc == E_ACCESSDENIED)
5462 /* The VM is not running or the service is not (yet) accessible */
5463 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5464 return rc;
5465#endif // VBOX_WITH_GUEST_PROPS
5466}
5467
5468HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5469{
5470 LONG64 dummyTimestamp;
5471 com::Utf8Str dummyFlags;
5472 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5473 return rc;
5474
5475}
5476HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5477{
5478 com::Utf8Str dummyFlags;
5479 com::Utf8Str dummyValue;
5480 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5481 return rc;
5482}
5483
5484#ifdef VBOX_WITH_GUEST_PROPS
5485/**
5486 * Set a guest property in VBoxSVC's internal structures.
5487 */
5488HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5489 const com::Utf8Str &aFlags, bool fDelete)
5490{
5491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5492 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5493 if (FAILED(rc)) return rc;
5494
5495 try
5496 {
5497 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5498 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5499 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5500
5501 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5502 if (it == mHWData->mGuestProperties.end())
5503 {
5504 if (!fDelete)
5505 {
5506 i_setModified(IsModified_MachineData);
5507 mHWData.backupEx();
5508
5509 RTTIMESPEC time;
5510 HWData::GuestProperty prop;
5511 prop.strValue = Bstr(aValue).raw();
5512 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5513 prop.mFlags = fFlags;
5514 mHWData->mGuestProperties[aName] = prop;
5515 }
5516 }
5517 else
5518 {
5519 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5520 {
5521 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5522 }
5523 else
5524 {
5525 i_setModified(IsModified_MachineData);
5526 mHWData.backupEx();
5527
5528 /* The backupEx() operation invalidates our iterator,
5529 * so get a new one. */
5530 it = mHWData->mGuestProperties.find(aName);
5531 Assert(it != mHWData->mGuestProperties.end());
5532
5533 if (!fDelete)
5534 {
5535 RTTIMESPEC time;
5536 it->second.strValue = aValue;
5537 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5538 it->second.mFlags = fFlags;
5539 }
5540 else
5541 mHWData->mGuestProperties.erase(it);
5542 }
5543 }
5544
5545 if (SUCCEEDED(rc))
5546 {
5547 alock.release();
5548
5549 mParent->i_onGuestPropertyChange(mData->mUuid,
5550 Bstr(aName).raw(),
5551 Bstr(aValue).raw(),
5552 Bstr(aFlags).raw());
5553 }
5554 }
5555 catch (std::bad_alloc &)
5556 {
5557 rc = E_OUTOFMEMORY;
5558 }
5559
5560 return rc;
5561}
5562
5563/**
5564 * Set a property on the VM that that property belongs to.
5565 * @returns E_ACCESSDENIED if the VM process is not available or not
5566 * currently handling queries and the setting should then be done in
5567 * VBoxSVC.
5568 */
5569HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5570 const com::Utf8Str &aFlags, bool fDelete)
5571{
5572 HRESULT rc;
5573
5574 try
5575 {
5576 ComPtr<IInternalSessionControl> directControl;
5577 {
5578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5579 if (mData->mSession.mLockType == LockType_VM)
5580 directControl = mData->mSession.mDirectControl;
5581 }
5582
5583 Bstr dummy1; /* will not be changed (setter) */
5584 Bstr dummy2; /* will not be changed (setter) */
5585 LONG64 dummy64;
5586 if (!directControl)
5587 rc = E_ACCESSDENIED;
5588 else
5589 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5590 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5591 fDelete ? 2 : 1 /* accessMode */,
5592 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5593 }
5594 catch (std::bad_alloc &)
5595 {
5596 rc = E_OUTOFMEMORY;
5597 }
5598
5599 return rc;
5600}
5601#endif // VBOX_WITH_GUEST_PROPS
5602
5603HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5604 const com::Utf8Str &aFlags)
5605{
5606#ifndef VBOX_WITH_GUEST_PROPS
5607 ReturnComNotImplemented();
5608#else // VBOX_WITH_GUEST_PROPS
5609 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5610 if (rc == E_ACCESSDENIED)
5611 /* The VM is not running or the service is not (yet) accessible */
5612 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5613 return rc;
5614#endif // VBOX_WITH_GUEST_PROPS
5615}
5616
5617HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5618{
5619 return setGuestProperty(aProperty, aValue, "");
5620}
5621
5622HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5623{
5624#ifndef VBOX_WITH_GUEST_PROPS
5625 ReturnComNotImplemented();
5626#else // VBOX_WITH_GUEST_PROPS
5627 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5628 if (rc == E_ACCESSDENIED)
5629 /* The VM is not running or the service is not (yet) accessible */
5630 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5631 return rc;
5632#endif // VBOX_WITH_GUEST_PROPS
5633}
5634
5635#ifdef VBOX_WITH_GUEST_PROPS
5636/**
5637 * Enumerate the guest properties in VBoxSVC's internal structures.
5638 */
5639HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5640 std::vector<com::Utf8Str> &aNames,
5641 std::vector<com::Utf8Str> &aValues,
5642 std::vector<LONG64> &aTimestamps,
5643 std::vector<com::Utf8Str> &aFlags)
5644{
5645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5646 Utf8Str strPatterns(aPatterns);
5647
5648 /*
5649 * Look for matching patterns and build up a list.
5650 */
5651 HWData::GuestPropertyMap propMap;
5652 for (HWData::GuestPropertyMap::const_iterator
5653 it = mHWData->mGuestProperties.begin();
5654 it != mHWData->mGuestProperties.end();
5655 ++it)
5656 {
5657 if ( strPatterns.isEmpty()
5658 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5659 RTSTR_MAX,
5660 it->first.c_str(),
5661 RTSTR_MAX,
5662 NULL)
5663 )
5664 propMap.insert(*it);
5665 }
5666
5667 alock.release();
5668
5669 /*
5670 * And build up the arrays for returning the property information.
5671 */
5672 size_t cEntries = propMap.size();
5673
5674 aNames.resize(cEntries);
5675 aValues.resize(cEntries);
5676 aTimestamps.resize(cEntries);
5677 aFlags.resize(cEntries);
5678
5679 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5680 size_t i = 0;
5681 for (HWData::GuestPropertyMap::const_iterator
5682 it = propMap.begin();
5683 it != propMap.end();
5684 ++it, ++i)
5685 {
5686 aNames[i] = it->first;
5687 aValues[i] = it->second.strValue;
5688 aTimestamps[i] = it->second.mTimestamp;
5689 GuestPropWriteFlags(it->second.mFlags, szFlags);
5690 aFlags[i] = Utf8Str(szFlags);
5691 }
5692
5693 return S_OK;
5694}
5695
5696/**
5697 * Enumerate the properties managed by a VM.
5698 * @returns E_ACCESSDENIED if the VM process is not available or not
5699 * currently handling queries and the setting should then be done in
5700 * VBoxSVC.
5701 */
5702HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5703 std::vector<com::Utf8Str> &aNames,
5704 std::vector<com::Utf8Str> &aValues,
5705 std::vector<LONG64> &aTimestamps,
5706 std::vector<com::Utf8Str> &aFlags)
5707{
5708 HRESULT rc;
5709 ComPtr<IInternalSessionControl> directControl;
5710 {
5711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5712 if (mData->mSession.mLockType == LockType_VM)
5713 directControl = mData->mSession.mDirectControl;
5714 }
5715
5716 com::SafeArray<BSTR> bNames;
5717 com::SafeArray<BSTR> bValues;
5718 com::SafeArray<LONG64> bTimestamps;
5719 com::SafeArray<BSTR> bFlags;
5720
5721 if (!directControl)
5722 rc = E_ACCESSDENIED;
5723 else
5724 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5725 ComSafeArrayAsOutParam(bNames),
5726 ComSafeArrayAsOutParam(bValues),
5727 ComSafeArrayAsOutParam(bTimestamps),
5728 ComSafeArrayAsOutParam(bFlags));
5729 size_t i;
5730 aNames.resize(bNames.size());
5731 for (i = 0; i < bNames.size(); ++i)
5732 aNames[i] = Utf8Str(bNames[i]);
5733 aValues.resize(bValues.size());
5734 for (i = 0; i < bValues.size(); ++i)
5735 aValues[i] = Utf8Str(bValues[i]);
5736 aTimestamps.resize(bTimestamps.size());
5737 for (i = 0; i < bTimestamps.size(); ++i)
5738 aTimestamps[i] = bTimestamps[i];
5739 aFlags.resize(bFlags.size());
5740 for (i = 0; i < bFlags.size(); ++i)
5741 aFlags[i] = Utf8Str(bFlags[i]);
5742
5743 return rc;
5744}
5745#endif // VBOX_WITH_GUEST_PROPS
5746HRESULT Machine::enumerateGuestProperties(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#ifndef VBOX_WITH_GUEST_PROPS
5753 ReturnComNotImplemented();
5754#else // VBOX_WITH_GUEST_PROPS
5755
5756 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5757
5758 if (rc == E_ACCESSDENIED)
5759 /* The VM is not running or the service is not (yet) accessible */
5760 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5761 return rc;
5762#endif // VBOX_WITH_GUEST_PROPS
5763}
5764
5765HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5766 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5767{
5768 MediumAttachmentList atts;
5769
5770 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5771 if (FAILED(rc)) return rc;
5772
5773 aMediumAttachments.resize(atts.size());
5774 size_t i = 0;
5775 for (MediumAttachmentList::const_iterator
5776 it = atts.begin();
5777 it != atts.end();
5778 ++it, ++i)
5779 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5780
5781 return S_OK;
5782}
5783
5784HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5785 LONG aControllerPort,
5786 LONG aDevice,
5787 ComPtr<IMediumAttachment> &aAttachment)
5788{
5789 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5790 aName.c_str(), aControllerPort, aDevice));
5791
5792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5793
5794 aAttachment = NULL;
5795
5796 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5797 aName,
5798 aControllerPort,
5799 aDevice);
5800 if (pAttach.isNull())
5801 return setError(VBOX_E_OBJECT_NOT_FOUND,
5802 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5803 aDevice, aControllerPort, aName.c_str());
5804
5805 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5806
5807 return S_OK;
5808}
5809
5810
5811HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5812 StorageBus_T aConnectionType,
5813 ComPtr<IStorageController> &aController)
5814{
5815 if ( (aConnectionType <= StorageBus_Null)
5816 || (aConnectionType > StorageBus_VirtioSCSI))
5817 return setError(E_INVALIDARG,
5818 tr("Invalid connection type: %d"),
5819 aConnectionType);
5820
5821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5822
5823 HRESULT rc = i_checkStateDependency(MutableStateDep);
5824 if (FAILED(rc)) return rc;
5825
5826 /* try to find one with the name first. */
5827 ComObjPtr<StorageController> ctrl;
5828
5829 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5830 if (SUCCEEDED(rc))
5831 return setError(VBOX_E_OBJECT_IN_USE,
5832 tr("Storage controller named '%s' already exists"),
5833 aName.c_str());
5834
5835 ctrl.createObject();
5836
5837 /* get a new instance number for the storage controller */
5838 ULONG ulInstance = 0;
5839 bool fBootable = true;
5840 for (StorageControllerList::const_iterator
5841 it = mStorageControllers->begin();
5842 it != mStorageControllers->end();
5843 ++it)
5844 {
5845 if ((*it)->i_getStorageBus() == aConnectionType)
5846 {
5847 ULONG ulCurInst = (*it)->i_getInstance();
5848
5849 if (ulCurInst >= ulInstance)
5850 ulInstance = ulCurInst + 1;
5851
5852 /* Only one controller of each type can be marked as bootable. */
5853 if ((*it)->i_getBootable())
5854 fBootable = false;
5855 }
5856 }
5857
5858 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5859 if (FAILED(rc)) return rc;
5860
5861 i_setModified(IsModified_Storage);
5862 mStorageControllers.backup();
5863 mStorageControllers->push_back(ctrl);
5864
5865 ctrl.queryInterfaceTo(aController.asOutParam());
5866
5867 /* inform the direct session if any */
5868 alock.release();
5869 i_onStorageControllerChange(i_getId(), aName);
5870
5871 return S_OK;
5872}
5873
5874HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5875 ComPtr<IStorageController> &aStorageController)
5876{
5877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5878
5879 ComObjPtr<StorageController> ctrl;
5880
5881 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5882 if (SUCCEEDED(rc))
5883 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5884
5885 return rc;
5886}
5887
5888HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5889 ULONG aInstance,
5890 ComPtr<IStorageController> &aStorageController)
5891{
5892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5893
5894 for (StorageControllerList::const_iterator
5895 it = mStorageControllers->begin();
5896 it != mStorageControllers->end();
5897 ++it)
5898 {
5899 if ( (*it)->i_getStorageBus() == aConnectionType
5900 && (*it)->i_getInstance() == aInstance)
5901 {
5902 (*it).queryInterfaceTo(aStorageController.asOutParam());
5903 return S_OK;
5904 }
5905 }
5906
5907 return setError(VBOX_E_OBJECT_NOT_FOUND,
5908 tr("Could not find a storage controller with instance number '%lu'"),
5909 aInstance);
5910}
5911
5912HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5913{
5914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5915
5916 HRESULT rc = i_checkStateDependency(MutableStateDep);
5917 if (FAILED(rc)) return rc;
5918
5919 ComObjPtr<StorageController> ctrl;
5920
5921 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5922 if (SUCCEEDED(rc))
5923 {
5924 /* Ensure that only one controller of each type is marked as bootable. */
5925 if (aBootable == TRUE)
5926 {
5927 for (StorageControllerList::const_iterator
5928 it = mStorageControllers->begin();
5929 it != mStorageControllers->end();
5930 ++it)
5931 {
5932 ComObjPtr<StorageController> aCtrl = (*it);
5933
5934 if ( (aCtrl->i_getName() != aName)
5935 && aCtrl->i_getBootable() == TRUE
5936 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5937 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5938 {
5939 aCtrl->i_setBootable(FALSE);
5940 break;
5941 }
5942 }
5943 }
5944
5945 if (SUCCEEDED(rc))
5946 {
5947 ctrl->i_setBootable(aBootable);
5948 i_setModified(IsModified_Storage);
5949 }
5950 }
5951
5952 if (SUCCEEDED(rc))
5953 {
5954 /* inform the direct session if any */
5955 alock.release();
5956 i_onStorageControllerChange(i_getId(), aName);
5957 }
5958
5959 return rc;
5960}
5961
5962HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5963{
5964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5965
5966 HRESULT rc = i_checkStateDependency(MutableStateDep);
5967 if (FAILED(rc)) return rc;
5968
5969 ComObjPtr<StorageController> ctrl;
5970 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5971 if (FAILED(rc)) return rc;
5972
5973 MediumAttachmentList llDetachedAttachments;
5974 {
5975 /* find all attached devices to the appropriate storage controller and detach them all */
5976 // make a temporary list because detachDevice invalidates iterators into
5977 // mMediumAttachments
5978 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5979
5980 for (MediumAttachmentList::const_iterator
5981 it = llAttachments2.begin();
5982 it != llAttachments2.end();
5983 ++it)
5984 {
5985 MediumAttachment *pAttachTemp = *it;
5986
5987 AutoCaller localAutoCaller(pAttachTemp);
5988 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5989
5990 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5991
5992 if (pAttachTemp->i_getControllerName() == aName)
5993 {
5994 llDetachedAttachments.push_back(pAttachTemp);
5995 rc = i_detachDevice(pAttachTemp, alock, NULL);
5996 if (FAILED(rc)) return rc;
5997 }
5998 }
5999 }
6000
6001 /* send event about detached devices before removing parent controller */
6002 for (MediumAttachmentList::const_iterator
6003 it = llDetachedAttachments.begin();
6004 it != llDetachedAttachments.end();
6005 ++it)
6006 {
6007 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6008 }
6009
6010 /* We can remove it now. */
6011 i_setModified(IsModified_Storage);
6012 mStorageControllers.backup();
6013
6014 ctrl->i_unshare();
6015
6016 mStorageControllers->remove(ctrl);
6017
6018 /* inform the direct session if any */
6019 alock.release();
6020 i_onStorageControllerChange(i_getId(), aName);
6021
6022 return S_OK;
6023}
6024
6025HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6026 ComPtr<IUSBController> &aController)
6027{
6028 if ( (aType <= USBControllerType_Null)
6029 || (aType >= USBControllerType_Last))
6030 return setError(E_INVALIDARG,
6031 tr("Invalid USB controller type: %d"),
6032 aType);
6033
6034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6035
6036 HRESULT rc = i_checkStateDependency(MutableStateDep);
6037 if (FAILED(rc)) return rc;
6038
6039 /* try to find one with the same type first. */
6040 ComObjPtr<USBController> ctrl;
6041
6042 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6043 if (SUCCEEDED(rc))
6044 return setError(VBOX_E_OBJECT_IN_USE,
6045 tr("USB controller named '%s' already exists"),
6046 aName.c_str());
6047
6048 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6049 ULONG maxInstances;
6050 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6051 if (FAILED(rc))
6052 return rc;
6053
6054 ULONG cInstances = i_getUSBControllerCountByType(aType);
6055 if (cInstances >= maxInstances)
6056 return setError(E_INVALIDARG,
6057 tr("Too many USB controllers of this type"));
6058
6059 ctrl.createObject();
6060
6061 rc = ctrl->init(this, aName, aType);
6062 if (FAILED(rc)) return rc;
6063
6064 i_setModified(IsModified_USB);
6065 mUSBControllers.backup();
6066 mUSBControllers->push_back(ctrl);
6067
6068 ctrl.queryInterfaceTo(aController.asOutParam());
6069
6070 /* inform the direct session if any */
6071 alock.release();
6072 i_onUSBControllerChange();
6073
6074 return S_OK;
6075}
6076
6077HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6078{
6079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6080
6081 ComObjPtr<USBController> ctrl;
6082
6083 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6084 if (SUCCEEDED(rc))
6085 ctrl.queryInterfaceTo(aController.asOutParam());
6086
6087 return rc;
6088}
6089
6090HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6091 ULONG *aControllers)
6092{
6093 if ( (aType <= USBControllerType_Null)
6094 || (aType >= USBControllerType_Last))
6095 return setError(E_INVALIDARG,
6096 tr("Invalid USB controller type: %d"),
6097 aType);
6098
6099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6100
6101 ComObjPtr<USBController> ctrl;
6102
6103 *aControllers = i_getUSBControllerCountByType(aType);
6104
6105 return S_OK;
6106}
6107
6108HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6109{
6110
6111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6112
6113 HRESULT rc = i_checkStateDependency(MutableStateDep);
6114 if (FAILED(rc)) return rc;
6115
6116 ComObjPtr<USBController> ctrl;
6117 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6118 if (FAILED(rc)) return rc;
6119
6120 i_setModified(IsModified_USB);
6121 mUSBControllers.backup();
6122
6123 ctrl->i_unshare();
6124
6125 mUSBControllers->remove(ctrl);
6126
6127 /* inform the direct session if any */
6128 alock.release();
6129 i_onUSBControllerChange();
6130
6131 return S_OK;
6132}
6133
6134HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6135 ULONG *aOriginX,
6136 ULONG *aOriginY,
6137 ULONG *aWidth,
6138 ULONG *aHeight,
6139 BOOL *aEnabled)
6140{
6141 uint32_t u32OriginX= 0;
6142 uint32_t u32OriginY= 0;
6143 uint32_t u32Width = 0;
6144 uint32_t u32Height = 0;
6145 uint16_t u16Flags = 0;
6146
6147 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6148 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6149 if (RT_FAILURE(vrc))
6150 {
6151#ifdef RT_OS_WINDOWS
6152 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6153 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6154 * So just assign fEnable to TRUE again.
6155 * The right fix would be to change GUI API wrappers to make sure that parameters
6156 * are changed only if API succeeds.
6157 */
6158 *aEnabled = TRUE;
6159#endif
6160 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6161 tr("Saved guest size is not available (%Rrc)"),
6162 vrc);
6163 }
6164
6165 *aOriginX = u32OriginX;
6166 *aOriginY = u32OriginY;
6167 *aWidth = u32Width;
6168 *aHeight = u32Height;
6169 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6170
6171 return S_OK;
6172}
6173
6174HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6175 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6176{
6177 if (aScreenId != 0)
6178 return E_NOTIMPL;
6179
6180 if ( aBitmapFormat != BitmapFormat_BGR0
6181 && aBitmapFormat != BitmapFormat_BGRA
6182 && aBitmapFormat != BitmapFormat_RGBA
6183 && aBitmapFormat != BitmapFormat_PNG)
6184 return setError(E_NOTIMPL,
6185 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6186
6187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6188
6189 uint8_t *pu8Data = NULL;
6190 uint32_t cbData = 0;
6191 uint32_t u32Width = 0;
6192 uint32_t u32Height = 0;
6193
6194 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6195
6196 if (RT_FAILURE(vrc))
6197 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6198 tr("Saved thumbnail data is not available (%Rrc)"),
6199 vrc);
6200
6201 HRESULT hr = S_OK;
6202
6203 *aWidth = u32Width;
6204 *aHeight = u32Height;
6205
6206 if (cbData > 0)
6207 {
6208 /* Convert pixels to the format expected by the API caller. */
6209 if (aBitmapFormat == BitmapFormat_BGR0)
6210 {
6211 /* [0] B, [1] G, [2] R, [3] 0. */
6212 aData.resize(cbData);
6213 memcpy(&aData.front(), pu8Data, cbData);
6214 }
6215 else if (aBitmapFormat == BitmapFormat_BGRA)
6216 {
6217 /* [0] B, [1] G, [2] R, [3] A. */
6218 aData.resize(cbData);
6219 for (uint32_t i = 0; i < cbData; i += 4)
6220 {
6221 aData[i] = pu8Data[i];
6222 aData[i + 1] = pu8Data[i + 1];
6223 aData[i + 2] = pu8Data[i + 2];
6224 aData[i + 3] = 0xff;
6225 }
6226 }
6227 else if (aBitmapFormat == BitmapFormat_RGBA)
6228 {
6229 /* [0] R, [1] G, [2] B, [3] A. */
6230 aData.resize(cbData);
6231 for (uint32_t i = 0; i < cbData; i += 4)
6232 {
6233 aData[i] = pu8Data[i + 2];
6234 aData[i + 1] = pu8Data[i + 1];
6235 aData[i + 2] = pu8Data[i];
6236 aData[i + 3] = 0xff;
6237 }
6238 }
6239 else if (aBitmapFormat == BitmapFormat_PNG)
6240 {
6241 uint8_t *pu8PNG = NULL;
6242 uint32_t cbPNG = 0;
6243 uint32_t cxPNG = 0;
6244 uint32_t cyPNG = 0;
6245
6246 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6247
6248 if (RT_SUCCESS(vrc))
6249 {
6250 aData.resize(cbPNG);
6251 if (cbPNG)
6252 memcpy(&aData.front(), pu8PNG, cbPNG);
6253 }
6254 else
6255 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6256 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6257 vrc);
6258
6259 RTMemFree(pu8PNG);
6260 }
6261 }
6262
6263 freeSavedDisplayScreenshot(pu8Data);
6264
6265 return hr;
6266}
6267
6268HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6269 ULONG *aWidth,
6270 ULONG *aHeight,
6271 std::vector<BitmapFormat_T> &aBitmapFormats)
6272{
6273 if (aScreenId != 0)
6274 return E_NOTIMPL;
6275
6276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 uint8_t *pu8Data = NULL;
6279 uint32_t cbData = 0;
6280 uint32_t u32Width = 0;
6281 uint32_t u32Height = 0;
6282
6283 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6284
6285 if (RT_FAILURE(vrc))
6286 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6287 tr("Saved screenshot data is not available (%Rrc)"),
6288 vrc);
6289
6290 *aWidth = u32Width;
6291 *aHeight = u32Height;
6292 aBitmapFormats.resize(1);
6293 aBitmapFormats[0] = BitmapFormat_PNG;
6294
6295 freeSavedDisplayScreenshot(pu8Data);
6296
6297 return S_OK;
6298}
6299
6300HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6301 BitmapFormat_T aBitmapFormat,
6302 ULONG *aWidth,
6303 ULONG *aHeight,
6304 std::vector<BYTE> &aData)
6305{
6306 if (aScreenId != 0)
6307 return E_NOTIMPL;
6308
6309 if (aBitmapFormat != BitmapFormat_PNG)
6310 return E_NOTIMPL;
6311
6312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 uint8_t *pu8Data = NULL;
6315 uint32_t cbData = 0;
6316 uint32_t u32Width = 0;
6317 uint32_t u32Height = 0;
6318
6319 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6320
6321 if (RT_FAILURE(vrc))
6322 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6323 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6324 vrc);
6325
6326 *aWidth = u32Width;
6327 *aHeight = u32Height;
6328
6329 aData.resize(cbData);
6330 if (cbData)
6331 memcpy(&aData.front(), pu8Data, cbData);
6332
6333 freeSavedDisplayScreenshot(pu8Data);
6334
6335 return S_OK;
6336}
6337
6338HRESULT Machine::hotPlugCPU(ULONG aCpu)
6339{
6340 HRESULT rc = S_OK;
6341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6342
6343 if (!mHWData->mCPUHotPlugEnabled)
6344 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6345
6346 if (aCpu >= mHWData->mCPUCount)
6347 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6348
6349 if (mHWData->mCPUAttached[aCpu])
6350 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6351
6352 alock.release();
6353 rc = i_onCPUChange(aCpu, false);
6354 alock.acquire();
6355 if (FAILED(rc)) return rc;
6356
6357 i_setModified(IsModified_MachineData);
6358 mHWData.backup();
6359 mHWData->mCPUAttached[aCpu] = true;
6360
6361 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6362 if (Global::IsOnline(mData->mMachineState))
6363 i_saveSettings(NULL);
6364
6365 return S_OK;
6366}
6367
6368HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6369{
6370 HRESULT rc = S_OK;
6371
6372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6373
6374 if (!mHWData->mCPUHotPlugEnabled)
6375 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6376
6377 if (aCpu >= SchemaDefs::MaxCPUCount)
6378 return setError(E_INVALIDARG,
6379 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6380 SchemaDefs::MaxCPUCount);
6381
6382 if (!mHWData->mCPUAttached[aCpu])
6383 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6384
6385 /* CPU 0 can't be detached */
6386 if (aCpu == 0)
6387 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6388
6389 alock.release();
6390 rc = i_onCPUChange(aCpu, true);
6391 alock.acquire();
6392 if (FAILED(rc)) return rc;
6393
6394 i_setModified(IsModified_MachineData);
6395 mHWData.backup();
6396 mHWData->mCPUAttached[aCpu] = false;
6397
6398 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6399 if (Global::IsOnline(mData->mMachineState))
6400 i_saveSettings(NULL);
6401
6402 return S_OK;
6403}
6404
6405HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6406{
6407 *aAttached = false;
6408
6409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6410
6411 /* If hotplug is enabled the CPU is always enabled. */
6412 if (!mHWData->mCPUHotPlugEnabled)
6413 {
6414 if (aCpu < mHWData->mCPUCount)
6415 *aAttached = true;
6416 }
6417 else
6418 {
6419 if (aCpu < SchemaDefs::MaxCPUCount)
6420 *aAttached = mHWData->mCPUAttached[aCpu];
6421 }
6422
6423 return S_OK;
6424}
6425
6426HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6427{
6428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6429
6430 Utf8Str log = i_getLogFilename(aIdx);
6431 if (!RTFileExists(log.c_str()))
6432 log.setNull();
6433 aFilename = log;
6434
6435 return S_OK;
6436}
6437
6438HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6439{
6440 if (aSize < 0)
6441 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6442
6443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6444
6445 HRESULT rc = S_OK;
6446 Utf8Str log = i_getLogFilename(aIdx);
6447
6448 /* do not unnecessarily hold the lock while doing something which does
6449 * not need the lock and potentially takes a long time. */
6450 alock.release();
6451
6452 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6453 * keeps the SOAP reply size under 1M for the webservice (we're using
6454 * base64 encoded strings for binary data for years now, avoiding the
6455 * expansion of each byte array element to approx. 25 bytes of XML. */
6456 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6457 aData.resize(cbData);
6458
6459 RTFILE LogFile;
6460 int vrc = RTFileOpen(&LogFile, log.c_str(),
6461 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6462 if (RT_SUCCESS(vrc))
6463 {
6464 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6465 if (RT_SUCCESS(vrc))
6466 aData.resize(cbData);
6467 else
6468 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6469 tr("Could not read log file '%s' (%Rrc)"),
6470 log.c_str(), vrc);
6471 RTFileClose(LogFile);
6472 }
6473 else
6474 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6475 tr("Could not open log file '%s' (%Rrc)"),
6476 log.c_str(), vrc);
6477
6478 if (FAILED(rc))
6479 aData.resize(0);
6480
6481 return rc;
6482}
6483
6484
6485/**
6486 * Currently this method doesn't attach device to the running VM,
6487 * just makes sure it's plugged on next VM start.
6488 */
6489HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6490{
6491 // lock scope
6492 {
6493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6494
6495 HRESULT rc = i_checkStateDependency(MutableStateDep);
6496 if (FAILED(rc)) return rc;
6497
6498 ChipsetType_T aChipset = ChipsetType_PIIX3;
6499 COMGETTER(ChipsetType)(&aChipset);
6500
6501 if (aChipset != ChipsetType_ICH9)
6502 {
6503 return setError(E_INVALIDARG,
6504 tr("Host PCI attachment only supported with ICH9 chipset"));
6505 }
6506
6507 // check if device with this host PCI address already attached
6508 for (HWData::PCIDeviceAssignmentList::const_iterator
6509 it = mHWData->mPCIDeviceAssignments.begin();
6510 it != mHWData->mPCIDeviceAssignments.end();
6511 ++it)
6512 {
6513 LONG iHostAddress = -1;
6514 ComPtr<PCIDeviceAttachment> pAttach;
6515 pAttach = *it;
6516 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6517 if (iHostAddress == aHostAddress)
6518 return setError(E_INVALIDARG,
6519 tr("Device with host PCI address already attached to this VM"));
6520 }
6521
6522 ComObjPtr<PCIDeviceAttachment> pda;
6523 char name[32];
6524
6525 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6526 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6527 pda.createObject();
6528 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6529 i_setModified(IsModified_MachineData);
6530 mHWData.backup();
6531 mHWData->mPCIDeviceAssignments.push_back(pda);
6532 }
6533
6534 return S_OK;
6535}
6536
6537/**
6538 * Currently this method doesn't detach device from the running VM,
6539 * just makes sure it's not plugged on next VM start.
6540 */
6541HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6542{
6543 ComObjPtr<PCIDeviceAttachment> pAttach;
6544 bool fRemoved = false;
6545 HRESULT rc;
6546
6547 // lock scope
6548 {
6549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6550
6551 rc = i_checkStateDependency(MutableStateDep);
6552 if (FAILED(rc)) return rc;
6553
6554 for (HWData::PCIDeviceAssignmentList::const_iterator
6555 it = mHWData->mPCIDeviceAssignments.begin();
6556 it != mHWData->mPCIDeviceAssignments.end();
6557 ++it)
6558 {
6559 LONG iHostAddress = -1;
6560 pAttach = *it;
6561 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6562 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6563 {
6564 i_setModified(IsModified_MachineData);
6565 mHWData.backup();
6566 mHWData->mPCIDeviceAssignments.remove(pAttach);
6567 fRemoved = true;
6568 break;
6569 }
6570 }
6571 }
6572
6573
6574 /* Fire event outside of the lock */
6575 if (fRemoved)
6576 {
6577 Assert(!pAttach.isNull());
6578 ComPtr<IEventSource> es;
6579 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6580 Assert(SUCCEEDED(rc));
6581 Bstr mid;
6582 rc = this->COMGETTER(Id)(mid.asOutParam());
6583 Assert(SUCCEEDED(rc));
6584 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6585 }
6586
6587 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6588 tr("No host PCI device %08x attached"),
6589 aHostAddress
6590 );
6591}
6592
6593HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6594{
6595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6596
6597 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6598 size_t i = 0;
6599 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6600 it = mHWData->mPCIDeviceAssignments.begin();
6601 it != mHWData->mPCIDeviceAssignments.end();
6602 ++it, ++i)
6603 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6604
6605 return S_OK;
6606}
6607
6608HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6609{
6610 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6611
6612 return S_OK;
6613}
6614
6615HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6616{
6617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6618
6619 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6620
6621 return S_OK;
6622}
6623
6624HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6625{
6626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6627 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6628 if (SUCCEEDED(hrc))
6629 {
6630 hrc = mHWData.backupEx();
6631 if (SUCCEEDED(hrc))
6632 {
6633 i_setModified(IsModified_MachineData);
6634 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6635 }
6636 }
6637 return hrc;
6638}
6639
6640HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6641{
6642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6643 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6644 return S_OK;
6645}
6646
6647HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6648{
6649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6650 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6651 if (SUCCEEDED(hrc))
6652 {
6653 hrc = mHWData.backupEx();
6654 if (SUCCEEDED(hrc))
6655 {
6656 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6657 if (SUCCEEDED(hrc))
6658 i_setModified(IsModified_MachineData);
6659 }
6660 }
6661 return hrc;
6662}
6663
6664HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6665{
6666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6667
6668 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6669
6670 return S_OK;
6671}
6672
6673HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6674{
6675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6676 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6677 if (SUCCEEDED(hrc))
6678 {
6679 hrc = mHWData.backupEx();
6680 if (SUCCEEDED(hrc))
6681 {
6682 i_setModified(IsModified_MachineData);
6683 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6684 }
6685 }
6686 return hrc;
6687}
6688
6689HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6690{
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692
6693 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6694
6695 return S_OK;
6696}
6697
6698HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6699{
6700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6701
6702 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6703 if ( SUCCEEDED(hrc)
6704 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6705 {
6706 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6707 int vrc;
6708
6709 if (aAutostartEnabled)
6710 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6711 else
6712 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6713
6714 if (RT_SUCCESS(vrc))
6715 {
6716 hrc = mHWData.backupEx();
6717 if (SUCCEEDED(hrc))
6718 {
6719 i_setModified(IsModified_MachineData);
6720 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6721 }
6722 }
6723 else if (vrc == VERR_NOT_SUPPORTED)
6724 hrc = setError(VBOX_E_NOT_SUPPORTED,
6725 tr("The VM autostart feature is not supported on this platform"));
6726 else if (vrc == VERR_PATH_NOT_FOUND)
6727 hrc = setError(E_FAIL,
6728 tr("The path to the autostart database is not set"));
6729 else
6730 hrc = setError(E_UNEXPECTED,
6731 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6732 aAutostartEnabled ? "Adding" : "Removing",
6733 mUserData->s.strName.c_str(), vrc);
6734 }
6735 return hrc;
6736}
6737
6738HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6739{
6740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6741
6742 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6743
6744 return S_OK;
6745}
6746
6747HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6748{
6749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6750 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6751 if (SUCCEEDED(hrc))
6752 {
6753 hrc = mHWData.backupEx();
6754 if (SUCCEEDED(hrc))
6755 {
6756 i_setModified(IsModified_MachineData);
6757 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6758 }
6759 }
6760 return hrc;
6761}
6762
6763HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6764{
6765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6766
6767 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6768
6769 return S_OK;
6770}
6771
6772HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6773{
6774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6775 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6776 if ( SUCCEEDED(hrc)
6777 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6778 {
6779 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6780 int vrc;
6781
6782 if (aAutostopType != AutostopType_Disabled)
6783 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6784 else
6785 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6786
6787 if (RT_SUCCESS(vrc))
6788 {
6789 hrc = mHWData.backupEx();
6790 if (SUCCEEDED(hrc))
6791 {
6792 i_setModified(IsModified_MachineData);
6793 mHWData->mAutostart.enmAutostopType = aAutostopType;
6794 }
6795 }
6796 else if (vrc == VERR_NOT_SUPPORTED)
6797 hrc = setError(VBOX_E_NOT_SUPPORTED,
6798 tr("The VM autostop feature is not supported on this platform"));
6799 else if (vrc == VERR_PATH_NOT_FOUND)
6800 hrc = setError(E_FAIL,
6801 tr("The path to the autostart database is not set"));
6802 else
6803 hrc = setError(E_UNEXPECTED,
6804 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6805 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6806 mUserData->s.strName.c_str(), vrc);
6807 }
6808 return hrc;
6809}
6810
6811HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6812{
6813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6814
6815 aDefaultFrontend = mHWData->mDefaultFrontend;
6816
6817 return S_OK;
6818}
6819
6820HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6821{
6822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6823 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6824 if (SUCCEEDED(hrc))
6825 {
6826 hrc = mHWData.backupEx();
6827 if (SUCCEEDED(hrc))
6828 {
6829 i_setModified(IsModified_MachineData);
6830 mHWData->mDefaultFrontend = aDefaultFrontend;
6831 }
6832 }
6833 return hrc;
6834}
6835
6836HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6837{
6838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6839 size_t cbIcon = mUserData->s.ovIcon.size();
6840 aIcon.resize(cbIcon);
6841 if (cbIcon)
6842 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6843 return S_OK;
6844}
6845
6846HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6847{
6848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6849 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6850 if (SUCCEEDED(hrc))
6851 {
6852 i_setModified(IsModified_MachineData);
6853 mUserData.backup();
6854 size_t cbIcon = aIcon.size();
6855 mUserData->s.ovIcon.resize(cbIcon);
6856 if (cbIcon)
6857 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6858 }
6859 return hrc;
6860}
6861
6862HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6863{
6864#ifdef VBOX_WITH_USB
6865 *aUSBProxyAvailable = true;
6866#else
6867 *aUSBProxyAvailable = false;
6868#endif
6869 return S_OK;
6870}
6871
6872HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6873{
6874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6875
6876 *aVMProcessPriority = mUserData->s.enmVMPriority;
6877
6878 return S_OK;
6879}
6880
6881HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6882{
6883 RT_NOREF(aVMProcessPriority);
6884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6885 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6886 if (SUCCEEDED(hrc))
6887 {
6888 hrc = mUserData.backupEx();
6889 if (SUCCEEDED(hrc))
6890 {
6891 i_setModified(IsModified_MachineData);
6892 mUserData->s.enmVMPriority = aVMProcessPriority;
6893 }
6894 }
6895 alock.release();
6896 if (SUCCEEDED(hrc))
6897 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6898 return hrc;
6899}
6900
6901HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6902 ComPtr<IProgress> &aProgress)
6903{
6904 ComObjPtr<Progress> pP;
6905 Progress *ppP = pP;
6906 IProgress *iP = static_cast<IProgress *>(ppP);
6907 IProgress **pProgress = &iP;
6908
6909 IMachine *pTarget = aTarget;
6910
6911 /* Convert the options. */
6912 RTCList<CloneOptions_T> optList;
6913 if (aOptions.size())
6914 for (size_t i = 0; i < aOptions.size(); ++i)
6915 optList.append(aOptions[i]);
6916
6917 if (optList.contains(CloneOptions_Link))
6918 {
6919 if (!i_isSnapshotMachine())
6920 return setError(E_INVALIDARG,
6921 tr("Linked clone can only be created from a snapshot"));
6922 if (aMode != CloneMode_MachineState)
6923 return setError(E_INVALIDARG,
6924 tr("Linked clone can only be created for a single machine state"));
6925 }
6926 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6927
6928 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6929
6930 HRESULT rc = pWorker->start(pProgress);
6931
6932 pP = static_cast<Progress *>(*pProgress);
6933 pP.queryInterfaceTo(aProgress.asOutParam());
6934
6935 return rc;
6936
6937}
6938
6939HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6940 const com::Utf8Str &aType,
6941 ComPtr<IProgress> &aProgress)
6942{
6943 LogFlowThisFuncEnter();
6944
6945 ComObjPtr<Progress> ptrProgress;
6946 HRESULT hrc = ptrProgress.createObject();
6947 if (SUCCEEDED(hrc))
6948 {
6949 /* Initialize our worker task */
6950 MachineMoveVM *pTask = NULL;
6951 try
6952 {
6953 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6954 }
6955 catch (std::bad_alloc &)
6956 {
6957 return E_OUTOFMEMORY;
6958 }
6959
6960 hrc = pTask->init();//no exceptions are thrown
6961
6962 if (SUCCEEDED(hrc))
6963 {
6964 hrc = pTask->createThread();
6965 pTask = NULL; /* Consumed by createThread(). */
6966 if (SUCCEEDED(hrc))
6967 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6968 else
6969 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6970 }
6971 else
6972 delete pTask;
6973 }
6974
6975 LogFlowThisFuncLeave();
6976 return hrc;
6977
6978}
6979
6980HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6981{
6982 NOREF(aProgress);
6983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6984
6985 // This check should always fail.
6986 HRESULT rc = i_checkStateDependency(MutableStateDep);
6987 if (FAILED(rc)) return rc;
6988
6989 AssertFailedReturn(E_NOTIMPL);
6990}
6991
6992HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6993{
6994 NOREF(aSavedStateFile);
6995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6996
6997 // This check should always fail.
6998 HRESULT rc = i_checkStateDependency(MutableStateDep);
6999 if (FAILED(rc)) return rc;
7000
7001 AssertFailedReturn(E_NOTIMPL);
7002}
7003
7004HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7005{
7006 NOREF(aFRemoveFile);
7007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7008
7009 // This check should always fail.
7010 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7011 if (FAILED(rc)) return rc;
7012
7013 AssertFailedReturn(E_NOTIMPL);
7014}
7015
7016// public methods for internal purposes
7017/////////////////////////////////////////////////////////////////////////////
7018
7019/**
7020 * Adds the given IsModified_* flag to the dirty flags of the machine.
7021 * This must be called either during i_loadSettings or under the machine write lock.
7022 * @param fl Flag
7023 * @param fAllowStateModification If state modifications are allowed.
7024 */
7025void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7026{
7027 mData->flModifications |= fl;
7028 if (fAllowStateModification && i_isStateModificationAllowed())
7029 mData->mCurrentStateModified = true;
7030}
7031
7032/**
7033 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7034 * care of the write locking.
7035 *
7036 * @param fModification The flag to add.
7037 * @param fAllowStateModification If state modifications are allowed.
7038 */
7039void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7040{
7041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 i_setModified(fModification, fAllowStateModification);
7043}
7044
7045/**
7046 * Saves the registry entry of this machine to the given configuration node.
7047 *
7048 * @param data Machine registry data.
7049 *
7050 * @note locks this object for reading.
7051 */
7052HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7053{
7054 AutoLimitedCaller autoCaller(this);
7055 AssertComRCReturnRC(autoCaller.rc());
7056
7057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7058
7059 data.uuid = mData->mUuid;
7060 data.strSettingsFile = mData->m_strConfigFile;
7061
7062 return S_OK;
7063}
7064
7065/**
7066 * Calculates the absolute path of the given path taking the directory of the
7067 * machine settings file as the current directory.
7068 *
7069 * @param strPath Path to calculate the absolute path for.
7070 * @param aResult Where to put the result (used only on success, can be the
7071 * same Utf8Str instance as passed in @a aPath).
7072 * @return IPRT result.
7073 *
7074 * @note Locks this object for reading.
7075 */
7076int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7077{
7078 AutoCaller autoCaller(this);
7079 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7080
7081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7082
7083 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7084
7085 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7086
7087 strSettingsDir.stripFilename();
7088 char szFolder[RTPATH_MAX];
7089 size_t cbFolder = sizeof(szFolder);
7090 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7091 if (RT_SUCCESS(vrc))
7092 aResult = szFolder;
7093
7094 return vrc;
7095}
7096
7097/**
7098 * Copies strSource to strTarget, making it relative to the machine folder
7099 * if it is a subdirectory thereof, or simply copying it otherwise.
7100 *
7101 * @param strSource Path to evaluate and copy.
7102 * @param strTarget Buffer to receive target path.
7103 *
7104 * @note Locks this object for reading.
7105 */
7106void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7107 Utf8Str &strTarget)
7108{
7109 AutoCaller autoCaller(this);
7110 AssertComRCReturn(autoCaller.rc(), (void)0);
7111
7112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7113
7114 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7115 // use strTarget as a temporary buffer to hold the machine settings dir
7116 strTarget = mData->m_strConfigFileFull;
7117 strTarget.stripFilename();
7118 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7119 {
7120 // is relative: then append what's left
7121 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7122 // for empty paths (only possible for subdirs) use "." to avoid
7123 // triggering default settings for not present config attributes.
7124 if (strTarget.isEmpty())
7125 strTarget = ".";
7126 }
7127 else
7128 // is not relative: then overwrite
7129 strTarget = strSource;
7130}
7131
7132/**
7133 * Returns the full path to the machine's log folder in the
7134 * \a aLogFolder argument.
7135 */
7136void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7137{
7138 AutoCaller autoCaller(this);
7139 AssertComRCReturnVoid(autoCaller.rc());
7140
7141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7142
7143 char szTmp[RTPATH_MAX];
7144 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7145 if (RT_SUCCESS(vrc))
7146 {
7147 if (szTmp[0] && !mUserData.isNull())
7148 {
7149 char szTmp2[RTPATH_MAX];
7150 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7151 if (RT_SUCCESS(vrc))
7152 aLogFolder = Utf8StrFmt("%s%c%s",
7153 szTmp2,
7154 RTPATH_DELIMITER,
7155 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7156 }
7157 else
7158 vrc = VERR_PATH_IS_RELATIVE;
7159 }
7160
7161 if (RT_FAILURE(vrc))
7162 {
7163 // fallback if VBOX_USER_LOGHOME is not set or invalid
7164 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7165 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7166 aLogFolder.append(RTPATH_DELIMITER);
7167 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7168 }
7169}
7170
7171/**
7172 * Returns the full path to the machine's log file for an given index.
7173 */
7174Utf8Str Machine::i_getLogFilename(ULONG idx)
7175{
7176 Utf8Str logFolder;
7177 getLogFolder(logFolder);
7178 Assert(logFolder.length());
7179
7180 Utf8Str log;
7181 if (idx == 0)
7182 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7183#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7184 else if (idx == 1)
7185 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7186 else
7187 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7188#else
7189 else
7190 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7191#endif
7192 return log;
7193}
7194
7195/**
7196 * Returns the full path to the machine's hardened log file.
7197 */
7198Utf8Str Machine::i_getHardeningLogFilename(void)
7199{
7200 Utf8Str strFilename;
7201 getLogFolder(strFilename);
7202 Assert(strFilename.length());
7203 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7204 return strFilename;
7205}
7206
7207/**
7208 * Returns the default NVRAM filename based on the location of the VM config.
7209 * Note that this is a relative path.
7210 */
7211Utf8Str Machine::i_getDefaultNVRAMFilename()
7212{
7213 AutoCaller autoCaller(this);
7214 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7215
7216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7217
7218 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7219 || i_isSnapshotMachine())
7220 return Utf8Str::Empty;
7221
7222 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7223 strNVRAMFilePath.stripPath();
7224 strNVRAMFilePath.stripSuffix();
7225 strNVRAMFilePath += ".nvram";
7226
7227 return strNVRAMFilePath;
7228}
7229
7230/**
7231 * Returns the NVRAM filename for a new snapshot. This intentionally works
7232 * similarly to the saved state file naming. Note that this is usually
7233 * a relative path, unless the snapshot folder is absolute.
7234 */
7235Utf8Str Machine::i_getSnapshotNVRAMFilename()
7236{
7237 AutoCaller autoCaller(this);
7238 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7239
7240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7241
7242 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7243 return Utf8Str::Empty;
7244
7245 RTTIMESPEC ts;
7246 RTTimeNow(&ts);
7247 RTTIME time;
7248 RTTimeExplode(&time, &ts);
7249
7250 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7251 strNVRAMFilePath += RTPATH_DELIMITER;
7252 strNVRAMFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7253 time.i32Year, time.u8Month, time.u8MonthDay,
7254 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7255
7256 return strNVRAMFilePath;
7257}
7258
7259/**
7260 * Composes a unique saved state filename based on the current system time. The filename is
7261 * granular to the second so this will work so long as no more than one snapshot is taken on
7262 * a machine per second.
7263 *
7264 * Before version 4.1, we used this formula for saved state files:
7265 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7266 * which no longer works because saved state files can now be shared between the saved state of the
7267 * "saved" machine and an online snapshot, and the following would cause problems:
7268 * 1) save machine
7269 * 2) create online snapshot from that machine state --> reusing saved state file
7270 * 3) save machine again --> filename would be reused, breaking the online snapshot
7271 *
7272 * So instead we now use a timestamp.
7273 *
7274 * @param strStateFilePath
7275 */
7276
7277void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7278{
7279 AutoCaller autoCaller(this);
7280 AssertComRCReturnVoid(autoCaller.rc());
7281
7282 {
7283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7284 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7285 }
7286
7287 RTTIMESPEC ts;
7288 RTTimeNow(&ts);
7289 RTTIME time;
7290 RTTimeExplode(&time, &ts);
7291
7292 strStateFilePath += RTPATH_DELIMITER;
7293 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7294 time.i32Year, time.u8Month, time.u8MonthDay,
7295 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7296}
7297
7298/**
7299 * Returns whether at least one USB controller is present for the VM.
7300 */
7301bool Machine::i_isUSBControllerPresent()
7302{
7303 AutoCaller autoCaller(this);
7304 AssertComRCReturn(autoCaller.rc(), false);
7305
7306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7307
7308 return (mUSBControllers->size() > 0);
7309}
7310
7311#ifdef VBOX_WITH_CLOUD_NET
7312HRESULT Machine::i_connectToCloudNetwork(ProgressProxy *aProgress)
7313{
7314 LogFlowThisFuncEnter();
7315 AssertReturn(aProgress, E_FAIL);
7316
7317 HRESULT hrc = E_FAIL;
7318 Bstr name;
7319
7320 LogFlowThisFunc(("Checking if cloud network needs to be connected\n"));
7321 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7322 {
7323 BOOL enabled;
7324 hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
7325 if ( FAILED(hrc)
7326 || !enabled)
7327 continue;
7328
7329 NetworkAttachmentType_T type;
7330 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
7331 if ( SUCCEEDED(hrc)
7332 && type == NetworkAttachmentType_Cloud)
7333 {
7334 if (name.isNotEmpty())
7335 {
7336 LogRel(("VM '%s' uses multiple cloud network attachments. '%ls' will be ignored.\n",
7337 mUserData->s.strName.c_str(), name.raw()));
7338 continue;
7339 }
7340 hrc = mNetworkAdapters[slot]->COMGETTER(CloudNetwork)(name.asOutParam());
7341 if (SUCCEEDED(hrc))
7342 {
7343 LogRel(("VM '%s' uses cloud network '%ls'\n",
7344 mUserData->s.strName.c_str(), name.raw()));
7345 }
7346 }
7347 }
7348 if (name.isNotEmpty())
7349 {
7350 LogFlowThisFunc(("Connecting to cloud network '%ls'...\n", name.raw()));
7351 ComObjPtr<CloudNetwork> network;
7352 hrc = mParent->i_findCloudNetworkByName(name, &network);
7353 if (FAILED(hrc))
7354 {
7355 LogRel(("Could not find cloud network '%ls'.\n", name.raw()));
7356 return hrc;
7357 }
7358 GatewayInfo gateways;
7359 hrc = startGateways(mParent, network, gateways);
7360 if (SUCCEEDED(hrc))
7361 {
7362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7363 mData->mGatewayInfo = gateways;
7364 }
7365 }
7366 else
7367 LogFlowThisFunc(("VM '%s' has no cloud network attachments.\n", mUserData->s.strName.c_str()));
7368
7369 LogFlowThisFuncLeave();
7370 return hrc;
7371}
7372
7373HRESULT Machine::i_disconnectFromCloudNetwork()
7374{
7375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7376 GatewayInfo gateways(mData->mGatewayInfo);
7377 mData->mGatewayInfo.setNull();
7378 alock.release();
7379
7380 HRESULT hrc = stopGateways(mParent, gateways);
7381 return hrc;
7382}
7383#endif /* VBOX_WITH_CLOUD_NET */
7384
7385
7386/**
7387 * @note Locks this object for writing, calls the client process
7388 * (inside the lock).
7389 */
7390HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7391 const Utf8Str &strFrontend,
7392 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7393 ProgressProxy *aProgress)
7394{
7395 LogFlowThisFuncEnter();
7396
7397 AssertReturn(aControl, E_FAIL);
7398 AssertReturn(aProgress, E_FAIL);
7399 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7400
7401 AutoCaller autoCaller(this);
7402 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7403
7404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7405
7406 if (!mData->mRegistered)
7407 return setError(E_UNEXPECTED,
7408 tr("The machine '%s' is not registered"),
7409 mUserData->s.strName.c_str());
7410
7411 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7412
7413 /* The process started when launching a VM with separate UI/VM processes is always
7414 * the UI process, i.e. needs special handling as it won't claim the session. */
7415 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7416
7417 if (fSeparate)
7418 {
7419 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7420 return setError(VBOX_E_INVALID_OBJECT_STATE,
7421 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7422 mUserData->s.strName.c_str());
7423 }
7424 else
7425 {
7426 if ( mData->mSession.mState == SessionState_Locked
7427 || mData->mSession.mState == SessionState_Spawning
7428 || mData->mSession.mState == SessionState_Unlocking)
7429 return setError(VBOX_E_INVALID_OBJECT_STATE,
7430 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7431 mUserData->s.strName.c_str());
7432
7433 /* may not be busy */
7434 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7435 }
7436
7437 /* Hardening logging */
7438#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7439 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7440 {
7441 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7442 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7443 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7444 {
7445 Utf8Str strStartupLogDir = strHardeningLogFile;
7446 strStartupLogDir.stripFilename();
7447 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7448 file without stripping the file. */
7449 }
7450 strSupHardeningLogArg.append(strHardeningLogFile);
7451
7452 /* Remove legacy log filename to avoid confusion. */
7453 Utf8Str strOldStartupLogFile;
7454 getLogFolder(strOldStartupLogFile);
7455 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7456 RTFileDelete(strOldStartupLogFile.c_str());
7457 }
7458#else
7459 Utf8Str strSupHardeningLogArg;
7460#endif
7461
7462 Utf8Str strAppOverride;
7463#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7464 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7465#endif
7466
7467 bool fUseVBoxSDS = false;
7468 Utf8Str strCanonicalName;
7469 if (false)
7470 { }
7471#ifdef VBOX_WITH_QTGUI
7472 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7473 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7474 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7475 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7476 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7477 {
7478 strCanonicalName = "GUI/Qt";
7479 fUseVBoxSDS = true;
7480 }
7481#endif
7482#ifdef VBOX_WITH_VBOXSDL
7483 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7484 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7485 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7486 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7487 {
7488 strCanonicalName = "GUI/SDL";
7489 fUseVBoxSDS = true;
7490 }
7491#endif
7492#ifdef VBOX_WITH_HEADLESS
7493 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7494 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7495 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7496 {
7497 strCanonicalName = "headless";
7498 }
7499#endif
7500 else
7501 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7502
7503 Utf8Str idStr = mData->mUuid.toString();
7504 Utf8Str const &strMachineName = mUserData->s.strName;
7505 RTPROCESS pid = NIL_RTPROCESS;
7506
7507#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7508 RT_NOREF(fUseVBoxSDS);
7509#else
7510 DWORD idCallerSession = ~(DWORD)0;
7511 if (fUseVBoxSDS)
7512 {
7513 /*
7514 * The VBoxSDS should be used for process launching the VM with
7515 * GUI only if the caller and the VBoxSDS are in different Windows
7516 * sessions and the caller in the interactive one.
7517 */
7518 fUseVBoxSDS = false;
7519
7520 /* Get windows session of the current process. The process token used
7521 due to several reasons:
7522 1. The token is absent for the current thread except someone set it
7523 for us.
7524 2. Needs to get the id of the session where the process is started.
7525 We only need to do this once, though. */
7526 static DWORD s_idCurrentSession = ~(DWORD)0;
7527 DWORD idCurrentSession = s_idCurrentSession;
7528 if (idCurrentSession == ~(DWORD)0)
7529 {
7530 HANDLE hCurrentProcessToken = NULL;
7531 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7532 {
7533 DWORD cbIgn = 0;
7534 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7535 s_idCurrentSession = idCurrentSession;
7536 else
7537 {
7538 idCurrentSession = ~(DWORD)0;
7539 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7540 }
7541 CloseHandle(hCurrentProcessToken);
7542 }
7543 else
7544 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7545 }
7546
7547 /* get the caller's session */
7548 HRESULT hrc = CoImpersonateClient();
7549 if (SUCCEEDED(hrc))
7550 {
7551 HANDLE hCallerThreadToken;
7552 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7553 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7554 &hCallerThreadToken))
7555 {
7556 SetLastError(NO_ERROR);
7557 DWORD cbIgn = 0;
7558 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7559 {
7560 /* Only need to use SDS if the session ID differs: */
7561 if (idCurrentSession != idCallerSession)
7562 {
7563 fUseVBoxSDS = false;
7564
7565 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7566 DWORD cbTokenGroups = 0;
7567 PTOKEN_GROUPS pTokenGroups = NULL;
7568 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7569 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7570 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7571 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7572 {
7573 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7574 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7575 PSID pInteractiveSid = NULL;
7576 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7577 {
7578 /* Iterate over the groups looking for the interactive SID: */
7579 fUseVBoxSDS = false;
7580 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7581 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7582 {
7583 fUseVBoxSDS = true;
7584 break;
7585 }
7586 FreeSid(pInteractiveSid);
7587 }
7588 }
7589 else
7590 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7591 RTMemTmpFree(pTokenGroups);
7592 }
7593 }
7594 else
7595 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7596 CloseHandle(hCallerThreadToken);
7597 }
7598 else
7599 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7600 CoRevertToSelf();
7601 }
7602 else
7603 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7604 }
7605 if (fUseVBoxSDS)
7606 {
7607 /* connect to VBoxSDS */
7608 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7609 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7610 if (FAILED(rc))
7611 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7612 strMachineName.c_str());
7613
7614 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7615 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7616 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7617 service to access the files. */
7618 rc = CoSetProxyBlanket(pVBoxSDS,
7619 RPC_C_AUTHN_DEFAULT,
7620 RPC_C_AUTHZ_DEFAULT,
7621 COLE_DEFAULT_PRINCIPAL,
7622 RPC_C_AUTHN_LEVEL_DEFAULT,
7623 RPC_C_IMP_LEVEL_IMPERSONATE,
7624 NULL,
7625 EOAC_DEFAULT);
7626 if (FAILED(rc))
7627 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7628
7629 size_t const cEnvVars = aEnvironmentChanges.size();
7630 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7631 for (size_t i = 0; i < cEnvVars; i++)
7632 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7633
7634 ULONG uPid = 0;
7635 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7636 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7637 idCallerSession, &uPid);
7638 if (FAILED(rc))
7639 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7640 pid = (RTPROCESS)uPid;
7641 }
7642 else
7643#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7644 {
7645 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7646 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7647 if (RT_FAILURE(vrc))
7648 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7649 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7650 }
7651
7652 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7653 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7654
7655 if (!fSeparate)
7656 {
7657 /*
7658 * Note that we don't release the lock here before calling the client,
7659 * because it doesn't need to call us back if called with a NULL argument.
7660 * Releasing the lock here is dangerous because we didn't prepare the
7661 * launch data yet, but the client we've just started may happen to be
7662 * too fast and call LockMachine() that will fail (because of PID, etc.),
7663 * so that the Machine will never get out of the Spawning session state.
7664 */
7665
7666 /* inform the session that it will be a remote one */
7667 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7668#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7669 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7670#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7671 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7672#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7673 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7674
7675 if (FAILED(rc))
7676 {
7677 /* restore the session state */
7678 mData->mSession.mState = SessionState_Unlocked;
7679 alock.release();
7680 mParent->i_addProcessToReap(pid);
7681 /* The failure may occur w/o any error info (from RPC), so provide one */
7682 return setError(VBOX_E_VM_ERROR,
7683 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7684 }
7685
7686 /* attach launch data to the machine */
7687 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7688 mData->mSession.mRemoteControls.push_back(aControl);
7689 mData->mSession.mProgress = aProgress;
7690 mData->mSession.mPID = pid;
7691 mData->mSession.mState = SessionState_Spawning;
7692 Assert(strCanonicalName.isNotEmpty());
7693 mData->mSession.mName = strCanonicalName;
7694 }
7695 else
7696 {
7697 /* For separate UI process we declare the launch as completed instantly, as the
7698 * actual headless VM start may or may not come. No point in remembering anything
7699 * yet, as what matters for us is when the headless VM gets started. */
7700 aProgress->i_notifyComplete(S_OK);
7701 }
7702
7703 alock.release();
7704 mParent->i_addProcessToReap(pid);
7705
7706 LogFlowThisFuncLeave();
7707 return S_OK;
7708}
7709
7710/**
7711 * Returns @c true if the given session machine instance has an open direct
7712 * session (and optionally also for direct sessions which are closing) and
7713 * returns the session control machine instance if so.
7714 *
7715 * Note that when the method returns @c false, the arguments remain unchanged.
7716 *
7717 * @param aMachine Session machine object.
7718 * @param aControl Direct session control object (optional).
7719 * @param aRequireVM If true then only allow VM sessions.
7720 * @param aAllowClosing If true then additionally a session which is currently
7721 * being closed will also be allowed.
7722 *
7723 * @note locks this object for reading.
7724 */
7725bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7726 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7727 bool aRequireVM /*= false*/,
7728 bool aAllowClosing /*= false*/)
7729{
7730 AutoLimitedCaller autoCaller(this);
7731 AssertComRCReturn(autoCaller.rc(), false);
7732
7733 /* just return false for inaccessible machines */
7734 if (getObjectState().getState() != ObjectState::Ready)
7735 return false;
7736
7737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7738
7739 if ( ( mData->mSession.mState == SessionState_Locked
7740 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7741 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7742 )
7743 {
7744 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7745
7746 aMachine = mData->mSession.mMachine;
7747
7748 if (aControl != NULL)
7749 *aControl = mData->mSession.mDirectControl;
7750
7751 return true;
7752 }
7753
7754 return false;
7755}
7756
7757/**
7758 * Returns @c true if the given machine has an spawning direct session.
7759 *
7760 * @note locks this object for reading.
7761 */
7762bool Machine::i_isSessionSpawning()
7763{
7764 AutoLimitedCaller autoCaller(this);
7765 AssertComRCReturn(autoCaller.rc(), false);
7766
7767 /* just return false for inaccessible machines */
7768 if (getObjectState().getState() != ObjectState::Ready)
7769 return false;
7770
7771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7772
7773 if (mData->mSession.mState == SessionState_Spawning)
7774 return true;
7775
7776 return false;
7777}
7778
7779/**
7780 * Called from the client watcher thread to check for unexpected client process
7781 * death during Session_Spawning state (e.g. before it successfully opened a
7782 * direct session).
7783 *
7784 * On Win32 and on OS/2, this method is called only when we've got the
7785 * direct client's process termination notification, so it always returns @c
7786 * true.
7787 *
7788 * On other platforms, this method returns @c true if the client process is
7789 * terminated and @c false if it's still alive.
7790 *
7791 * @note Locks this object for writing.
7792 */
7793bool Machine::i_checkForSpawnFailure()
7794{
7795 AutoCaller autoCaller(this);
7796 if (!autoCaller.isOk())
7797 {
7798 /* nothing to do */
7799 LogFlowThisFunc(("Already uninitialized!\n"));
7800 return true;
7801 }
7802
7803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7804
7805 if (mData->mSession.mState != SessionState_Spawning)
7806 {
7807 /* nothing to do */
7808 LogFlowThisFunc(("Not spawning any more!\n"));
7809 return true;
7810 }
7811
7812 HRESULT rc = S_OK;
7813
7814 /* PID not yet initialized, skip check. */
7815 if (mData->mSession.mPID == NIL_RTPROCESS)
7816 return false;
7817
7818 RTPROCSTATUS status;
7819 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7820
7821 if (vrc != VERR_PROCESS_RUNNING)
7822 {
7823 Utf8Str strExtraInfo;
7824
7825#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7826 /* If the startup logfile exists and is of non-zero length, tell the
7827 user to look there for more details to encourage them to attach it
7828 when reporting startup issues. */
7829 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7830 uint64_t cbStartupLogFile = 0;
7831 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7832 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7833 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7834#endif
7835
7836 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7837 rc = setError(E_FAIL,
7838 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7839 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7840 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7841 rc = setError(E_FAIL,
7842 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7843 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7844 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7845 rc = setError(E_FAIL,
7846 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7847 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7848 else
7849 rc = setErrorBoth(E_FAIL, vrc,
7850 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7851 i_getName().c_str(), vrc, strExtraInfo.c_str());
7852 }
7853
7854 if (FAILED(rc))
7855 {
7856 /* Close the remote session, remove the remote control from the list
7857 * and reset session state to Closed (@note keep the code in sync with
7858 * the relevant part in LockMachine()). */
7859
7860 Assert(mData->mSession.mRemoteControls.size() == 1);
7861 if (mData->mSession.mRemoteControls.size() == 1)
7862 {
7863 ErrorInfoKeeper eik;
7864 mData->mSession.mRemoteControls.front()->Uninitialize();
7865 }
7866
7867 mData->mSession.mRemoteControls.clear();
7868 mData->mSession.mState = SessionState_Unlocked;
7869
7870 /* finalize the progress after setting the state */
7871 if (!mData->mSession.mProgress.isNull())
7872 {
7873 mData->mSession.mProgress->notifyComplete(rc);
7874 mData->mSession.mProgress.setNull();
7875 }
7876
7877 mData->mSession.mPID = NIL_RTPROCESS;
7878
7879 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7880 return true;
7881 }
7882
7883 return false;
7884}
7885
7886/**
7887 * Checks whether the machine can be registered. If so, commits and saves
7888 * all settings.
7889 *
7890 * @note Must be called from mParent's write lock. Locks this object and
7891 * children for writing.
7892 */
7893HRESULT Machine::i_prepareRegister()
7894{
7895 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7896
7897 AutoLimitedCaller autoCaller(this);
7898 AssertComRCReturnRC(autoCaller.rc());
7899
7900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7901
7902 /* wait for state dependents to drop to zero */
7903 i_ensureNoStateDependencies();
7904
7905 if (!mData->mAccessible)
7906 return setError(VBOX_E_INVALID_OBJECT_STATE,
7907 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7908 mUserData->s.strName.c_str(),
7909 mData->mUuid.toString().c_str());
7910
7911 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7912
7913 if (mData->mRegistered)
7914 return setError(VBOX_E_INVALID_OBJECT_STATE,
7915 tr("The machine '%s' with UUID {%s} is already registered"),
7916 mUserData->s.strName.c_str(),
7917 mData->mUuid.toString().c_str());
7918
7919 HRESULT rc = S_OK;
7920
7921 // Ensure the settings are saved. If we are going to be registered and
7922 // no config file exists yet, create it by calling i_saveSettings() too.
7923 if ( (mData->flModifications)
7924 || (!mData->pMachineConfigFile->fileExists())
7925 )
7926 {
7927 rc = i_saveSettings(NULL);
7928 // no need to check whether VirtualBox.xml needs saving too since
7929 // we can't have a machine XML file rename pending
7930 if (FAILED(rc)) return rc;
7931 }
7932
7933 /* more config checking goes here */
7934
7935 if (SUCCEEDED(rc))
7936 {
7937 /* we may have had implicit modifications we want to fix on success */
7938 i_commit();
7939
7940 mData->mRegistered = true;
7941 }
7942 else
7943 {
7944 /* we may have had implicit modifications we want to cancel on failure*/
7945 i_rollback(false /* aNotify */);
7946 }
7947
7948 return rc;
7949}
7950
7951/**
7952 * Increases the number of objects dependent on the machine state or on the
7953 * registered state. Guarantees that these two states will not change at least
7954 * until #i_releaseStateDependency() is called.
7955 *
7956 * Depending on the @a aDepType value, additional state checks may be made.
7957 * These checks will set extended error info on failure. See
7958 * #i_checkStateDependency() for more info.
7959 *
7960 * If this method returns a failure, the dependency is not added and the caller
7961 * is not allowed to rely on any particular machine state or registration state
7962 * value and may return the failed result code to the upper level.
7963 *
7964 * @param aDepType Dependency type to add.
7965 * @param aState Current machine state (NULL if not interested).
7966 * @param aRegistered Current registered state (NULL if not interested).
7967 *
7968 * @note Locks this object for writing.
7969 */
7970HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7971 MachineState_T *aState /* = NULL */,
7972 BOOL *aRegistered /* = NULL */)
7973{
7974 AutoCaller autoCaller(this);
7975 AssertComRCReturnRC(autoCaller.rc());
7976
7977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7978
7979 HRESULT rc = i_checkStateDependency(aDepType);
7980 if (FAILED(rc)) return rc;
7981
7982 {
7983 if (mData->mMachineStateChangePending != 0)
7984 {
7985 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7986 * drop to zero so don't add more. It may make sense to wait a bit
7987 * and retry before reporting an error (since the pending state
7988 * transition should be really quick) but let's just assert for
7989 * now to see if it ever happens on practice. */
7990
7991 AssertFailed();
7992
7993 return setError(E_ACCESSDENIED,
7994 tr("Machine state change is in progress. Please retry the operation later."));
7995 }
7996
7997 ++mData->mMachineStateDeps;
7998 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7999 }
8000
8001 if (aState)
8002 *aState = mData->mMachineState;
8003 if (aRegistered)
8004 *aRegistered = mData->mRegistered;
8005
8006 return S_OK;
8007}
8008
8009/**
8010 * Decreases the number of objects dependent on the machine state.
8011 * Must always complete the #i_addStateDependency() call after the state
8012 * dependency is no more necessary.
8013 */
8014void Machine::i_releaseStateDependency()
8015{
8016 AutoCaller autoCaller(this);
8017 AssertComRCReturnVoid(autoCaller.rc());
8018
8019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8020
8021 /* releaseStateDependency() w/o addStateDependency()? */
8022 AssertReturnVoid(mData->mMachineStateDeps != 0);
8023 -- mData->mMachineStateDeps;
8024
8025 if (mData->mMachineStateDeps == 0)
8026 {
8027 /* inform i_ensureNoStateDependencies() that there are no more deps */
8028 if (mData->mMachineStateChangePending != 0)
8029 {
8030 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8031 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8032 }
8033 }
8034}
8035
8036Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8037{
8038 /* start with nothing found */
8039 Utf8Str strResult("");
8040
8041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8042
8043 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8044 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8045 // found:
8046 strResult = it->second; // source is a Utf8Str
8047
8048 return strResult;
8049}
8050
8051// protected methods
8052/////////////////////////////////////////////////////////////////////////////
8053
8054/**
8055 * Performs machine state checks based on the @a aDepType value. If a check
8056 * fails, this method will set extended error info, otherwise it will return
8057 * S_OK. It is supposed, that on failure, the caller will immediately return
8058 * the return value of this method to the upper level.
8059 *
8060 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8061 *
8062 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8063 * current state of this machine object allows to change settings of the
8064 * machine (i.e. the machine is not registered, or registered but not running
8065 * and not saved). It is useful to call this method from Machine setters
8066 * before performing any change.
8067 *
8068 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8069 * as for MutableStateDep except that if the machine is saved, S_OK is also
8070 * returned. This is useful in setters which allow changing machine
8071 * properties when it is in the saved state.
8072 *
8073 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8074 * if the current state of this machine object allows to change runtime
8075 * changeable settings of the machine (i.e. the machine is not registered, or
8076 * registered but either running or not running and not saved). It is useful
8077 * to call this method from Machine setters before performing any changes to
8078 * runtime changeable settings.
8079 *
8080 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8081 * the same as for MutableOrRunningStateDep except that if the machine is
8082 * saved, S_OK is also returned. This is useful in setters which allow
8083 * changing runtime and saved state changeable machine properties.
8084 *
8085 * @param aDepType Dependency type to check.
8086 *
8087 * @note Non Machine based classes should use #i_addStateDependency() and
8088 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8089 * template.
8090 *
8091 * @note This method must be called from under this object's read or write
8092 * lock.
8093 */
8094HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8095{
8096 switch (aDepType)
8097 {
8098 case AnyStateDep:
8099 {
8100 break;
8101 }
8102 case MutableStateDep:
8103 {
8104 if ( mData->mRegistered
8105 && ( !i_isSessionMachine()
8106 || ( mData->mMachineState != MachineState_Aborted
8107 && mData->mMachineState != MachineState_Teleported
8108 && mData->mMachineState != MachineState_PoweredOff
8109 )
8110 )
8111 )
8112 return setError(VBOX_E_INVALID_VM_STATE,
8113 tr("The machine is not mutable (state is %s)"),
8114 Global::stringifyMachineState(mData->mMachineState));
8115 break;
8116 }
8117 case MutableOrSavedStateDep:
8118 {
8119 if ( mData->mRegistered
8120 && ( !i_isSessionMachine()
8121 || ( mData->mMachineState != MachineState_Aborted
8122 && mData->mMachineState != MachineState_Teleported
8123 && mData->mMachineState != MachineState_Saved
8124 && mData->mMachineState != MachineState_PoweredOff
8125 )
8126 )
8127 )
8128 return setError(VBOX_E_INVALID_VM_STATE,
8129 tr("The machine is not mutable or saved (state is %s)"),
8130 Global::stringifyMachineState(mData->mMachineState));
8131 break;
8132 }
8133 case MutableOrRunningStateDep:
8134 {
8135 if ( mData->mRegistered
8136 && ( !i_isSessionMachine()
8137 || ( mData->mMachineState != MachineState_Aborted
8138 && mData->mMachineState != MachineState_Teleported
8139 && mData->mMachineState != MachineState_PoweredOff
8140 && !Global::IsOnline(mData->mMachineState)
8141 )
8142 )
8143 )
8144 return setError(VBOX_E_INVALID_VM_STATE,
8145 tr("The machine is not mutable or running (state is %s)"),
8146 Global::stringifyMachineState(mData->mMachineState));
8147 break;
8148 }
8149 case MutableOrSavedOrRunningStateDep:
8150 {
8151 if ( mData->mRegistered
8152 && ( !i_isSessionMachine()
8153 || ( mData->mMachineState != MachineState_Aborted
8154 && mData->mMachineState != MachineState_Teleported
8155 && mData->mMachineState != MachineState_Saved
8156 && mData->mMachineState != MachineState_PoweredOff
8157 && !Global::IsOnline(mData->mMachineState)
8158 )
8159 )
8160 )
8161 return setError(VBOX_E_INVALID_VM_STATE,
8162 tr("The machine is not mutable, saved or running (state is %s)"),
8163 Global::stringifyMachineState(mData->mMachineState));
8164 break;
8165 }
8166 }
8167
8168 return S_OK;
8169}
8170
8171/**
8172 * Helper to initialize all associated child objects and allocate data
8173 * structures.
8174 *
8175 * This method must be called as a part of the object's initialization procedure
8176 * (usually done in the #init() method).
8177 *
8178 * @note Must be called only from #init() or from #i_registeredInit().
8179 */
8180HRESULT Machine::initDataAndChildObjects()
8181{
8182 AutoCaller autoCaller(this);
8183 AssertComRCReturnRC(autoCaller.rc());
8184 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8185 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8186
8187 AssertReturn(!mData->mAccessible, E_FAIL);
8188
8189 /* allocate data structures */
8190 mSSData.allocate();
8191 mUserData.allocate();
8192 mHWData.allocate();
8193 mMediumAttachments.allocate();
8194 mStorageControllers.allocate();
8195 mUSBControllers.allocate();
8196
8197 /* initialize mOSTypeId */
8198 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8199
8200/** @todo r=bird: init() methods never fails, right? Why don't we make them
8201 * return void then! */
8202
8203 /* create associated BIOS settings object */
8204 unconst(mBIOSSettings).createObject();
8205 mBIOSSettings->init(this);
8206
8207 /* create associated record settings object */
8208 unconst(mRecordingSettings).createObject();
8209 mRecordingSettings->init(this);
8210
8211 /* create the graphics adapter object (always present) */
8212 unconst(mGraphicsAdapter).createObject();
8213 mGraphicsAdapter->init(this);
8214
8215 /* create an associated VRDE object (default is disabled) */
8216 unconst(mVRDEServer).createObject();
8217 mVRDEServer->init(this);
8218
8219 /* create associated serial port objects */
8220 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8221 {
8222 unconst(mSerialPorts[slot]).createObject();
8223 mSerialPorts[slot]->init(this, slot);
8224 }
8225
8226 /* create associated parallel port objects */
8227 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8228 {
8229 unconst(mParallelPorts[slot]).createObject();
8230 mParallelPorts[slot]->init(this, slot);
8231 }
8232
8233 /* create the audio adapter object (always present, default is disabled) */
8234 unconst(mAudioAdapter).createObject();
8235 mAudioAdapter->init(this);
8236
8237 /* create the USB device filters object (always present) */
8238 unconst(mUSBDeviceFilters).createObject();
8239 mUSBDeviceFilters->init(this);
8240
8241 /* create associated network adapter objects */
8242 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8243 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8244 {
8245 unconst(mNetworkAdapters[slot]).createObject();
8246 mNetworkAdapters[slot]->init(this, slot);
8247 }
8248
8249 /* create the bandwidth control */
8250 unconst(mBandwidthControl).createObject();
8251 mBandwidthControl->init(this);
8252
8253 return S_OK;
8254}
8255
8256/**
8257 * Helper to uninitialize all associated child objects and to free all data
8258 * structures.
8259 *
8260 * This method must be called as a part of the object's uninitialization
8261 * procedure (usually done in the #uninit() method).
8262 *
8263 * @note Must be called only from #uninit() or from #i_registeredInit().
8264 */
8265void Machine::uninitDataAndChildObjects()
8266{
8267 AutoCaller autoCaller(this);
8268 AssertComRCReturnVoid(autoCaller.rc());
8269 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8270 || getObjectState().getState() == ObjectState::Limited);
8271
8272 /* tell all our other child objects we've been uninitialized */
8273 if (mBandwidthControl)
8274 {
8275 mBandwidthControl->uninit();
8276 unconst(mBandwidthControl).setNull();
8277 }
8278
8279 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8280 {
8281 if (mNetworkAdapters[slot])
8282 {
8283 mNetworkAdapters[slot]->uninit();
8284 unconst(mNetworkAdapters[slot]).setNull();
8285 }
8286 }
8287
8288 if (mUSBDeviceFilters)
8289 {
8290 mUSBDeviceFilters->uninit();
8291 unconst(mUSBDeviceFilters).setNull();
8292 }
8293
8294 if (mAudioAdapter)
8295 {
8296 mAudioAdapter->uninit();
8297 unconst(mAudioAdapter).setNull();
8298 }
8299
8300 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8301 {
8302 if (mParallelPorts[slot])
8303 {
8304 mParallelPorts[slot]->uninit();
8305 unconst(mParallelPorts[slot]).setNull();
8306 }
8307 }
8308
8309 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8310 {
8311 if (mSerialPorts[slot])
8312 {
8313 mSerialPorts[slot]->uninit();
8314 unconst(mSerialPorts[slot]).setNull();
8315 }
8316 }
8317
8318 if (mVRDEServer)
8319 {
8320 mVRDEServer->uninit();
8321 unconst(mVRDEServer).setNull();
8322 }
8323
8324 if (mGraphicsAdapter)
8325 {
8326 mGraphicsAdapter->uninit();
8327 unconst(mGraphicsAdapter).setNull();
8328 }
8329
8330 if (mBIOSSettings)
8331 {
8332 mBIOSSettings->uninit();
8333 unconst(mBIOSSettings).setNull();
8334 }
8335
8336 if (mRecordingSettings)
8337 {
8338 mRecordingSettings->uninit();
8339 unconst(mRecordingSettings).setNull();
8340 }
8341
8342 /* Deassociate media (only when a real Machine or a SnapshotMachine
8343 * instance is uninitialized; SessionMachine instances refer to real
8344 * Machine media). This is necessary for a clean re-initialization of
8345 * the VM after successfully re-checking the accessibility state. Note
8346 * that in case of normal Machine or SnapshotMachine uninitialization (as
8347 * a result of unregistering or deleting the snapshot), outdated media
8348 * attachments will already be uninitialized and deleted, so this
8349 * code will not affect them. */
8350 if ( !mMediumAttachments.isNull()
8351 && !i_isSessionMachine()
8352 )
8353 {
8354 for (MediumAttachmentList::const_iterator
8355 it = mMediumAttachments->begin();
8356 it != mMediumAttachments->end();
8357 ++it)
8358 {
8359 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8360 if (pMedium.isNull())
8361 continue;
8362 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8363 AssertComRC(rc);
8364 }
8365 }
8366
8367 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8368 {
8369 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8370 if (mData->mFirstSnapshot)
8371 {
8372 // snapshots tree is protected by machine write lock; strictly
8373 // this isn't necessary here since we're deleting the entire
8374 // machine, but otherwise we assert in Snapshot::uninit()
8375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8376 mData->mFirstSnapshot->uninit();
8377 mData->mFirstSnapshot.setNull();
8378 }
8379
8380 mData->mCurrentSnapshot.setNull();
8381 }
8382
8383 /* free data structures (the essential mData structure is not freed here
8384 * since it may be still in use) */
8385 mMediumAttachments.free();
8386 mStorageControllers.free();
8387 mUSBControllers.free();
8388 mHWData.free();
8389 mUserData.free();
8390 mSSData.free();
8391}
8392
8393/**
8394 * Returns a pointer to the Machine object for this machine that acts like a
8395 * parent for complex machine data objects such as shared folders, etc.
8396 *
8397 * For primary Machine objects and for SnapshotMachine objects, returns this
8398 * object's pointer itself. For SessionMachine objects, returns the peer
8399 * (primary) machine pointer.
8400 */
8401Machine *Machine::i_getMachine()
8402{
8403 if (i_isSessionMachine())
8404 return (Machine*)mPeer;
8405 return this;
8406}
8407
8408/**
8409 * Makes sure that there are no machine state dependents. If necessary, waits
8410 * for the number of dependents to drop to zero.
8411 *
8412 * Make sure this method is called from under this object's write lock to
8413 * guarantee that no new dependents may be added when this method returns
8414 * control to the caller.
8415 *
8416 * @note Locks this object for writing. The lock will be released while waiting
8417 * (if necessary).
8418 *
8419 * @warning To be used only in methods that change the machine state!
8420 */
8421void Machine::i_ensureNoStateDependencies()
8422{
8423 AssertReturnVoid(isWriteLockOnCurrentThread());
8424
8425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8426
8427 /* Wait for all state dependents if necessary */
8428 if (mData->mMachineStateDeps != 0)
8429 {
8430 /* lazy semaphore creation */
8431 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8432 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8433
8434 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8435 mData->mMachineStateDeps));
8436
8437 ++mData->mMachineStateChangePending;
8438
8439 /* reset the semaphore before waiting, the last dependent will signal
8440 * it */
8441 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8442
8443 alock.release();
8444
8445 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8446
8447 alock.acquire();
8448
8449 -- mData->mMachineStateChangePending;
8450 }
8451}
8452
8453/**
8454 * Changes the machine state and informs callbacks.
8455 *
8456 * This method is not intended to fail so it either returns S_OK or asserts (and
8457 * returns a failure).
8458 *
8459 * @note Locks this object for writing.
8460 */
8461HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8462{
8463 LogFlowThisFuncEnter();
8464 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8465 Assert(aMachineState != MachineState_Null);
8466
8467 AutoCaller autoCaller(this);
8468 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8469
8470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8471
8472 /* wait for state dependents to drop to zero */
8473 i_ensureNoStateDependencies();
8474
8475 MachineState_T const enmOldState = mData->mMachineState;
8476 if (enmOldState != aMachineState)
8477 {
8478 mData->mMachineState = aMachineState;
8479 RTTimeNow(&mData->mLastStateChange);
8480
8481#ifdef VBOX_WITH_DTRACE_R3_MAIN
8482 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8483#endif
8484 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8485 }
8486
8487 LogFlowThisFuncLeave();
8488 return S_OK;
8489}
8490
8491/**
8492 * Searches for a shared folder with the given logical name
8493 * in the collection of shared folders.
8494 *
8495 * @param aName logical name of the shared folder
8496 * @param aSharedFolder where to return the found object
8497 * @param aSetError whether to set the error info if the folder is
8498 * not found
8499 * @return
8500 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8501 *
8502 * @note
8503 * must be called from under the object's lock!
8504 */
8505HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8506 ComObjPtr<SharedFolder> &aSharedFolder,
8507 bool aSetError /* = false */)
8508{
8509 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8510 for (HWData::SharedFolderList::const_iterator
8511 it = mHWData->mSharedFolders.begin();
8512 it != mHWData->mSharedFolders.end();
8513 ++it)
8514 {
8515 SharedFolder *pSF = *it;
8516 AutoCaller autoCaller(pSF);
8517 if (pSF->i_getName() == aName)
8518 {
8519 aSharedFolder = pSF;
8520 rc = S_OK;
8521 break;
8522 }
8523 }
8524
8525 if (aSetError && FAILED(rc))
8526 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8527
8528 return rc;
8529}
8530
8531/**
8532 * Initializes all machine instance data from the given settings structures
8533 * from XML. The exception is the machine UUID which needs special handling
8534 * depending on the caller's use case, so the caller needs to set that herself.
8535 *
8536 * This gets called in several contexts during machine initialization:
8537 *
8538 * -- When machine XML exists on disk already and needs to be loaded into memory,
8539 * for example, from #i_registeredInit() to load all registered machines on
8540 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8541 * attached to the machine should be part of some media registry already.
8542 *
8543 * -- During OVF import, when a machine config has been constructed from an
8544 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8545 * ensure that the media listed as attachments in the config (which have
8546 * been imported from the OVF) receive the correct registry ID.
8547 *
8548 * -- During VM cloning.
8549 *
8550 * @param config Machine settings from XML.
8551 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8552 * for each attached medium in the config.
8553 * @return
8554 */
8555HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8556 const Guid *puuidRegistry)
8557{
8558 // copy name, description, OS type, teleporter, UTC etc.
8559 mUserData->s = config.machineUserData;
8560
8561 // look up the object by Id to check it is valid
8562 ComObjPtr<GuestOSType> pGuestOSType;
8563 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8564 if (!pGuestOSType.isNull())
8565 mUserData->s.strOsType = pGuestOSType->i_id();
8566
8567 // stateFile (optional)
8568 if (config.strStateFile.isEmpty())
8569 mSSData->strStateFilePath.setNull();
8570 else
8571 {
8572 Utf8Str stateFilePathFull(config.strStateFile);
8573 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8574 if (RT_FAILURE(vrc))
8575 return setErrorBoth(E_FAIL, vrc,
8576 tr("Invalid saved state file path '%s' (%Rrc)"),
8577 config.strStateFile.c_str(),
8578 vrc);
8579 mSSData->strStateFilePath = stateFilePathFull;
8580 }
8581
8582 // snapshot folder needs special processing so set it again
8583 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8584 if (FAILED(rc)) return rc;
8585
8586 /* Copy the extra data items (config may or may not be the same as
8587 * mData->pMachineConfigFile) if necessary. When loading the XML files
8588 * from disk they are the same, but not for OVF import. */
8589 if (mData->pMachineConfigFile != &config)
8590 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8591
8592 /* currentStateModified (optional, default is true) */
8593 mData->mCurrentStateModified = config.fCurrentStateModified;
8594
8595 mData->mLastStateChange = config.timeLastStateChange;
8596
8597 /*
8598 * note: all mUserData members must be assigned prior this point because
8599 * we need to commit changes in order to let mUserData be shared by all
8600 * snapshot machine instances.
8601 */
8602 mUserData.commitCopy();
8603
8604 // machine registry, if present (must be loaded before snapshots)
8605 if (config.canHaveOwnMediaRegistry())
8606 {
8607 // determine machine folder
8608 Utf8Str strMachineFolder = i_getSettingsFileFull();
8609 strMachineFolder.stripFilename();
8610 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8611 config.mediaRegistry,
8612 strMachineFolder);
8613 if (FAILED(rc)) return rc;
8614 }
8615
8616 /* Snapshot node (optional) */
8617 size_t cRootSnapshots;
8618 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8619 {
8620 // there must be only one root snapshot
8621 Assert(cRootSnapshots == 1);
8622
8623 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8624
8625 rc = i_loadSnapshot(snap,
8626 config.uuidCurrentSnapshot,
8627 NULL); // no parent == first snapshot
8628 if (FAILED(rc)) return rc;
8629 }
8630
8631 // hardware data
8632 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8633 if (FAILED(rc)) return rc;
8634
8635 /*
8636 * NOTE: the assignment below must be the last thing to do,
8637 * otherwise it will be not possible to change the settings
8638 * somewhere in the code above because all setters will be
8639 * blocked by i_checkStateDependency(MutableStateDep).
8640 */
8641
8642 /* set the machine state to Aborted or Saved when appropriate */
8643 if (config.fAborted)
8644 {
8645 mSSData->strStateFilePath.setNull();
8646
8647 /* no need to use i_setMachineState() during init() */
8648 mData->mMachineState = MachineState_Aborted;
8649 }
8650 else if (!mSSData->strStateFilePath.isEmpty())
8651 {
8652 /* no need to use i_setMachineState() during init() */
8653 mData->mMachineState = MachineState_Saved;
8654 }
8655
8656 // after loading settings, we are no longer different from the XML on disk
8657 mData->flModifications = 0;
8658
8659 return S_OK;
8660}
8661
8662/**
8663 * Recursively loads all snapshots starting from the given.
8664 *
8665 * @param data snapshot settings.
8666 * @param aCurSnapshotId Current snapshot ID from the settings file.
8667 * @param aParentSnapshot Parent snapshot.
8668 */
8669HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8670 const Guid &aCurSnapshotId,
8671 Snapshot *aParentSnapshot)
8672{
8673 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8674 AssertReturn(!i_isSessionMachine(), E_FAIL);
8675
8676 HRESULT rc = S_OK;
8677
8678 Utf8Str strStateFile;
8679 if (!data.strStateFile.isEmpty())
8680 {
8681 /* optional */
8682 strStateFile = data.strStateFile;
8683 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8684 if (RT_FAILURE(vrc))
8685 return setErrorBoth(E_FAIL, vrc,
8686 tr("Invalid saved state file path '%s' (%Rrc)"),
8687 strStateFile.c_str(),
8688 vrc);
8689 }
8690
8691 /* create a snapshot machine object */
8692 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8693 pSnapshotMachine.createObject();
8694 rc = pSnapshotMachine->initFromSettings(this,
8695 data.hardware,
8696 &data.debugging,
8697 &data.autostart,
8698 data.uuid.ref(),
8699 strStateFile);
8700 if (FAILED(rc)) return rc;
8701
8702 /* create a snapshot object */
8703 ComObjPtr<Snapshot> pSnapshot;
8704 pSnapshot.createObject();
8705 /* initialize the snapshot */
8706 rc = pSnapshot->init(mParent, // VirtualBox object
8707 data.uuid,
8708 data.strName,
8709 data.strDescription,
8710 data.timestamp,
8711 pSnapshotMachine,
8712 aParentSnapshot);
8713 if (FAILED(rc)) return rc;
8714
8715 /* memorize the first snapshot if necessary */
8716 if (!mData->mFirstSnapshot)
8717 mData->mFirstSnapshot = pSnapshot;
8718
8719 /* memorize the current snapshot when appropriate */
8720 if ( !mData->mCurrentSnapshot
8721 && pSnapshot->i_getId() == aCurSnapshotId
8722 )
8723 mData->mCurrentSnapshot = pSnapshot;
8724
8725 // now create the children
8726 for (settings::SnapshotsList::const_iterator
8727 it = data.llChildSnapshots.begin();
8728 it != data.llChildSnapshots.end();
8729 ++it)
8730 {
8731 const settings::Snapshot &childData = *it;
8732 // recurse
8733 rc = i_loadSnapshot(childData,
8734 aCurSnapshotId,
8735 pSnapshot); // parent = the one we created above
8736 if (FAILED(rc)) return rc;
8737 }
8738
8739 return rc;
8740}
8741
8742/**
8743 * Loads settings into mHWData.
8744 *
8745 * @param puuidRegistry Registry ID.
8746 * @param puuidSnapshot Snapshot ID
8747 * @param data Reference to the hardware settings.
8748 * @param pDbg Pointer to the debugging settings.
8749 * @param pAutostart Pointer to the autostart settings.
8750 */
8751HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8752 const Guid *puuidSnapshot,
8753 const settings::Hardware &data,
8754 const settings::Debugging *pDbg,
8755 const settings::Autostart *pAutostart)
8756{
8757 AssertReturn(!i_isSessionMachine(), E_FAIL);
8758
8759 HRESULT rc = S_OK;
8760
8761 try
8762 {
8763 ComObjPtr<GuestOSType> pGuestOSType;
8764 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8765
8766 /* The hardware version attribute (optional). */
8767 mHWData->mHWVersion = data.strVersion;
8768 mHWData->mHardwareUUID = data.uuid;
8769
8770 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8771 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8772 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8773 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8774 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8775 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8776 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8777 mHWData->mPAEEnabled = data.fPAE;
8778 mHWData->mLongMode = data.enmLongMode;
8779 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8780 mHWData->mAPIC = data.fAPIC;
8781 mHWData->mX2APIC = data.fX2APIC;
8782 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8783 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8784 mHWData->mSpecCtrl = data.fSpecCtrl;
8785 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8786 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8787 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8788 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8789 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8790 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8791 mHWData->mCPUCount = data.cCPUs;
8792 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8793 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8794 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8795 mHWData->mCpuProfile = data.strCpuProfile;
8796
8797 // cpu
8798 if (mHWData->mCPUHotPlugEnabled)
8799 {
8800 for (settings::CpuList::const_iterator
8801 it = data.llCpus.begin();
8802 it != data.llCpus.end();
8803 ++it)
8804 {
8805 const settings::Cpu &cpu = *it;
8806
8807 mHWData->mCPUAttached[cpu.ulId] = true;
8808 }
8809 }
8810
8811 // cpuid leafs
8812 for (settings::CpuIdLeafsList::const_iterator
8813 it = data.llCpuIdLeafs.begin();
8814 it != data.llCpuIdLeafs.end();
8815 ++it)
8816 {
8817 const settings::CpuIdLeaf &rLeaf= *it;
8818 if ( rLeaf.idx < UINT32_C(0x20)
8819 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8820 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8821 mHWData->mCpuIdLeafList.push_back(rLeaf);
8822 /* else: just ignore */
8823 }
8824
8825 mHWData->mMemorySize = data.ulMemorySizeMB;
8826 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8827
8828 // boot order
8829 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8830 {
8831 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8832 if (it == data.mapBootOrder.end())
8833 mHWData->mBootOrder[i] = DeviceType_Null;
8834 else
8835 mHWData->mBootOrder[i] = it->second;
8836 }
8837
8838 mHWData->mFirmwareType = data.firmwareType;
8839 mHWData->mPointingHIDType = data.pointingHIDType;
8840 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8841 mHWData->mChipsetType = data.chipsetType;
8842 mHWData->mParavirtProvider = data.paravirtProvider;
8843 mHWData->mParavirtDebug = data.strParavirtDebug;
8844 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8845 mHWData->mHPETEnabled = data.fHPETEnabled;
8846
8847 /* GraphicsAdapter */
8848 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8849 if (FAILED(rc)) return rc;
8850
8851 /* VRDEServer */
8852 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8853 if (FAILED(rc)) return rc;
8854
8855 /* BIOS */
8856 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8857 if (FAILED(rc)) return rc;
8858
8859 /* Recording settings */
8860 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8861 if (FAILED(rc)) return rc;
8862
8863 // Bandwidth control (must come before network adapters)
8864 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8865 if (FAILED(rc)) return rc;
8866
8867 /* USB controllers */
8868 for (settings::USBControllerList::const_iterator
8869 it = data.usbSettings.llUSBControllers.begin();
8870 it != data.usbSettings.llUSBControllers.end();
8871 ++it)
8872 {
8873 const settings::USBController &settingsCtrl = *it;
8874 ComObjPtr<USBController> newCtrl;
8875
8876 newCtrl.createObject();
8877 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8878 mUSBControllers->push_back(newCtrl);
8879 }
8880
8881 /* USB device filters */
8882 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8883 if (FAILED(rc)) return rc;
8884
8885 // network adapters (establish array size first and apply defaults, to
8886 // ensure reading the same settings as we saved, since the list skips
8887 // adapters having defaults)
8888 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8889 size_t oldCount = mNetworkAdapters.size();
8890 if (newCount > oldCount)
8891 {
8892 mNetworkAdapters.resize(newCount);
8893 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8894 {
8895 unconst(mNetworkAdapters[slot]).createObject();
8896 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8897 }
8898 }
8899 else if (newCount < oldCount)
8900 mNetworkAdapters.resize(newCount);
8901 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8902 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8903 for (settings::NetworkAdaptersList::const_iterator
8904 it = data.llNetworkAdapters.begin();
8905 it != data.llNetworkAdapters.end();
8906 ++it)
8907 {
8908 const settings::NetworkAdapter &nic = *it;
8909
8910 /* slot uniqueness is guaranteed by XML Schema */
8911 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8912 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8913 if (FAILED(rc)) return rc;
8914 }
8915
8916 // serial ports (establish defaults first, to ensure reading the same
8917 // settings as we saved, since the list skips ports having defaults)
8918 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8919 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8920 for (settings::SerialPortsList::const_iterator
8921 it = data.llSerialPorts.begin();
8922 it != data.llSerialPorts.end();
8923 ++it)
8924 {
8925 const settings::SerialPort &s = *it;
8926
8927 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8928 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8929 if (FAILED(rc)) return rc;
8930 }
8931
8932 // parallel ports (establish defaults first, to ensure reading the same
8933 // settings as we saved, since the list skips ports having defaults)
8934 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8935 mParallelPorts[i]->i_applyDefaults();
8936 for (settings::ParallelPortsList::const_iterator
8937 it = data.llParallelPorts.begin();
8938 it != data.llParallelPorts.end();
8939 ++it)
8940 {
8941 const settings::ParallelPort &p = *it;
8942
8943 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8944 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8945 if (FAILED(rc)) return rc;
8946 }
8947
8948 /* AudioAdapter */
8949 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8950 if (FAILED(rc)) return rc;
8951
8952 /* storage controllers */
8953 rc = i_loadStorageControllers(data.storage,
8954 puuidRegistry,
8955 puuidSnapshot);
8956 if (FAILED(rc)) return rc;
8957
8958 /* Shared folders */
8959 for (settings::SharedFoldersList::const_iterator
8960 it = data.llSharedFolders.begin();
8961 it != data.llSharedFolders.end();
8962 ++it)
8963 {
8964 const settings::SharedFolder &sf = *it;
8965
8966 ComObjPtr<SharedFolder> sharedFolder;
8967 /* Check for double entries. Not allowed! */
8968 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8969 if (SUCCEEDED(rc))
8970 return setError(VBOX_E_OBJECT_IN_USE,
8971 tr("Shared folder named '%s' already exists"),
8972 sf.strName.c_str());
8973
8974 /* Create the new shared folder. Don't break on error. This will be
8975 * reported when the machine starts. */
8976 sharedFolder.createObject();
8977 rc = sharedFolder->init(i_getMachine(),
8978 sf.strName,
8979 sf.strHostPath,
8980 RT_BOOL(sf.fWritable),
8981 RT_BOOL(sf.fAutoMount),
8982 sf.strAutoMountPoint,
8983 false /* fFailOnError */);
8984 if (FAILED(rc)) return rc;
8985 mHWData->mSharedFolders.push_back(sharedFolder);
8986 }
8987
8988 // Clipboard
8989 mHWData->mClipboardMode = data.clipboardMode;
8990 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8991
8992 // drag'n'drop
8993 mHWData->mDnDMode = data.dndMode;
8994
8995 // guest settings
8996 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8997
8998 // IO settings
8999 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9000 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9001
9002 // Host PCI devices
9003 for (settings::HostPCIDeviceAttachmentList::const_iterator
9004 it = data.pciAttachments.begin();
9005 it != data.pciAttachments.end();
9006 ++it)
9007 {
9008 const settings::HostPCIDeviceAttachment &hpda = *it;
9009 ComObjPtr<PCIDeviceAttachment> pda;
9010
9011 pda.createObject();
9012 pda->i_loadSettings(this, hpda);
9013 mHWData->mPCIDeviceAssignments.push_back(pda);
9014 }
9015
9016 /*
9017 * (The following isn't really real hardware, but it lives in HWData
9018 * for reasons of convenience.)
9019 */
9020
9021#ifdef VBOX_WITH_GUEST_PROPS
9022 /* Guest properties (optional) */
9023
9024 /* Only load transient guest properties for configs which have saved
9025 * state, because there shouldn't be any for powered off VMs. The same
9026 * logic applies for snapshots, as offline snapshots shouldn't have
9027 * any such properties. They confuse the code in various places.
9028 * Note: can't rely on the machine state, as it isn't set yet. */
9029 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9030 /* apologies for the hacky unconst() usage, but this needs hacking
9031 * actually inconsistent settings into consistency, otherwise there
9032 * will be some corner cases where the inconsistency survives
9033 * surprisingly long without getting fixed, especially for snapshots
9034 * as there are no config changes. */
9035 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9036 for (settings::GuestPropertiesList::iterator
9037 it = llGuestProperties.begin();
9038 it != llGuestProperties.end();
9039 /*nothing*/)
9040 {
9041 const settings::GuestProperty &prop = *it;
9042 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9043 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9044 if ( fSkipTransientGuestProperties
9045 && ( fFlags & GUEST_PROP_F_TRANSIENT
9046 || fFlags & GUEST_PROP_F_TRANSRESET))
9047 {
9048 it = llGuestProperties.erase(it);
9049 continue;
9050 }
9051 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9052 mHWData->mGuestProperties[prop.strName] = property;
9053 ++it;
9054 }
9055#endif /* VBOX_WITH_GUEST_PROPS defined */
9056
9057 rc = i_loadDebugging(pDbg);
9058 if (FAILED(rc))
9059 return rc;
9060
9061 mHWData->mAutostart = *pAutostart;
9062
9063 /* default frontend */
9064 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9065 }
9066 catch (std::bad_alloc &)
9067 {
9068 return E_OUTOFMEMORY;
9069 }
9070
9071 AssertComRC(rc);
9072 return rc;
9073}
9074
9075/**
9076 * Called from i_loadHardware() to load the debugging settings of the
9077 * machine.
9078 *
9079 * @param pDbg Pointer to the settings.
9080 */
9081HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9082{
9083 mHWData->mDebugging = *pDbg;
9084 /* no more processing currently required, this will probably change. */
9085 return S_OK;
9086}
9087
9088/**
9089 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9090 *
9091 * @param data storage settings.
9092 * @param puuidRegistry media registry ID to set media to or NULL;
9093 * see Machine::i_loadMachineDataFromSettings()
9094 * @param puuidSnapshot snapshot ID
9095 * @return
9096 */
9097HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9098 const Guid *puuidRegistry,
9099 const Guid *puuidSnapshot)
9100{
9101 AssertReturn(!i_isSessionMachine(), E_FAIL);
9102
9103 HRESULT rc = S_OK;
9104
9105 for (settings::StorageControllersList::const_iterator
9106 it = data.llStorageControllers.begin();
9107 it != data.llStorageControllers.end();
9108 ++it)
9109 {
9110 const settings::StorageController &ctlData = *it;
9111
9112 ComObjPtr<StorageController> pCtl;
9113 /* Try to find one with the name first. */
9114 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9115 if (SUCCEEDED(rc))
9116 return setError(VBOX_E_OBJECT_IN_USE,
9117 tr("Storage controller named '%s' already exists"),
9118 ctlData.strName.c_str());
9119
9120 pCtl.createObject();
9121 rc = pCtl->init(this,
9122 ctlData.strName,
9123 ctlData.storageBus,
9124 ctlData.ulInstance,
9125 ctlData.fBootable);
9126 if (FAILED(rc)) return rc;
9127
9128 mStorageControllers->push_back(pCtl);
9129
9130 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9131 if (FAILED(rc)) return rc;
9132
9133 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9134 if (FAILED(rc)) return rc;
9135
9136 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9137 if (FAILED(rc)) return rc;
9138
9139 /* Load the attached devices now. */
9140 rc = i_loadStorageDevices(pCtl,
9141 ctlData,
9142 puuidRegistry,
9143 puuidSnapshot);
9144 if (FAILED(rc)) return rc;
9145 }
9146
9147 return S_OK;
9148}
9149
9150/**
9151 * Called from i_loadStorageControllers for a controller's devices.
9152 *
9153 * @param aStorageController
9154 * @param data
9155 * @param puuidRegistry media registry ID to set media to or NULL; see
9156 * Machine::i_loadMachineDataFromSettings()
9157 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9158 * @return
9159 */
9160HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9161 const settings::StorageController &data,
9162 const Guid *puuidRegistry,
9163 const Guid *puuidSnapshot)
9164{
9165 HRESULT rc = S_OK;
9166
9167 /* paranoia: detect duplicate attachments */
9168 for (settings::AttachedDevicesList::const_iterator
9169 it = data.llAttachedDevices.begin();
9170 it != data.llAttachedDevices.end();
9171 ++it)
9172 {
9173 const settings::AttachedDevice &ad = *it;
9174
9175 for (settings::AttachedDevicesList::const_iterator it2 = it;
9176 it2 != data.llAttachedDevices.end();
9177 ++it2)
9178 {
9179 if (it == it2)
9180 continue;
9181
9182 const settings::AttachedDevice &ad2 = *it2;
9183
9184 if ( ad.lPort == ad2.lPort
9185 && ad.lDevice == ad2.lDevice)
9186 {
9187 return setError(E_FAIL,
9188 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9189 aStorageController->i_getName().c_str(),
9190 ad.lPort,
9191 ad.lDevice,
9192 mUserData->s.strName.c_str());
9193 }
9194 }
9195 }
9196
9197 for (settings::AttachedDevicesList::const_iterator
9198 it = data.llAttachedDevices.begin();
9199 it != data.llAttachedDevices.end();
9200 ++it)
9201 {
9202 const settings::AttachedDevice &dev = *it;
9203 ComObjPtr<Medium> medium;
9204
9205 switch (dev.deviceType)
9206 {
9207 case DeviceType_Floppy:
9208 case DeviceType_DVD:
9209 if (dev.strHostDriveSrc.isNotEmpty())
9210 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9211 false /* fRefresh */, medium);
9212 else
9213 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9214 dev.uuid,
9215 false /* fRefresh */,
9216 false /* aSetError */,
9217 medium);
9218 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9219 // This is not an error. The host drive or UUID might have vanished, so just go
9220 // ahead without this removeable medium attachment
9221 rc = S_OK;
9222 break;
9223
9224 case DeviceType_HardDisk:
9225 {
9226 /* find a hard disk by UUID */
9227 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9228 if (FAILED(rc))
9229 {
9230 if (i_isSnapshotMachine())
9231 {
9232 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9233 // so the user knows that the bad disk is in a snapshot somewhere
9234 com::ErrorInfo info;
9235 return setError(E_FAIL,
9236 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9237 puuidSnapshot->raw(),
9238 info.getText().raw());
9239 }
9240 else
9241 return rc;
9242 }
9243
9244 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9245
9246 if (medium->i_getType() == MediumType_Immutable)
9247 {
9248 if (i_isSnapshotMachine())
9249 return setError(E_FAIL,
9250 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9251 "of the virtual machine '%s' ('%s')"),
9252 medium->i_getLocationFull().c_str(),
9253 dev.uuid.raw(),
9254 puuidSnapshot->raw(),
9255 mUserData->s.strName.c_str(),
9256 mData->m_strConfigFileFull.c_str());
9257
9258 return setError(E_FAIL,
9259 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9260 medium->i_getLocationFull().c_str(),
9261 dev.uuid.raw(),
9262 mUserData->s.strName.c_str(),
9263 mData->m_strConfigFileFull.c_str());
9264 }
9265
9266 if (medium->i_getType() == MediumType_MultiAttach)
9267 {
9268 if (i_isSnapshotMachine())
9269 return setError(E_FAIL,
9270 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9271 "of the virtual machine '%s' ('%s')"),
9272 medium->i_getLocationFull().c_str(),
9273 dev.uuid.raw(),
9274 puuidSnapshot->raw(),
9275 mUserData->s.strName.c_str(),
9276 mData->m_strConfigFileFull.c_str());
9277
9278 return setError(E_FAIL,
9279 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9280 medium->i_getLocationFull().c_str(),
9281 dev.uuid.raw(),
9282 mUserData->s.strName.c_str(),
9283 mData->m_strConfigFileFull.c_str());
9284 }
9285
9286 if ( !i_isSnapshotMachine()
9287 && medium->i_getChildren().size() != 0
9288 )
9289 return setError(E_FAIL,
9290 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9291 "because it has %d differencing child hard disks"),
9292 medium->i_getLocationFull().c_str(),
9293 dev.uuid.raw(),
9294 mUserData->s.strName.c_str(),
9295 mData->m_strConfigFileFull.c_str(),
9296 medium->i_getChildren().size());
9297
9298 if (i_findAttachment(*mMediumAttachments.data(),
9299 medium))
9300 return setError(E_FAIL,
9301 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9302 medium->i_getLocationFull().c_str(),
9303 dev.uuid.raw(),
9304 mUserData->s.strName.c_str(),
9305 mData->m_strConfigFileFull.c_str());
9306
9307 break;
9308 }
9309
9310 default:
9311 return setError(E_FAIL,
9312 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9313 medium->i_getLocationFull().c_str(),
9314 mUserData->s.strName.c_str(),
9315 mData->m_strConfigFileFull.c_str());
9316 }
9317
9318 if (FAILED(rc))
9319 break;
9320
9321 /* Bandwidth groups are loaded at this point. */
9322 ComObjPtr<BandwidthGroup> pBwGroup;
9323
9324 if (!dev.strBwGroup.isEmpty())
9325 {
9326 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9327 if (FAILED(rc))
9328 return setError(E_FAIL,
9329 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9330 medium->i_getLocationFull().c_str(),
9331 dev.strBwGroup.c_str(),
9332 mUserData->s.strName.c_str(),
9333 mData->m_strConfigFileFull.c_str());
9334 pBwGroup->i_reference();
9335 }
9336
9337 const Utf8Str controllerName = aStorageController->i_getName();
9338 ComObjPtr<MediumAttachment> pAttachment;
9339 pAttachment.createObject();
9340 rc = pAttachment->init(this,
9341 medium,
9342 controllerName,
9343 dev.lPort,
9344 dev.lDevice,
9345 dev.deviceType,
9346 false,
9347 dev.fPassThrough,
9348 dev.fTempEject,
9349 dev.fNonRotational,
9350 dev.fDiscard,
9351 dev.fHotPluggable,
9352 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9353 if (FAILED(rc)) break;
9354
9355 /* associate the medium with this machine and snapshot */
9356 if (!medium.isNull())
9357 {
9358 AutoCaller medCaller(medium);
9359 if (FAILED(medCaller.rc())) return medCaller.rc();
9360 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9361
9362 if (i_isSnapshotMachine())
9363 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9364 else
9365 rc = medium->i_addBackReference(mData->mUuid);
9366 /* If the medium->addBackReference fails it sets an appropriate
9367 * error message, so no need to do any guesswork here. */
9368
9369 if (puuidRegistry)
9370 // caller wants registry ID to be set on all attached media (OVF import case)
9371 medium->i_addRegistry(*puuidRegistry);
9372 }
9373
9374 if (FAILED(rc))
9375 break;
9376
9377 /* back up mMediumAttachments to let registeredInit() properly rollback
9378 * on failure (= limited accessibility) */
9379 i_setModified(IsModified_Storage);
9380 mMediumAttachments.backup();
9381 mMediumAttachments->push_back(pAttachment);
9382 }
9383
9384 return rc;
9385}
9386
9387/**
9388 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9389 *
9390 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9391 * @param aSnapshot where to return the found snapshot
9392 * @param aSetError true to set extended error info on failure
9393 */
9394HRESULT Machine::i_findSnapshotById(const Guid &aId,
9395 ComObjPtr<Snapshot> &aSnapshot,
9396 bool aSetError /* = false */)
9397{
9398 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9399
9400 if (!mData->mFirstSnapshot)
9401 {
9402 if (aSetError)
9403 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9404 return E_FAIL;
9405 }
9406
9407 if (aId.isZero())
9408 aSnapshot = mData->mFirstSnapshot;
9409 else
9410 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9411
9412 if (!aSnapshot)
9413 {
9414 if (aSetError)
9415 return setError(E_FAIL,
9416 tr("Could not find a snapshot with UUID {%s}"),
9417 aId.toString().c_str());
9418 return E_FAIL;
9419 }
9420
9421 return S_OK;
9422}
9423
9424/**
9425 * Returns the snapshot with the given name or fails of no such snapshot.
9426 *
9427 * @param strName snapshot name to find
9428 * @param aSnapshot where to return the found snapshot
9429 * @param aSetError true to set extended error info on failure
9430 */
9431HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9432 ComObjPtr<Snapshot> &aSnapshot,
9433 bool aSetError /* = false */)
9434{
9435 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9436
9437 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9438
9439 if (!mData->mFirstSnapshot)
9440 {
9441 if (aSetError)
9442 return setError(VBOX_E_OBJECT_NOT_FOUND,
9443 tr("This machine does not have any snapshots"));
9444 return VBOX_E_OBJECT_NOT_FOUND;
9445 }
9446
9447 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9448
9449 if (!aSnapshot)
9450 {
9451 if (aSetError)
9452 return setError(VBOX_E_OBJECT_NOT_FOUND,
9453 tr("Could not find a snapshot named '%s'"), strName.c_str());
9454 return VBOX_E_OBJECT_NOT_FOUND;
9455 }
9456
9457 return S_OK;
9458}
9459
9460/**
9461 * Returns a storage controller object with the given name.
9462 *
9463 * @param aName storage controller name to find
9464 * @param aStorageController where to return the found storage controller
9465 * @param aSetError true to set extended error info on failure
9466 */
9467HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9468 ComObjPtr<StorageController> &aStorageController,
9469 bool aSetError /* = false */)
9470{
9471 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9472
9473 for (StorageControllerList::const_iterator
9474 it = mStorageControllers->begin();
9475 it != mStorageControllers->end();
9476 ++it)
9477 {
9478 if ((*it)->i_getName() == aName)
9479 {
9480 aStorageController = (*it);
9481 return S_OK;
9482 }
9483 }
9484
9485 if (aSetError)
9486 return setError(VBOX_E_OBJECT_NOT_FOUND,
9487 tr("Could not find a storage controller named '%s'"),
9488 aName.c_str());
9489 return VBOX_E_OBJECT_NOT_FOUND;
9490}
9491
9492/**
9493 * Returns a USB controller object with the given name.
9494 *
9495 * @param aName USB controller name to find
9496 * @param aUSBController where to return the found USB controller
9497 * @param aSetError true to set extended error info on failure
9498 */
9499HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9500 ComObjPtr<USBController> &aUSBController,
9501 bool aSetError /* = false */)
9502{
9503 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9504
9505 for (USBControllerList::const_iterator
9506 it = mUSBControllers->begin();
9507 it != mUSBControllers->end();
9508 ++it)
9509 {
9510 if ((*it)->i_getName() == aName)
9511 {
9512 aUSBController = (*it);
9513 return S_OK;
9514 }
9515 }
9516
9517 if (aSetError)
9518 return setError(VBOX_E_OBJECT_NOT_FOUND,
9519 tr("Could not find a storage controller named '%s'"),
9520 aName.c_str());
9521 return VBOX_E_OBJECT_NOT_FOUND;
9522}
9523
9524/**
9525 * Returns the number of USB controller instance of the given type.
9526 *
9527 * @param enmType USB controller type.
9528 */
9529ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9530{
9531 ULONG cCtrls = 0;
9532
9533 for (USBControllerList::const_iterator
9534 it = mUSBControllers->begin();
9535 it != mUSBControllers->end();
9536 ++it)
9537 {
9538 if ((*it)->i_getControllerType() == enmType)
9539 cCtrls++;
9540 }
9541
9542 return cCtrls;
9543}
9544
9545HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9546 MediumAttachmentList &atts)
9547{
9548 AutoCaller autoCaller(this);
9549 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9550
9551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9552
9553 for (MediumAttachmentList::const_iterator
9554 it = mMediumAttachments->begin();
9555 it != mMediumAttachments->end();
9556 ++it)
9557 {
9558 const ComObjPtr<MediumAttachment> &pAtt = *it;
9559 // should never happen, but deal with NULL pointers in the list.
9560 AssertContinue(!pAtt.isNull());
9561
9562 // getControllerName() needs caller+read lock
9563 AutoCaller autoAttCaller(pAtt);
9564 if (FAILED(autoAttCaller.rc()))
9565 {
9566 atts.clear();
9567 return autoAttCaller.rc();
9568 }
9569 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9570
9571 if (pAtt->i_getControllerName() == aName)
9572 atts.push_back(pAtt);
9573 }
9574
9575 return S_OK;
9576}
9577
9578
9579/**
9580 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9581 * file if the machine name was changed and about creating a new settings file
9582 * if this is a new machine.
9583 *
9584 * @note Must be never called directly but only from #saveSettings().
9585 */
9586HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9587{
9588 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9589
9590 HRESULT rc = S_OK;
9591
9592 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9593
9594 /// @todo need to handle primary group change, too
9595
9596 /* attempt to rename the settings file if machine name is changed */
9597 if ( mUserData->s.fNameSync
9598 && mUserData.isBackedUp()
9599 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9600 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9601 )
9602 {
9603 bool dirRenamed = false;
9604 bool fileRenamed = false;
9605
9606 Utf8Str configFile, newConfigFile;
9607 Utf8Str configFilePrev, newConfigFilePrev;
9608 Utf8Str NVRAMFile, newNVRAMFile;
9609 Utf8Str configDir, newConfigDir;
9610
9611 do
9612 {
9613 int vrc = VINF_SUCCESS;
9614
9615 Utf8Str name = mUserData.backedUpData()->s.strName;
9616 Utf8Str newName = mUserData->s.strName;
9617 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9618 if (group == "/")
9619 group.setNull();
9620 Utf8Str newGroup = mUserData->s.llGroups.front();
9621 if (newGroup == "/")
9622 newGroup.setNull();
9623
9624 configFile = mData->m_strConfigFileFull;
9625
9626 /* first, rename the directory if it matches the group and machine name */
9627 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9628 group.c_str(), RTPATH_DELIMITER, name.c_str());
9629 /** @todo hack, make somehow use of ComposeMachineFilename */
9630 if (mUserData->s.fDirectoryIncludesUUID)
9631 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9632 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9633 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9634 /** @todo hack, make somehow use of ComposeMachineFilename */
9635 if (mUserData->s.fDirectoryIncludesUUID)
9636 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9637 configDir = configFile;
9638 configDir.stripFilename();
9639 newConfigDir = configDir;
9640 if ( configDir.length() >= groupPlusName.length()
9641 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9642 groupPlusName.c_str()))
9643 {
9644 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9645 Utf8Str newConfigBaseDir(newConfigDir);
9646 newConfigDir.append(newGroupPlusName);
9647 /* consistency: use \ if appropriate on the platform */
9648 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9649 /* new dir and old dir cannot be equal here because of 'if'
9650 * above and because name != newName */
9651 Assert(configDir != newConfigDir);
9652 if (!fSettingsFileIsNew)
9653 {
9654 /* perform real rename only if the machine is not new */
9655 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9656 if ( vrc == VERR_FILE_NOT_FOUND
9657 || vrc == VERR_PATH_NOT_FOUND)
9658 {
9659 /* create the parent directory, then retry renaming */
9660 Utf8Str parent(newConfigDir);
9661 parent.stripFilename();
9662 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9663 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9664 }
9665 if (RT_FAILURE(vrc))
9666 {
9667 rc = setErrorBoth(E_FAIL, vrc,
9668 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9669 configDir.c_str(),
9670 newConfigDir.c_str(),
9671 vrc);
9672 break;
9673 }
9674 /* delete subdirectories which are no longer needed */
9675 Utf8Str dir(configDir);
9676 dir.stripFilename();
9677 while (dir != newConfigBaseDir && dir != ".")
9678 {
9679 vrc = RTDirRemove(dir.c_str());
9680 if (RT_FAILURE(vrc))
9681 break;
9682 dir.stripFilename();
9683 }
9684 dirRenamed = true;
9685 }
9686 }
9687
9688 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9689 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9690
9691 /* then try to rename the settings file itself */
9692 if (newConfigFile != configFile)
9693 {
9694 /* get the path to old settings file in renamed directory */
9695 configFile = Utf8StrFmt("%s%c%s",
9696 newConfigDir.c_str(),
9697 RTPATH_DELIMITER,
9698 RTPathFilename(configFile.c_str()));
9699 if (!fSettingsFileIsNew)
9700 {
9701 /* perform real rename only if the machine is not new */
9702 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9703 if (RT_FAILURE(vrc))
9704 {
9705 rc = setErrorBoth(E_FAIL, vrc,
9706 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9707 configFile.c_str(),
9708 newConfigFile.c_str(),
9709 vrc);
9710 break;
9711 }
9712 fileRenamed = true;
9713 configFilePrev = configFile;
9714 configFilePrev += "-prev";
9715 newConfigFilePrev = newConfigFile;
9716 newConfigFilePrev += "-prev";
9717 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9718 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9719 if (NVRAMFile.isNotEmpty())
9720 {
9721 // in the NVRAM file path, replace the old directory with the new directory
9722 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9723 {
9724 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9725 NVRAMFile = newConfigDir + strNVRAMFile;
9726 }
9727 newNVRAMFile = newConfigFile;
9728 newNVRAMFile.stripSuffix();
9729 newNVRAMFile += ".nvram";
9730 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9731 }
9732 }
9733 }
9734
9735 // update m_strConfigFileFull amd mConfigFile
9736 mData->m_strConfigFileFull = newConfigFile;
9737 // compute the relative path too
9738 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9739
9740 // store the old and new so that VirtualBox::i_saveSettings() can update
9741 // the media registry
9742 if ( mData->mRegistered
9743 && (configDir != newConfigDir || configFile != newConfigFile))
9744 {
9745 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9746
9747 if (pfNeedsGlobalSaveSettings)
9748 *pfNeedsGlobalSaveSettings = true;
9749 }
9750
9751 // in the saved state file path, replace the old directory with the new directory
9752 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9753 {
9754 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9755 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9756 }
9757 if (newNVRAMFile.isNotEmpty())
9758 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9759
9760 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9761 if (mData->mFirstSnapshot)
9762 {
9763 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9764 newConfigDir.c_str());
9765 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9766 newConfigDir.c_str());
9767 }
9768 }
9769 while (0);
9770
9771 if (FAILED(rc))
9772 {
9773 /* silently try to rename everything back */
9774 if (fileRenamed)
9775 {
9776 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9777 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9778 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9779 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9780 }
9781 if (dirRenamed)
9782 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9783 }
9784
9785 if (FAILED(rc)) return rc;
9786 }
9787
9788 if (fSettingsFileIsNew)
9789 {
9790 /* create a virgin config file */
9791 int vrc = VINF_SUCCESS;
9792
9793 /* ensure the settings directory exists */
9794 Utf8Str path(mData->m_strConfigFileFull);
9795 path.stripFilename();
9796 if (!RTDirExists(path.c_str()))
9797 {
9798 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9799 if (RT_FAILURE(vrc))
9800 {
9801 return setErrorBoth(E_FAIL, vrc,
9802 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9803 path.c_str(),
9804 vrc);
9805 }
9806 }
9807
9808 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9809 path = Utf8Str(mData->m_strConfigFileFull);
9810 RTFILE f = NIL_RTFILE;
9811 vrc = RTFileOpen(&f, path.c_str(),
9812 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9813 if (RT_FAILURE(vrc))
9814 return setErrorBoth(E_FAIL, vrc,
9815 tr("Could not create the settings file '%s' (%Rrc)"),
9816 path.c_str(),
9817 vrc);
9818 RTFileClose(f);
9819 }
9820
9821 return rc;
9822}
9823
9824/**
9825 * Saves and commits machine data, user data and hardware data.
9826 *
9827 * Note that on failure, the data remains uncommitted.
9828 *
9829 * @a aFlags may combine the following flags:
9830 *
9831 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9832 * Used when saving settings after an operation that makes them 100%
9833 * correspond to the settings from the current snapshot.
9834 * - SaveS_Force: settings will be saved without doing a deep compare of the
9835 * settings structures. This is used when this is called because snapshots
9836 * have changed to avoid the overhead of the deep compare.
9837 *
9838 * @note Must be called from under this object's write lock. Locks children for
9839 * writing.
9840 *
9841 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9842 * initialized to false and that will be set to true by this function if
9843 * the caller must invoke VirtualBox::i_saveSettings() because the global
9844 * settings have changed. This will happen if a machine rename has been
9845 * saved and the global machine and media registries will therefore need
9846 * updating.
9847 * @param aFlags Flags.
9848 */
9849HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9850 int aFlags /*= 0*/)
9851{
9852 LogFlowThisFuncEnter();
9853
9854 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9855
9856 /* make sure child objects are unable to modify the settings while we are
9857 * saving them */
9858 i_ensureNoStateDependencies();
9859
9860 AssertReturn(!i_isSnapshotMachine(),
9861 E_FAIL);
9862
9863 if (!mData->mAccessible)
9864 return setError(VBOX_E_INVALID_VM_STATE,
9865 tr("The machine is not accessible, so cannot save settings"));
9866
9867 HRESULT rc = S_OK;
9868 bool fNeedsWrite = false;
9869
9870 /* First, prepare to save settings. It will care about renaming the
9871 * settings directory and file if the machine name was changed and about
9872 * creating a new settings file if this is a new machine. */
9873 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9874 if (FAILED(rc)) return rc;
9875
9876 // keep a pointer to the current settings structures
9877 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9878 settings::MachineConfigFile *pNewConfig = NULL;
9879
9880 try
9881 {
9882 // make a fresh one to have everyone write stuff into
9883 pNewConfig = new settings::MachineConfigFile(NULL);
9884 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9885
9886 // now go and copy all the settings data from COM to the settings structures
9887 // (this calls i_saveSettings() on all the COM objects in the machine)
9888 i_copyMachineDataToSettings(*pNewConfig);
9889
9890 if (aFlags & SaveS_ResetCurStateModified)
9891 {
9892 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9893 mData->mCurrentStateModified = FALSE;
9894 fNeedsWrite = true; // always, no need to compare
9895 }
9896 else if (aFlags & SaveS_Force)
9897 {
9898 fNeedsWrite = true; // always, no need to compare
9899 }
9900 else
9901 {
9902 if (!mData->mCurrentStateModified)
9903 {
9904 // do a deep compare of the settings that we just saved with the settings
9905 // previously stored in the config file; this invokes MachineConfigFile::operator==
9906 // which does a deep compare of all the settings, which is expensive but less expensive
9907 // than writing out XML in vain
9908 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9909
9910 // could still be modified if any settings changed
9911 mData->mCurrentStateModified = fAnySettingsChanged;
9912
9913 fNeedsWrite = fAnySettingsChanged;
9914 }
9915 else
9916 fNeedsWrite = true;
9917 }
9918
9919 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9920
9921 if (fNeedsWrite)
9922 // now spit it all out!
9923 pNewConfig->write(mData->m_strConfigFileFull);
9924
9925 mData->pMachineConfigFile = pNewConfig;
9926 delete pOldConfig;
9927 i_commit();
9928
9929 // after saving settings, we are no longer different from the XML on disk
9930 mData->flModifications = 0;
9931 }
9932 catch (HRESULT err)
9933 {
9934 // we assume that error info is set by the thrower
9935 rc = err;
9936
9937 // restore old config
9938 delete pNewConfig;
9939 mData->pMachineConfigFile = pOldConfig;
9940 }
9941 catch (...)
9942 {
9943 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9944 }
9945
9946 if (fNeedsWrite)
9947 {
9948 /* Fire the data change event, even on failure (since we've already
9949 * committed all data). This is done only for SessionMachines because
9950 * mutable Machine instances are always not registered (i.e. private
9951 * to the client process that creates them) and thus don't need to
9952 * inform callbacks. */
9953 if (i_isSessionMachine())
9954 mParent->i_onMachineDataChange(mData->mUuid);
9955 }
9956
9957 LogFlowThisFunc(("rc=%08X\n", rc));
9958 LogFlowThisFuncLeave();
9959 return rc;
9960}
9961
9962/**
9963 * Implementation for saving the machine settings into the given
9964 * settings::MachineConfigFile instance. This copies machine extradata
9965 * from the previous machine config file in the instance data, if any.
9966 *
9967 * This gets called from two locations:
9968 *
9969 * -- Machine::i_saveSettings(), during the regular XML writing;
9970 *
9971 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9972 * exported to OVF and we write the VirtualBox proprietary XML
9973 * into a <vbox:Machine> tag.
9974 *
9975 * This routine fills all the fields in there, including snapshots, *except*
9976 * for the following:
9977 *
9978 * -- fCurrentStateModified. There is some special logic associated with that.
9979 *
9980 * The caller can then call MachineConfigFile::write() or do something else
9981 * with it.
9982 *
9983 * Caller must hold the machine lock!
9984 *
9985 * This throws XML errors and HRESULT, so the caller must have a catch block!
9986 */
9987void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9988{
9989 // deep copy extradata, being extra careful with self assignment (the STL
9990 // map assignment on Mac OS X clang based Xcode isn't checking)
9991 if (&config != mData->pMachineConfigFile)
9992 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9993
9994 config.uuid = mData->mUuid;
9995
9996 // copy name, description, OS type, teleport, UTC etc.
9997 config.machineUserData = mUserData->s;
9998
9999 if ( mData->mMachineState == MachineState_Saved
10000 || mData->mMachineState == MachineState_Restoring
10001 // when doing certain snapshot operations we may or may not have
10002 // a saved state in the current state, so keep everything as is
10003 || ( ( mData->mMachineState == MachineState_Snapshotting
10004 || mData->mMachineState == MachineState_DeletingSnapshot
10005 || mData->mMachineState == MachineState_RestoringSnapshot)
10006 && (!mSSData->strStateFilePath.isEmpty())
10007 )
10008 )
10009 {
10010 Assert(!mSSData->strStateFilePath.isEmpty());
10011 /* try to make the file name relative to the settings file dir */
10012 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10013 }
10014 else
10015 {
10016 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10017 config.strStateFile.setNull();
10018 }
10019
10020 if (mData->mCurrentSnapshot)
10021 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10022 else
10023 config.uuidCurrentSnapshot.clear();
10024
10025 config.timeLastStateChange = mData->mLastStateChange;
10026 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10027 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10028
10029 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10030 if (FAILED(rc)) throw rc;
10031
10032 // save machine's media registry if this is VirtualBox 4.0 or later
10033 if (config.canHaveOwnMediaRegistry())
10034 {
10035 // determine machine folder
10036 Utf8Str strMachineFolder = i_getSettingsFileFull();
10037 strMachineFolder.stripFilename();
10038 mParent->i_saveMediaRegistry(config.mediaRegistry,
10039 i_getId(), // only media with registry ID == machine UUID
10040 strMachineFolder);
10041 // this throws HRESULT
10042 }
10043
10044 // save snapshots
10045 rc = i_saveAllSnapshots(config);
10046 if (FAILED(rc)) throw rc;
10047}
10048
10049/**
10050 * Saves all snapshots of the machine into the given machine config file. Called
10051 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10052 * @param config
10053 * @return
10054 */
10055HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10056{
10057 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10058
10059 HRESULT rc = S_OK;
10060
10061 try
10062 {
10063 config.llFirstSnapshot.clear();
10064
10065 if (mData->mFirstSnapshot)
10066 {
10067 // the settings use a list for "the first snapshot"
10068 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10069
10070 // get reference to the snapshot on the list and work on that
10071 // element straight in the list to avoid excessive copying later
10072 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10073 if (FAILED(rc)) throw rc;
10074 }
10075
10076// if (mType == IsSessionMachine)
10077// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10078
10079 }
10080 catch (HRESULT err)
10081 {
10082 /* we assume that error info is set by the thrower */
10083 rc = err;
10084 }
10085 catch (...)
10086 {
10087 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10088 }
10089
10090 return rc;
10091}
10092
10093/**
10094 * Saves the VM hardware configuration. It is assumed that the
10095 * given node is empty.
10096 *
10097 * @param data Reference to the settings object for the hardware config.
10098 * @param pDbg Pointer to the settings object for the debugging config
10099 * which happens to live in mHWData.
10100 * @param pAutostart Pointer to the settings object for the autostart config
10101 * which happens to live in mHWData.
10102 */
10103HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10104 settings::Autostart *pAutostart)
10105{
10106 HRESULT rc = S_OK;
10107
10108 try
10109 {
10110 /* The hardware version attribute (optional).
10111 Automatically upgrade from 1 to current default hardware version
10112 when there is no saved state. (ugly!) */
10113 if ( mHWData->mHWVersion == "1"
10114 && mSSData->strStateFilePath.isEmpty()
10115 )
10116 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10117
10118 data.strVersion = mHWData->mHWVersion;
10119 data.uuid = mHWData->mHardwareUUID;
10120
10121 // CPU
10122 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10123 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10124 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10125 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10126 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10127 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10128 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10129 data.fPAE = !!mHWData->mPAEEnabled;
10130 data.enmLongMode = mHWData->mLongMode;
10131 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10132 data.fAPIC = !!mHWData->mAPIC;
10133 data.fX2APIC = !!mHWData->mX2APIC;
10134 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10135 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10136 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10137 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10138 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10139 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10140 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10141 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10142 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10143 data.cCPUs = mHWData->mCPUCount;
10144 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10145 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10146 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10147 data.strCpuProfile = mHWData->mCpuProfile;
10148
10149 data.llCpus.clear();
10150 if (data.fCpuHotPlug)
10151 {
10152 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10153 {
10154 if (mHWData->mCPUAttached[idx])
10155 {
10156 settings::Cpu cpu;
10157 cpu.ulId = idx;
10158 data.llCpus.push_back(cpu);
10159 }
10160 }
10161 }
10162
10163 /* Standard and Extended CPUID leafs. */
10164 data.llCpuIdLeafs.clear();
10165 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10166
10167 // memory
10168 data.ulMemorySizeMB = mHWData->mMemorySize;
10169 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10170
10171 // firmware
10172 data.firmwareType = mHWData->mFirmwareType;
10173
10174 // HID
10175 data.pointingHIDType = mHWData->mPointingHIDType;
10176 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10177
10178 // chipset
10179 data.chipsetType = mHWData->mChipsetType;
10180
10181 // paravirt
10182 data.paravirtProvider = mHWData->mParavirtProvider;
10183 data.strParavirtDebug = mHWData->mParavirtDebug;
10184
10185 // emulated USB card reader
10186 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10187
10188 // HPET
10189 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10190
10191 // boot order
10192 data.mapBootOrder.clear();
10193 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10194 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10195
10196 /* VRDEServer settings (optional) */
10197 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10198 if (FAILED(rc)) throw rc;
10199
10200 /* BIOS settings (required) */
10201 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10202 if (FAILED(rc)) throw rc;
10203
10204 /* Recording settings (required) */
10205 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10206 if (FAILED(rc)) throw rc;
10207
10208 /* GraphicsAdapter settings (required) */
10209 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10210 if (FAILED(rc)) throw rc;
10211
10212 /* USB Controller (required) */
10213 data.usbSettings.llUSBControllers.clear();
10214 for (USBControllerList::const_iterator
10215 it = mUSBControllers->begin();
10216 it != mUSBControllers->end();
10217 ++it)
10218 {
10219 ComObjPtr<USBController> ctrl = *it;
10220 settings::USBController settingsCtrl;
10221
10222 settingsCtrl.strName = ctrl->i_getName();
10223 settingsCtrl.enmType = ctrl->i_getControllerType();
10224
10225 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10226 }
10227
10228 /* USB device filters (required) */
10229 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10230 if (FAILED(rc)) throw rc;
10231
10232 /* Network adapters (required) */
10233 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10234 data.llNetworkAdapters.clear();
10235 /* Write out only the nominal number of network adapters for this
10236 * chipset type. Since Machine::commit() hasn't been called there
10237 * may be extra NIC settings in the vector. */
10238 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10239 {
10240 settings::NetworkAdapter nic;
10241 nic.ulSlot = (uint32_t)slot;
10242 /* paranoia check... must not be NULL, but must not crash either. */
10243 if (mNetworkAdapters[slot])
10244 {
10245 if (mNetworkAdapters[slot]->i_hasDefaults())
10246 continue;
10247
10248 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10249 if (FAILED(rc)) throw rc;
10250
10251 data.llNetworkAdapters.push_back(nic);
10252 }
10253 }
10254
10255 /* Serial ports */
10256 data.llSerialPorts.clear();
10257 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10258 {
10259 if (mSerialPorts[slot]->i_hasDefaults())
10260 continue;
10261
10262 settings::SerialPort s;
10263 s.ulSlot = slot;
10264 rc = mSerialPorts[slot]->i_saveSettings(s);
10265 if (FAILED(rc)) return rc;
10266
10267 data.llSerialPorts.push_back(s);
10268 }
10269
10270 /* Parallel ports */
10271 data.llParallelPorts.clear();
10272 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10273 {
10274 if (mParallelPorts[slot]->i_hasDefaults())
10275 continue;
10276
10277 settings::ParallelPort p;
10278 p.ulSlot = slot;
10279 rc = mParallelPorts[slot]->i_saveSettings(p);
10280 if (FAILED(rc)) return rc;
10281
10282 data.llParallelPorts.push_back(p);
10283 }
10284
10285 /* Audio adapter */
10286 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10287 if (FAILED(rc)) return rc;
10288
10289 rc = i_saveStorageControllers(data.storage);
10290 if (FAILED(rc)) return rc;
10291
10292 /* Shared folders */
10293 data.llSharedFolders.clear();
10294 for (HWData::SharedFolderList::const_iterator
10295 it = mHWData->mSharedFolders.begin();
10296 it != mHWData->mSharedFolders.end();
10297 ++it)
10298 {
10299 SharedFolder *pSF = *it;
10300 AutoCaller sfCaller(pSF);
10301 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10302 settings::SharedFolder sf;
10303 sf.strName = pSF->i_getName();
10304 sf.strHostPath = pSF->i_getHostPath();
10305 sf.fWritable = !!pSF->i_isWritable();
10306 sf.fAutoMount = !!pSF->i_isAutoMounted();
10307 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10308
10309 data.llSharedFolders.push_back(sf);
10310 }
10311
10312 // clipboard
10313 data.clipboardMode = mHWData->mClipboardMode;
10314 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10315
10316 // drag'n'drop
10317 data.dndMode = mHWData->mDnDMode;
10318
10319 /* Guest */
10320 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10321
10322 // IO settings
10323 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10324 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10325
10326 /* BandwidthControl (required) */
10327 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10328 if (FAILED(rc)) throw rc;
10329
10330 /* Host PCI devices */
10331 data.pciAttachments.clear();
10332 for (HWData::PCIDeviceAssignmentList::const_iterator
10333 it = mHWData->mPCIDeviceAssignments.begin();
10334 it != mHWData->mPCIDeviceAssignments.end();
10335 ++it)
10336 {
10337 ComObjPtr<PCIDeviceAttachment> pda = *it;
10338 settings::HostPCIDeviceAttachment hpda;
10339
10340 rc = pda->i_saveSettings(hpda);
10341 if (FAILED(rc)) throw rc;
10342
10343 data.pciAttachments.push_back(hpda);
10344 }
10345
10346 // guest properties
10347 data.llGuestProperties.clear();
10348#ifdef VBOX_WITH_GUEST_PROPS
10349 for (HWData::GuestPropertyMap::const_iterator
10350 it = mHWData->mGuestProperties.begin();
10351 it != mHWData->mGuestProperties.end();
10352 ++it)
10353 {
10354 HWData::GuestProperty property = it->second;
10355
10356 /* Remove transient guest properties at shutdown unless we
10357 * are saving state. Note that restoring snapshot intentionally
10358 * keeps them, they will be removed if appropriate once the final
10359 * machine state is set (as crashes etc. need to work). */
10360 if ( ( mData->mMachineState == MachineState_PoweredOff
10361 || mData->mMachineState == MachineState_Aborted
10362 || mData->mMachineState == MachineState_Teleported)
10363 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10364 continue;
10365 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10366 prop.strName = it->first;
10367 prop.strValue = property.strValue;
10368 prop.timestamp = property.mTimestamp;
10369 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10370 GuestPropWriteFlags(property.mFlags, szFlags);
10371 prop.strFlags = szFlags;
10372
10373 data.llGuestProperties.push_back(prop);
10374 }
10375
10376 /* I presume this doesn't require a backup(). */
10377 mData->mGuestPropertiesModified = FALSE;
10378#endif /* VBOX_WITH_GUEST_PROPS defined */
10379
10380 *pDbg = mHWData->mDebugging;
10381 *pAutostart = mHWData->mAutostart;
10382
10383 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10384 }
10385 catch (std::bad_alloc &)
10386 {
10387 return E_OUTOFMEMORY;
10388 }
10389
10390 AssertComRC(rc);
10391 return rc;
10392}
10393
10394/**
10395 * Saves the storage controller configuration.
10396 *
10397 * @param data storage settings.
10398 */
10399HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10400{
10401 data.llStorageControllers.clear();
10402
10403 for (StorageControllerList::const_iterator
10404 it = mStorageControllers->begin();
10405 it != mStorageControllers->end();
10406 ++it)
10407 {
10408 HRESULT rc;
10409 ComObjPtr<StorageController> pCtl = *it;
10410
10411 settings::StorageController ctl;
10412 ctl.strName = pCtl->i_getName();
10413 ctl.controllerType = pCtl->i_getControllerType();
10414 ctl.storageBus = pCtl->i_getStorageBus();
10415 ctl.ulInstance = pCtl->i_getInstance();
10416 ctl.fBootable = pCtl->i_getBootable();
10417
10418 /* Save the port count. */
10419 ULONG portCount;
10420 rc = pCtl->COMGETTER(PortCount)(&portCount);
10421 ComAssertComRCRet(rc, rc);
10422 ctl.ulPortCount = portCount;
10423
10424 /* Save fUseHostIOCache */
10425 BOOL fUseHostIOCache;
10426 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10427 ComAssertComRCRet(rc, rc);
10428 ctl.fUseHostIOCache = !!fUseHostIOCache;
10429
10430 /* save the devices now. */
10431 rc = i_saveStorageDevices(pCtl, ctl);
10432 ComAssertComRCRet(rc, rc);
10433
10434 data.llStorageControllers.push_back(ctl);
10435 }
10436
10437 return S_OK;
10438}
10439
10440/**
10441 * Saves the hard disk configuration.
10442 */
10443HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10444 settings::StorageController &data)
10445{
10446 MediumAttachmentList atts;
10447
10448 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10449 if (FAILED(rc)) return rc;
10450
10451 data.llAttachedDevices.clear();
10452 for (MediumAttachmentList::const_iterator
10453 it = atts.begin();
10454 it != atts.end();
10455 ++it)
10456 {
10457 settings::AttachedDevice dev;
10458 IMediumAttachment *iA = *it;
10459 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10460 Medium *pMedium = pAttach->i_getMedium();
10461
10462 dev.deviceType = pAttach->i_getType();
10463 dev.lPort = pAttach->i_getPort();
10464 dev.lDevice = pAttach->i_getDevice();
10465 dev.fPassThrough = pAttach->i_getPassthrough();
10466 dev.fHotPluggable = pAttach->i_getHotPluggable();
10467 if (pMedium)
10468 {
10469 if (pMedium->i_isHostDrive())
10470 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10471 else
10472 dev.uuid = pMedium->i_getId();
10473 dev.fTempEject = pAttach->i_getTempEject();
10474 dev.fNonRotational = pAttach->i_getNonRotational();
10475 dev.fDiscard = pAttach->i_getDiscard();
10476 }
10477
10478 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10479
10480 data.llAttachedDevices.push_back(dev);
10481 }
10482
10483 return S_OK;
10484}
10485
10486/**
10487 * Saves machine state settings as defined by aFlags
10488 * (SaveSTS_* values).
10489 *
10490 * @param aFlags Combination of SaveSTS_* flags.
10491 *
10492 * @note Locks objects for writing.
10493 */
10494HRESULT Machine::i_saveStateSettings(int aFlags)
10495{
10496 if (aFlags == 0)
10497 return S_OK;
10498
10499 AutoCaller autoCaller(this);
10500 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10501
10502 /* This object's write lock is also necessary to serialize file access
10503 * (prevent concurrent reads and writes) */
10504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10505
10506 HRESULT rc = S_OK;
10507
10508 Assert(mData->pMachineConfigFile);
10509
10510 try
10511 {
10512 if (aFlags & SaveSTS_CurStateModified)
10513 mData->pMachineConfigFile->fCurrentStateModified = true;
10514
10515 if (aFlags & SaveSTS_StateFilePath)
10516 {
10517 if (!mSSData->strStateFilePath.isEmpty())
10518 /* try to make the file name relative to the settings file dir */
10519 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10520 else
10521 mData->pMachineConfigFile->strStateFile.setNull();
10522 }
10523
10524 if (aFlags & SaveSTS_StateTimeStamp)
10525 {
10526 Assert( mData->mMachineState != MachineState_Aborted
10527 || mSSData->strStateFilePath.isEmpty());
10528
10529 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10530
10531 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10532/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10533 }
10534
10535 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10536 }
10537 catch (...)
10538 {
10539 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10540 }
10541
10542 return rc;
10543}
10544
10545/**
10546 * Ensures that the given medium is added to a media registry. If this machine
10547 * was created with 4.0 or later, then the machine registry is used. Otherwise
10548 * the global VirtualBox media registry is used.
10549 *
10550 * Caller must NOT hold machine lock, media tree or any medium locks!
10551 *
10552 * @param pMedium
10553 */
10554void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10555{
10556 /* Paranoia checks: do not hold machine or media tree locks. */
10557 AssertReturnVoid(!isWriteLockOnCurrentThread());
10558 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10559
10560 ComObjPtr<Medium> pBase;
10561 {
10562 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10563 pBase = pMedium->i_getBase();
10564 }
10565
10566 /* Paranoia checks: do not hold medium locks. */
10567 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10568 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10569
10570 // decide which medium registry to use now that the medium is attached:
10571 Guid uuid;
10572 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10573 if (fCanHaveOwnMediaRegistry)
10574 // machine XML is VirtualBox 4.0 or higher:
10575 uuid = i_getId(); // machine UUID
10576 else
10577 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10578
10579 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10580 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10581 if (pMedium->i_addRegistry(uuid))
10582 mParent->i_markRegistryModified(uuid);
10583
10584 /* For more complex hard disk structures it can happen that the base
10585 * medium isn't yet associated with any medium registry. Do that now. */
10586 if (pMedium != pBase)
10587 {
10588 /* Tree lock needed by Medium::addRegistry when recursing. */
10589 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10590 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10591 {
10592 treeLock.release();
10593 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10594 treeLock.acquire();
10595 }
10596 if (pBase->i_addRegistryRecursive(uuid))
10597 {
10598 treeLock.release();
10599 mParent->i_markRegistryModified(uuid);
10600 }
10601 }
10602}
10603
10604/**
10605 * Creates differencing hard disks for all normal hard disks attached to this
10606 * machine and a new set of attachments to refer to created disks.
10607 *
10608 * Used when taking a snapshot or when deleting the current state. Gets called
10609 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10610 *
10611 * This method assumes that mMediumAttachments contains the original hard disk
10612 * attachments it needs to create diffs for. On success, these attachments will
10613 * be replaced with the created diffs.
10614 *
10615 * Attachments with non-normal hard disks are left as is.
10616 *
10617 * If @a aOnline is @c false then the original hard disks that require implicit
10618 * diffs will be locked for reading. Otherwise it is assumed that they are
10619 * already locked for writing (when the VM was started). Note that in the latter
10620 * case it is responsibility of the caller to lock the newly created diffs for
10621 * writing if this method succeeds.
10622 *
10623 * @param aProgress Progress object to run (must contain at least as
10624 * many operations left as the number of hard disks
10625 * attached).
10626 * @param aWeight Weight of this operation.
10627 * @param aOnline Whether the VM was online prior to this operation.
10628 *
10629 * @note The progress object is not marked as completed, neither on success nor
10630 * on failure. This is a responsibility of the caller.
10631 *
10632 * @note Locks this object and the media tree for writing.
10633 */
10634HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10635 ULONG aWeight,
10636 bool aOnline)
10637{
10638 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10639
10640 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10641 AssertReturn(!!pProgressControl, E_INVALIDARG);
10642
10643 AutoCaller autoCaller(this);
10644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10645
10646 AutoMultiWriteLock2 alock(this->lockHandle(),
10647 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10648
10649 /* must be in a protective state because we release the lock below */
10650 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10651 || mData->mMachineState == MachineState_OnlineSnapshotting
10652 || mData->mMachineState == MachineState_LiveSnapshotting
10653 || mData->mMachineState == MachineState_RestoringSnapshot
10654 || mData->mMachineState == MachineState_DeletingSnapshot
10655 , E_FAIL);
10656
10657 HRESULT rc = S_OK;
10658
10659 // use appropriate locked media map (online or offline)
10660 MediumLockListMap lockedMediaOffline;
10661 MediumLockListMap *lockedMediaMap;
10662 if (aOnline)
10663 lockedMediaMap = &mData->mSession.mLockedMedia;
10664 else
10665 lockedMediaMap = &lockedMediaOffline;
10666
10667 try
10668 {
10669 if (!aOnline)
10670 {
10671 /* lock all attached hard disks early to detect "in use"
10672 * situations before creating actual diffs */
10673 for (MediumAttachmentList::const_iterator
10674 it = mMediumAttachments->begin();
10675 it != mMediumAttachments->end();
10676 ++it)
10677 {
10678 MediumAttachment *pAtt = *it;
10679 if (pAtt->i_getType() == DeviceType_HardDisk)
10680 {
10681 Medium *pMedium = pAtt->i_getMedium();
10682 Assert(pMedium);
10683
10684 MediumLockList *pMediumLockList(new MediumLockList());
10685 alock.release();
10686 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10687 NULL /* pToLockWrite */,
10688 false /* fMediumLockWriteAll */,
10689 NULL,
10690 *pMediumLockList);
10691 alock.acquire();
10692 if (FAILED(rc))
10693 {
10694 delete pMediumLockList;
10695 throw rc;
10696 }
10697 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10698 if (FAILED(rc))
10699 {
10700 throw setError(rc,
10701 tr("Collecting locking information for all attached media failed"));
10702 }
10703 }
10704 }
10705
10706 /* Now lock all media. If this fails, nothing is locked. */
10707 alock.release();
10708 rc = lockedMediaMap->Lock();
10709 alock.acquire();
10710 if (FAILED(rc))
10711 {
10712 throw setError(rc,
10713 tr("Locking of attached media failed"));
10714 }
10715 }
10716
10717 /* remember the current list (note that we don't use backup() since
10718 * mMediumAttachments may be already backed up) */
10719 MediumAttachmentList atts = *mMediumAttachments.data();
10720
10721 /* start from scratch */
10722 mMediumAttachments->clear();
10723
10724 /* go through remembered attachments and create diffs for normal hard
10725 * disks and attach them */
10726 for (MediumAttachmentList::const_iterator
10727 it = atts.begin();
10728 it != atts.end();
10729 ++it)
10730 {
10731 MediumAttachment *pAtt = *it;
10732
10733 DeviceType_T devType = pAtt->i_getType();
10734 Medium *pMedium = pAtt->i_getMedium();
10735
10736 if ( devType != DeviceType_HardDisk
10737 || pMedium == NULL
10738 || pMedium->i_getType() != MediumType_Normal)
10739 {
10740 /* copy the attachment as is */
10741
10742 /** @todo the progress object created in SessionMachine::TakeSnaphot
10743 * only expects operations for hard disks. Later other
10744 * device types need to show up in the progress as well. */
10745 if (devType == DeviceType_HardDisk)
10746 {
10747 if (pMedium == NULL)
10748 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10749 aWeight); // weight
10750 else
10751 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10752 pMedium->i_getBase()->i_getName().c_str()).raw(),
10753 aWeight); // weight
10754 }
10755
10756 mMediumAttachments->push_back(pAtt);
10757 continue;
10758 }
10759
10760 /* need a diff */
10761 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10762 pMedium->i_getBase()->i_getName().c_str()).raw(),
10763 aWeight); // weight
10764
10765 Utf8Str strFullSnapshotFolder;
10766 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10767
10768 ComObjPtr<Medium> diff;
10769 diff.createObject();
10770 // store the diff in the same registry as the parent
10771 // (this cannot fail here because we can't create implicit diffs for
10772 // unregistered images)
10773 Guid uuidRegistryParent;
10774 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10775 Assert(fInRegistry); NOREF(fInRegistry);
10776 rc = diff->init(mParent,
10777 pMedium->i_getPreferredDiffFormat(),
10778 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10779 uuidRegistryParent,
10780 DeviceType_HardDisk);
10781 if (FAILED(rc)) throw rc;
10782
10783 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10784 * the push_back? Looks like we're going to release medium with the
10785 * wrong kind of lock (general issue with if we fail anywhere at all)
10786 * and an orphaned VDI in the snapshots folder. */
10787
10788 /* update the appropriate lock list */
10789 MediumLockList *pMediumLockList;
10790 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10791 AssertComRCThrowRC(rc);
10792 if (aOnline)
10793 {
10794 alock.release();
10795 /* The currently attached medium will be read-only, change
10796 * the lock type to read. */
10797 rc = pMediumLockList->Update(pMedium, false);
10798 alock.acquire();
10799 AssertComRCThrowRC(rc);
10800 }
10801
10802 /* release the locks before the potentially lengthy operation */
10803 alock.release();
10804 rc = pMedium->i_createDiffStorage(diff,
10805 pMedium->i_getPreferredDiffVariant(),
10806 pMediumLockList,
10807 NULL /* aProgress */,
10808 true /* aWait */,
10809 false /* aNotify */);
10810 alock.acquire();
10811 if (FAILED(rc)) throw rc;
10812
10813 /* actual lock list update is done in Machine::i_commitMedia */
10814
10815 rc = diff->i_addBackReference(mData->mUuid);
10816 AssertComRCThrowRC(rc);
10817
10818 /* add a new attachment */
10819 ComObjPtr<MediumAttachment> attachment;
10820 attachment.createObject();
10821 rc = attachment->init(this,
10822 diff,
10823 pAtt->i_getControllerName(),
10824 pAtt->i_getPort(),
10825 pAtt->i_getDevice(),
10826 DeviceType_HardDisk,
10827 true /* aImplicit */,
10828 false /* aPassthrough */,
10829 false /* aTempEject */,
10830 pAtt->i_getNonRotational(),
10831 pAtt->i_getDiscard(),
10832 pAtt->i_getHotPluggable(),
10833 pAtt->i_getBandwidthGroup());
10834 if (FAILED(rc)) throw rc;
10835
10836 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10837 AssertComRCThrowRC(rc);
10838 mMediumAttachments->push_back(attachment);
10839 }
10840 }
10841 catch (HRESULT aRC) { rc = aRC; }
10842
10843 /* unlock all hard disks we locked when there is no VM */
10844 if (!aOnline)
10845 {
10846 ErrorInfoKeeper eik;
10847
10848 HRESULT rc1 = lockedMediaMap->Clear();
10849 AssertComRC(rc1);
10850 }
10851
10852 return rc;
10853}
10854
10855/**
10856 * Deletes implicit differencing hard disks created either by
10857 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10858 * mMediumAttachments.
10859 *
10860 * Note that to delete hard disks created by #attachDevice() this method is
10861 * called from #i_rollbackMedia() when the changes are rolled back.
10862 *
10863 * @note Locks this object and the media tree for writing.
10864 */
10865HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10866{
10867 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10868
10869 AutoCaller autoCaller(this);
10870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10871
10872 AutoMultiWriteLock2 alock(this->lockHandle(),
10873 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10874
10875 /* We absolutely must have backed up state. */
10876 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10877
10878 /* Check if there are any implicitly created diff images. */
10879 bool fImplicitDiffs = false;
10880 for (MediumAttachmentList::const_iterator
10881 it = mMediumAttachments->begin();
10882 it != mMediumAttachments->end();
10883 ++it)
10884 {
10885 const ComObjPtr<MediumAttachment> &pAtt = *it;
10886 if (pAtt->i_isImplicit())
10887 {
10888 fImplicitDiffs = true;
10889 break;
10890 }
10891 }
10892 /* If there is nothing to do, leave early. This saves lots of image locking
10893 * effort. It also avoids a MachineStateChanged event without real reason.
10894 * This is important e.g. when loading a VM config, because there should be
10895 * no events. Otherwise API clients can become thoroughly confused for
10896 * inaccessible VMs (the code for loading VM configs uses this method for
10897 * cleanup if the config makes no sense), as they take such events as an
10898 * indication that the VM is alive, and they would force the VM config to
10899 * be reread, leading to an endless loop. */
10900 if (!fImplicitDiffs)
10901 return S_OK;
10902
10903 HRESULT rc = S_OK;
10904 MachineState_T oldState = mData->mMachineState;
10905
10906 /* will release the lock before the potentially lengthy operation,
10907 * so protect with the special state (unless already protected) */
10908 if ( oldState != MachineState_Snapshotting
10909 && oldState != MachineState_OnlineSnapshotting
10910 && oldState != MachineState_LiveSnapshotting
10911 && oldState != MachineState_RestoringSnapshot
10912 && oldState != MachineState_DeletingSnapshot
10913 && oldState != MachineState_DeletingSnapshotOnline
10914 && oldState != MachineState_DeletingSnapshotPaused
10915 )
10916 i_setMachineState(MachineState_SettingUp);
10917
10918 // use appropriate locked media map (online or offline)
10919 MediumLockListMap lockedMediaOffline;
10920 MediumLockListMap *lockedMediaMap;
10921 if (aOnline)
10922 lockedMediaMap = &mData->mSession.mLockedMedia;
10923 else
10924 lockedMediaMap = &lockedMediaOffline;
10925
10926 try
10927 {
10928 if (!aOnline)
10929 {
10930 /* lock all attached hard disks early to detect "in use"
10931 * situations before deleting actual diffs */
10932 for (MediumAttachmentList::const_iterator
10933 it = mMediumAttachments->begin();
10934 it != mMediumAttachments->end();
10935 ++it)
10936 {
10937 MediumAttachment *pAtt = *it;
10938 if (pAtt->i_getType() == DeviceType_HardDisk)
10939 {
10940 Medium *pMedium = pAtt->i_getMedium();
10941 Assert(pMedium);
10942
10943 MediumLockList *pMediumLockList(new MediumLockList());
10944 alock.release();
10945 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10946 NULL /* pToLockWrite */,
10947 false /* fMediumLockWriteAll */,
10948 NULL,
10949 *pMediumLockList);
10950 alock.acquire();
10951
10952 if (FAILED(rc))
10953 {
10954 delete pMediumLockList;
10955 throw rc;
10956 }
10957
10958 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10959 if (FAILED(rc))
10960 throw rc;
10961 }
10962 }
10963
10964 if (FAILED(rc))
10965 throw rc;
10966 } // end of offline
10967
10968 /* Lock lists are now up to date and include implicitly created media */
10969
10970 /* Go through remembered attachments and delete all implicitly created
10971 * diffs and fix up the attachment information */
10972 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10973 MediumAttachmentList implicitAtts;
10974 for (MediumAttachmentList::const_iterator
10975 it = mMediumAttachments->begin();
10976 it != mMediumAttachments->end();
10977 ++it)
10978 {
10979 ComObjPtr<MediumAttachment> pAtt = *it;
10980 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10981 if (pMedium.isNull())
10982 continue;
10983
10984 // Implicit attachments go on the list for deletion and back references are removed.
10985 if (pAtt->i_isImplicit())
10986 {
10987 /* Deassociate and mark for deletion */
10988 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10989 rc = pMedium->i_removeBackReference(mData->mUuid);
10990 if (FAILED(rc))
10991 throw rc;
10992 implicitAtts.push_back(pAtt);
10993 continue;
10994 }
10995
10996 /* Was this medium attached before? */
10997 if (!i_findAttachment(oldAtts, pMedium))
10998 {
10999 /* no: de-associate */
11000 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11001 rc = pMedium->i_removeBackReference(mData->mUuid);
11002 if (FAILED(rc))
11003 throw rc;
11004 continue;
11005 }
11006 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11007 }
11008
11009 /* If there are implicit attachments to delete, throw away the lock
11010 * map contents (which will unlock all media) since the medium
11011 * attachments will be rolled back. Below we need to completely
11012 * recreate the lock map anyway since it is infinitely complex to
11013 * do this incrementally (would need reconstructing each attachment
11014 * change, which would be extremely hairy). */
11015 if (implicitAtts.size() != 0)
11016 {
11017 ErrorInfoKeeper eik;
11018
11019 HRESULT rc1 = lockedMediaMap->Clear();
11020 AssertComRC(rc1);
11021 }
11022
11023 /* rollback hard disk changes */
11024 mMediumAttachments.rollback();
11025
11026 MultiResult mrc(S_OK);
11027
11028 // Delete unused implicit diffs.
11029 if (implicitAtts.size() != 0)
11030 {
11031 alock.release();
11032
11033 for (MediumAttachmentList::const_iterator
11034 it = implicitAtts.begin();
11035 it != implicitAtts.end();
11036 ++it)
11037 {
11038 // Remove medium associated with this attachment.
11039 ComObjPtr<MediumAttachment> pAtt = *it;
11040 Assert(pAtt);
11041 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11042 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11043 Assert(pMedium);
11044
11045 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11046 // continue on delete failure, just collect error messages
11047 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11048 pMedium->i_getLocationFull().c_str() ));
11049 mrc = rc;
11050 }
11051 // Clear the list of deleted implicit attachments now, while not
11052 // holding the lock, as it will ultimately trigger Medium::uninit()
11053 // calls which assume that the media tree lock isn't held.
11054 implicitAtts.clear();
11055
11056 alock.acquire();
11057
11058 /* if there is a VM recreate media lock map as mentioned above,
11059 * otherwise it is a waste of time and we leave things unlocked */
11060 if (aOnline)
11061 {
11062 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11063 /* must never be NULL, but better safe than sorry */
11064 if (!pMachine.isNull())
11065 {
11066 alock.release();
11067 rc = mData->mSession.mMachine->i_lockMedia();
11068 alock.acquire();
11069 if (FAILED(rc))
11070 throw rc;
11071 }
11072 }
11073 }
11074 }
11075 catch (HRESULT aRC) {rc = aRC;}
11076
11077 if (mData->mMachineState == MachineState_SettingUp)
11078 i_setMachineState(oldState);
11079
11080 /* unlock all hard disks we locked when there is no VM */
11081 if (!aOnline)
11082 {
11083 ErrorInfoKeeper eik;
11084
11085 HRESULT rc1 = lockedMediaMap->Clear();
11086 AssertComRC(rc1);
11087 }
11088
11089 return rc;
11090}
11091
11092
11093/**
11094 * Looks through the given list of media attachments for one with the given parameters
11095 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11096 * can be searched as well if needed.
11097 *
11098 * @param ll
11099 * @param aControllerName
11100 * @param aControllerPort
11101 * @param aDevice
11102 * @return
11103 */
11104MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11105 const Utf8Str &aControllerName,
11106 LONG aControllerPort,
11107 LONG aDevice)
11108{
11109 for (MediumAttachmentList::const_iterator
11110 it = ll.begin();
11111 it != ll.end();
11112 ++it)
11113 {
11114 MediumAttachment *pAttach = *it;
11115 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11116 return pAttach;
11117 }
11118
11119 return NULL;
11120}
11121
11122/**
11123 * Looks through the given list of media attachments for one with the given parameters
11124 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11125 * can be searched as well if needed.
11126 *
11127 * @param ll
11128 * @param pMedium
11129 * @return
11130 */
11131MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11132 ComObjPtr<Medium> pMedium)
11133{
11134 for (MediumAttachmentList::const_iterator
11135 it = ll.begin();
11136 it != ll.end();
11137 ++it)
11138 {
11139 MediumAttachment *pAttach = *it;
11140 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11141 if (pMediumThis == pMedium)
11142 return pAttach;
11143 }
11144
11145 return NULL;
11146}
11147
11148/**
11149 * Looks through the given list of media attachments for one with the given parameters
11150 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11151 * can be searched as well if needed.
11152 *
11153 * @param ll
11154 * @param id
11155 * @return
11156 */
11157MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11158 Guid &id)
11159{
11160 for (MediumAttachmentList::const_iterator
11161 it = ll.begin();
11162 it != ll.end();
11163 ++it)
11164 {
11165 MediumAttachment *pAttach = *it;
11166 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11167 if (pMediumThis->i_getId() == id)
11168 return pAttach;
11169 }
11170
11171 return NULL;
11172}
11173
11174/**
11175 * Main implementation for Machine::DetachDevice. This also gets called
11176 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11177 *
11178 * @param pAttach Medium attachment to detach.
11179 * @param writeLock Machine write lock which the caller must have locked once.
11180 * This may be released temporarily in here.
11181 * @param pSnapshot If NULL, then the detachment is for the current machine.
11182 * Otherwise this is for a SnapshotMachine, and this must be
11183 * its snapshot.
11184 * @return
11185 */
11186HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11187 AutoWriteLock &writeLock,
11188 Snapshot *pSnapshot)
11189{
11190 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11191 DeviceType_T mediumType = pAttach->i_getType();
11192
11193 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11194
11195 if (pAttach->i_isImplicit())
11196 {
11197 /* attempt to implicitly delete the implicitly created diff */
11198
11199 /// @todo move the implicit flag from MediumAttachment to Medium
11200 /// and forbid any hard disk operation when it is implicit. Or maybe
11201 /// a special media state for it to make it even more simple.
11202
11203 Assert(mMediumAttachments.isBackedUp());
11204
11205 /* will release the lock before the potentially lengthy operation, so
11206 * protect with the special state */
11207 MachineState_T oldState = mData->mMachineState;
11208 i_setMachineState(MachineState_SettingUp);
11209
11210 writeLock.release();
11211
11212 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11213 true /*aWait*/,
11214 false /*aNotify*/);
11215
11216 writeLock.acquire();
11217
11218 i_setMachineState(oldState);
11219
11220 if (FAILED(rc)) return rc;
11221 }
11222
11223 i_setModified(IsModified_Storage);
11224 mMediumAttachments.backup();
11225 mMediumAttachments->remove(pAttach);
11226
11227 if (!oldmedium.isNull())
11228 {
11229 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11230 if (pSnapshot)
11231 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11232 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11233 else if (mediumType != DeviceType_HardDisk)
11234 oldmedium->i_removeBackReference(mData->mUuid);
11235 }
11236
11237 return S_OK;
11238}
11239
11240/**
11241 * Goes thru all media of the given list and
11242 *
11243 * 1) calls i_detachDevice() on each of them for this machine and
11244 * 2) adds all Medium objects found in the process to the given list,
11245 * depending on cleanupMode.
11246 *
11247 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11248 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11249 * media to the list.
11250 *
11251 * This gets called from Machine::Unregister, both for the actual Machine and
11252 * the SnapshotMachine objects that might be found in the snapshots.
11253 *
11254 * Requires caller and locking. The machine lock must be passed in because it
11255 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11256 *
11257 * @param writeLock Machine lock from top-level caller; this gets passed to
11258 * i_detachDevice.
11259 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11260 * object if called for a SnapshotMachine.
11261 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11262 * added to llMedia; if Full, then all media get added;
11263 * otherwise no media get added.
11264 * @param llMedia Caller's list to receive Medium objects which got detached so
11265 * caller can close() them, depending on cleanupMode.
11266 * @return
11267 */
11268HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11269 Snapshot *pSnapshot,
11270 CleanupMode_T cleanupMode,
11271 MediaList &llMedia)
11272{
11273 Assert(isWriteLockOnCurrentThread());
11274
11275 HRESULT rc;
11276
11277 // make a temporary list because i_detachDevice invalidates iterators into
11278 // mMediumAttachments
11279 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11280
11281 for (MediumAttachmentList::iterator
11282 it = llAttachments2.begin();
11283 it != llAttachments2.end();
11284 ++it)
11285 {
11286 ComObjPtr<MediumAttachment> &pAttach = *it;
11287 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11288
11289 if (!pMedium.isNull())
11290 {
11291 AutoCaller mac(pMedium);
11292 if (FAILED(mac.rc())) return mac.rc();
11293 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11294 DeviceType_T devType = pMedium->i_getDeviceType();
11295 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11296 && devType == DeviceType_HardDisk)
11297 || (cleanupMode == CleanupMode_Full)
11298 )
11299 {
11300 llMedia.push_back(pMedium);
11301 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11302 /* Not allowed to keep this lock as below we need the parent
11303 * medium lock, and the lock order is parent to child. */
11304 lock.release();
11305 /*
11306 * Search for medias which are not attached to any machine, but
11307 * in the chain to an attached disk. Mediums are only consided
11308 * if they are:
11309 * - have only one child
11310 * - no references to any machines
11311 * - are of normal medium type
11312 */
11313 while (!pParent.isNull())
11314 {
11315 AutoCaller mac1(pParent);
11316 if (FAILED(mac1.rc())) return mac1.rc();
11317 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11318 if (pParent->i_getChildren().size() == 1)
11319 {
11320 if ( pParent->i_getMachineBackRefCount() == 0
11321 && pParent->i_getType() == MediumType_Normal
11322 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11323 llMedia.push_back(pParent);
11324 }
11325 else
11326 break;
11327 pParent = pParent->i_getParent();
11328 }
11329 }
11330 }
11331
11332 // real machine: then we need to use the proper method
11333 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11334
11335 if (FAILED(rc))
11336 return rc;
11337 }
11338
11339 return S_OK;
11340}
11341
11342/**
11343 * Perform deferred hard disk detachments.
11344 *
11345 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11346 * changed (not backed up).
11347 *
11348 * If @a aOnline is @c true then this method will also unlock the old hard
11349 * disks for which the new implicit diffs were created and will lock these new
11350 * diffs for writing.
11351 *
11352 * @param aOnline Whether the VM was online prior to this operation.
11353 *
11354 * @note Locks this object for writing!
11355 */
11356void Machine::i_commitMedia(bool aOnline /*= false*/)
11357{
11358 AutoCaller autoCaller(this);
11359 AssertComRCReturnVoid(autoCaller.rc());
11360
11361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11362
11363 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11364
11365 HRESULT rc = S_OK;
11366
11367 /* no attach/detach operations -- nothing to do */
11368 if (!mMediumAttachments.isBackedUp())
11369 return;
11370
11371 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11372 bool fMediaNeedsLocking = false;
11373
11374 /* enumerate new attachments */
11375 for (MediumAttachmentList::const_iterator
11376 it = mMediumAttachments->begin();
11377 it != mMediumAttachments->end();
11378 ++it)
11379 {
11380 MediumAttachment *pAttach = *it;
11381
11382 pAttach->i_commit();
11383
11384 Medium *pMedium = pAttach->i_getMedium();
11385 bool fImplicit = pAttach->i_isImplicit();
11386
11387 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11388 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11389 fImplicit));
11390
11391 /** @todo convert all this Machine-based voodoo to MediumAttachment
11392 * based commit logic. */
11393 if (fImplicit)
11394 {
11395 /* convert implicit attachment to normal */
11396 pAttach->i_setImplicit(false);
11397
11398 if ( aOnline
11399 && pMedium
11400 && pAttach->i_getType() == DeviceType_HardDisk
11401 )
11402 {
11403 /* update the appropriate lock list */
11404 MediumLockList *pMediumLockList;
11405 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11406 AssertComRC(rc);
11407 if (pMediumLockList)
11408 {
11409 /* unlock if there's a need to change the locking */
11410 if (!fMediaNeedsLocking)
11411 {
11412 rc = mData->mSession.mLockedMedia.Unlock();
11413 AssertComRC(rc);
11414 fMediaNeedsLocking = true;
11415 }
11416 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11417 AssertComRC(rc);
11418 rc = pMediumLockList->Append(pMedium, true);
11419 AssertComRC(rc);
11420 }
11421 }
11422
11423 continue;
11424 }
11425
11426 if (pMedium)
11427 {
11428 /* was this medium attached before? */
11429 for (MediumAttachmentList::iterator
11430 oldIt = oldAtts.begin();
11431 oldIt != oldAtts.end();
11432 ++oldIt)
11433 {
11434 MediumAttachment *pOldAttach = *oldIt;
11435 if (pOldAttach->i_getMedium() == pMedium)
11436 {
11437 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11438
11439 /* yes: remove from old to avoid de-association */
11440 oldAtts.erase(oldIt);
11441 break;
11442 }
11443 }
11444 }
11445 }
11446
11447 /* enumerate remaining old attachments and de-associate from the
11448 * current machine state */
11449 for (MediumAttachmentList::const_iterator
11450 it = oldAtts.begin();
11451 it != oldAtts.end();
11452 ++it)
11453 {
11454 MediumAttachment *pAttach = *it;
11455 Medium *pMedium = pAttach->i_getMedium();
11456
11457 /* Detach only hard disks, since DVD/floppy media is detached
11458 * instantly in MountMedium. */
11459 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11460 {
11461 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11462
11463 /* now de-associate from the current machine state */
11464 rc = pMedium->i_removeBackReference(mData->mUuid);
11465 AssertComRC(rc);
11466
11467 if (aOnline)
11468 {
11469 /* unlock since medium is not used anymore */
11470 MediumLockList *pMediumLockList;
11471 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11472 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11473 {
11474 /* this happens for online snapshots, there the attachment
11475 * is changing, but only to a diff image created under
11476 * the old one, so there is no separate lock list */
11477 Assert(!pMediumLockList);
11478 }
11479 else
11480 {
11481 AssertComRC(rc);
11482 if (pMediumLockList)
11483 {
11484 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11485 AssertComRC(rc);
11486 }
11487 }
11488 }
11489 }
11490 }
11491
11492 /* take media locks again so that the locking state is consistent */
11493 if (fMediaNeedsLocking)
11494 {
11495 Assert(aOnline);
11496 rc = mData->mSession.mLockedMedia.Lock();
11497 AssertComRC(rc);
11498 }
11499
11500 /* commit the hard disk changes */
11501 mMediumAttachments.commit();
11502
11503 if (i_isSessionMachine())
11504 {
11505 /*
11506 * Update the parent machine to point to the new owner.
11507 * This is necessary because the stored parent will point to the
11508 * session machine otherwise and cause crashes or errors later
11509 * when the session machine gets invalid.
11510 */
11511 /** @todo Change the MediumAttachment class to behave like any other
11512 * class in this regard by creating peer MediumAttachment
11513 * objects for session machines and share the data with the peer
11514 * machine.
11515 */
11516 for (MediumAttachmentList::const_iterator
11517 it = mMediumAttachments->begin();
11518 it != mMediumAttachments->end();
11519 ++it)
11520 (*it)->i_updateParentMachine(mPeer);
11521
11522 /* attach new data to the primary machine and reshare it */
11523 mPeer->mMediumAttachments.attach(mMediumAttachments);
11524 }
11525
11526 return;
11527}
11528
11529/**
11530 * Perform deferred deletion of implicitly created diffs.
11531 *
11532 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11533 * changed (not backed up).
11534 *
11535 * @note Locks this object for writing!
11536 */
11537void Machine::i_rollbackMedia()
11538{
11539 AutoCaller autoCaller(this);
11540 AssertComRCReturnVoid(autoCaller.rc());
11541
11542 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11543 LogFlowThisFunc(("Entering rollbackMedia\n"));
11544
11545 HRESULT rc = S_OK;
11546
11547 /* no attach/detach operations -- nothing to do */
11548 if (!mMediumAttachments.isBackedUp())
11549 return;
11550
11551 /* enumerate new attachments */
11552 for (MediumAttachmentList::const_iterator
11553 it = mMediumAttachments->begin();
11554 it != mMediumAttachments->end();
11555 ++it)
11556 {
11557 MediumAttachment *pAttach = *it;
11558 /* Fix up the backrefs for DVD/floppy media. */
11559 if (pAttach->i_getType() != DeviceType_HardDisk)
11560 {
11561 Medium *pMedium = pAttach->i_getMedium();
11562 if (pMedium)
11563 {
11564 rc = pMedium->i_removeBackReference(mData->mUuid);
11565 AssertComRC(rc);
11566 }
11567 }
11568
11569 (*it)->i_rollback();
11570
11571 pAttach = *it;
11572 /* Fix up the backrefs for DVD/floppy media. */
11573 if (pAttach->i_getType() != DeviceType_HardDisk)
11574 {
11575 Medium *pMedium = pAttach->i_getMedium();
11576 if (pMedium)
11577 {
11578 rc = pMedium->i_addBackReference(mData->mUuid);
11579 AssertComRC(rc);
11580 }
11581 }
11582 }
11583
11584 /** @todo convert all this Machine-based voodoo to MediumAttachment
11585 * based rollback logic. */
11586 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11587
11588 return;
11589}
11590
11591/**
11592 * Returns true if the settings file is located in the directory named exactly
11593 * as the machine; this means, among other things, that the machine directory
11594 * should be auto-renamed.
11595 *
11596 * @param aSettingsDir if not NULL, the full machine settings file directory
11597 * name will be assigned there.
11598 *
11599 * @note Doesn't lock anything.
11600 * @note Not thread safe (must be called from this object's lock).
11601 */
11602bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11603{
11604 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11605 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11606 if (aSettingsDir)
11607 *aSettingsDir = strMachineDirName;
11608 strMachineDirName.stripPath(); // vmname
11609 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11610 strConfigFileOnly.stripPath() // vmname.vbox
11611 .stripSuffix(); // vmname
11612 /** @todo hack, make somehow use of ComposeMachineFilename */
11613 if (mUserData->s.fDirectoryIncludesUUID)
11614 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11615
11616 AssertReturn(!strMachineDirName.isEmpty(), false);
11617 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11618
11619 return strMachineDirName == strConfigFileOnly;
11620}
11621
11622/**
11623 * Discards all changes to machine settings.
11624 *
11625 * @param aNotify Whether to notify the direct session about changes or not.
11626 *
11627 * @note Locks objects for writing!
11628 */
11629void Machine::i_rollback(bool aNotify)
11630{
11631 AutoCaller autoCaller(this);
11632 AssertComRCReturn(autoCaller.rc(), (void)0);
11633
11634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11635
11636 if (!mStorageControllers.isNull())
11637 {
11638 if (mStorageControllers.isBackedUp())
11639 {
11640 /* unitialize all new devices (absent in the backed up list). */
11641 StorageControllerList *backedList = mStorageControllers.backedUpData();
11642 for (StorageControllerList::const_iterator
11643 it = mStorageControllers->begin();
11644 it != mStorageControllers->end();
11645 ++it)
11646 {
11647 if ( std::find(backedList->begin(), backedList->end(), *it)
11648 == backedList->end()
11649 )
11650 {
11651 (*it)->uninit();
11652 }
11653 }
11654
11655 /* restore the list */
11656 mStorageControllers.rollback();
11657 }
11658
11659 /* rollback any changes to devices after restoring the list */
11660 if (mData->flModifications & IsModified_Storage)
11661 {
11662 for (StorageControllerList::const_iterator
11663 it = mStorageControllers->begin();
11664 it != mStorageControllers->end();
11665 ++it)
11666 {
11667 (*it)->i_rollback();
11668 }
11669 }
11670 }
11671
11672 if (!mUSBControllers.isNull())
11673 {
11674 if (mUSBControllers.isBackedUp())
11675 {
11676 /* unitialize all new devices (absent in the backed up list). */
11677 USBControllerList *backedList = mUSBControllers.backedUpData();
11678 for (USBControllerList::const_iterator
11679 it = mUSBControllers->begin();
11680 it != mUSBControllers->end();
11681 ++it)
11682 {
11683 if ( std::find(backedList->begin(), backedList->end(), *it)
11684 == backedList->end()
11685 )
11686 {
11687 (*it)->uninit();
11688 }
11689 }
11690
11691 /* restore the list */
11692 mUSBControllers.rollback();
11693 }
11694
11695 /* rollback any changes to devices after restoring the list */
11696 if (mData->flModifications & IsModified_USB)
11697 {
11698 for (USBControllerList::const_iterator
11699 it = mUSBControllers->begin();
11700 it != mUSBControllers->end();
11701 ++it)
11702 {
11703 (*it)->i_rollback();
11704 }
11705 }
11706 }
11707
11708 mUserData.rollback();
11709
11710 mHWData.rollback();
11711
11712 if (mData->flModifications & IsModified_Storage)
11713 i_rollbackMedia();
11714
11715 if (mBIOSSettings)
11716 mBIOSSettings->i_rollback();
11717
11718 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11719 mRecordingSettings->i_rollback();
11720
11721 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11722 mGraphicsAdapter->i_rollback();
11723
11724 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11725 mVRDEServer->i_rollback();
11726
11727 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11728 mAudioAdapter->i_rollback();
11729
11730 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11731 mUSBDeviceFilters->i_rollback();
11732
11733 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11734 mBandwidthControl->i_rollback();
11735
11736 if (!mHWData.isNull())
11737 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11738 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11739 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11740 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11741
11742 if (mData->flModifications & IsModified_NetworkAdapters)
11743 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11744 if ( mNetworkAdapters[slot]
11745 && mNetworkAdapters[slot]->i_isModified())
11746 {
11747 mNetworkAdapters[slot]->i_rollback();
11748 networkAdapters[slot] = mNetworkAdapters[slot];
11749 }
11750
11751 if (mData->flModifications & IsModified_SerialPorts)
11752 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11753 if ( mSerialPorts[slot]
11754 && mSerialPorts[slot]->i_isModified())
11755 {
11756 mSerialPorts[slot]->i_rollback();
11757 serialPorts[slot] = mSerialPorts[slot];
11758 }
11759
11760 if (mData->flModifications & IsModified_ParallelPorts)
11761 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11762 if ( mParallelPorts[slot]
11763 && mParallelPorts[slot]->i_isModified())
11764 {
11765 mParallelPorts[slot]->i_rollback();
11766 parallelPorts[slot] = mParallelPorts[slot];
11767 }
11768
11769 if (aNotify)
11770 {
11771 /* inform the direct session about changes */
11772
11773 ComObjPtr<Machine> that = this;
11774 uint32_t flModifications = mData->flModifications;
11775 alock.release();
11776
11777 if (flModifications & IsModified_SharedFolders)
11778 that->i_onSharedFolderChange();
11779
11780 if (flModifications & IsModified_VRDEServer)
11781 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11782 if (flModifications & IsModified_USB)
11783 that->i_onUSBControllerChange();
11784
11785 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11786 if (networkAdapters[slot])
11787 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11788 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11789 if (serialPorts[slot])
11790 that->i_onSerialPortChange(serialPorts[slot]);
11791 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11792 if (parallelPorts[slot])
11793 that->i_onParallelPortChange(parallelPorts[slot]);
11794
11795 if (flModifications & IsModified_Storage)
11796 {
11797 for (StorageControllerList::const_iterator
11798 it = mStorageControllers->begin();
11799 it != mStorageControllers->end();
11800 ++it)
11801 {
11802 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11803 }
11804 }
11805
11806
11807#if 0
11808 if (flModifications & IsModified_BandwidthControl)
11809 that->onBandwidthControlChange();
11810#endif
11811 }
11812}
11813
11814/**
11815 * Commits all the changes to machine settings.
11816 *
11817 * Note that this operation is supposed to never fail.
11818 *
11819 * @note Locks this object and children for writing.
11820 */
11821void Machine::i_commit()
11822{
11823 AutoCaller autoCaller(this);
11824 AssertComRCReturnVoid(autoCaller.rc());
11825
11826 AutoCaller peerCaller(mPeer);
11827 AssertComRCReturnVoid(peerCaller.rc());
11828
11829 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11830
11831 /*
11832 * use safe commit to ensure Snapshot machines (that share mUserData)
11833 * will still refer to a valid memory location
11834 */
11835 mUserData.commitCopy();
11836
11837 mHWData.commit();
11838
11839 if (mMediumAttachments.isBackedUp())
11840 i_commitMedia(Global::IsOnline(mData->mMachineState));
11841
11842 mBIOSSettings->i_commit();
11843 mRecordingSettings->i_commit();
11844 mGraphicsAdapter->i_commit();
11845 mVRDEServer->i_commit();
11846 mAudioAdapter->i_commit();
11847 mUSBDeviceFilters->i_commit();
11848 mBandwidthControl->i_commit();
11849
11850 /* Since mNetworkAdapters is a list which might have been changed (resized)
11851 * without using the Backupable<> template we need to handle the copying
11852 * of the list entries manually, including the creation of peers for the
11853 * new objects. */
11854 bool commitNetworkAdapters = false;
11855 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11856 if (mPeer)
11857 {
11858 /* commit everything, even the ones which will go away */
11859 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11860 mNetworkAdapters[slot]->i_commit();
11861 /* copy over the new entries, creating a peer and uninit the original */
11862 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11863 for (size_t slot = 0; slot < newSize; slot++)
11864 {
11865 /* look if this adapter has a peer device */
11866 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11867 if (!peer)
11868 {
11869 /* no peer means the adapter is a newly created one;
11870 * create a peer owning data this data share it with */
11871 peer.createObject();
11872 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11873 }
11874 mPeer->mNetworkAdapters[slot] = peer;
11875 }
11876 /* uninit any no longer needed network adapters */
11877 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11878 mNetworkAdapters[slot]->uninit();
11879 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11880 {
11881 if (mPeer->mNetworkAdapters[slot])
11882 mPeer->mNetworkAdapters[slot]->uninit();
11883 }
11884 /* Keep the original network adapter count until this point, so that
11885 * discarding a chipset type change will not lose settings. */
11886 mNetworkAdapters.resize(newSize);
11887 mPeer->mNetworkAdapters.resize(newSize);
11888 }
11889 else
11890 {
11891 /* we have no peer (our parent is the newly created machine);
11892 * just commit changes to the network adapters */
11893 commitNetworkAdapters = true;
11894 }
11895 if (commitNetworkAdapters)
11896 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11897 mNetworkAdapters[slot]->i_commit();
11898
11899 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11900 mSerialPorts[slot]->i_commit();
11901 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11902 mParallelPorts[slot]->i_commit();
11903
11904 bool commitStorageControllers = false;
11905
11906 if (mStorageControllers.isBackedUp())
11907 {
11908 mStorageControllers.commit();
11909
11910 if (mPeer)
11911 {
11912 /* Commit all changes to new controllers (this will reshare data with
11913 * peers for those who have peers) */
11914 StorageControllerList *newList = new StorageControllerList();
11915 for (StorageControllerList::const_iterator
11916 it = mStorageControllers->begin();
11917 it != mStorageControllers->end();
11918 ++it)
11919 {
11920 (*it)->i_commit();
11921
11922 /* look if this controller has a peer device */
11923 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11924 if (!peer)
11925 {
11926 /* no peer means the device is a newly created one;
11927 * create a peer owning data this device share it with */
11928 peer.createObject();
11929 peer->init(mPeer, *it, true /* aReshare */);
11930 }
11931 else
11932 {
11933 /* remove peer from the old list */
11934 mPeer->mStorageControllers->remove(peer);
11935 }
11936 /* and add it to the new list */
11937 newList->push_back(peer);
11938 }
11939
11940 /* uninit old peer's controllers that are left */
11941 for (StorageControllerList::const_iterator
11942 it = mPeer->mStorageControllers->begin();
11943 it != mPeer->mStorageControllers->end();
11944 ++it)
11945 {
11946 (*it)->uninit();
11947 }
11948
11949 /* attach new list of controllers to our peer */
11950 mPeer->mStorageControllers.attach(newList);
11951 }
11952 else
11953 {
11954 /* we have no peer (our parent is the newly created machine);
11955 * just commit changes to devices */
11956 commitStorageControllers = true;
11957 }
11958 }
11959 else
11960 {
11961 /* the list of controllers itself is not changed,
11962 * just commit changes to controllers themselves */
11963 commitStorageControllers = true;
11964 }
11965
11966 if (commitStorageControllers)
11967 {
11968 for (StorageControllerList::const_iterator
11969 it = mStorageControllers->begin();
11970 it != mStorageControllers->end();
11971 ++it)
11972 {
11973 (*it)->i_commit();
11974 }
11975 }
11976
11977 bool commitUSBControllers = false;
11978
11979 if (mUSBControllers.isBackedUp())
11980 {
11981 mUSBControllers.commit();
11982
11983 if (mPeer)
11984 {
11985 /* Commit all changes to new controllers (this will reshare data with
11986 * peers for those who have peers) */
11987 USBControllerList *newList = new USBControllerList();
11988 for (USBControllerList::const_iterator
11989 it = mUSBControllers->begin();
11990 it != mUSBControllers->end();
11991 ++it)
11992 {
11993 (*it)->i_commit();
11994
11995 /* look if this controller has a peer device */
11996 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11997 if (!peer)
11998 {
11999 /* no peer means the device is a newly created one;
12000 * create a peer owning data this device share it with */
12001 peer.createObject();
12002 peer->init(mPeer, *it, true /* aReshare */);
12003 }
12004 else
12005 {
12006 /* remove peer from the old list */
12007 mPeer->mUSBControllers->remove(peer);
12008 }
12009 /* and add it to the new list */
12010 newList->push_back(peer);
12011 }
12012
12013 /* uninit old peer's controllers that are left */
12014 for (USBControllerList::const_iterator
12015 it = mPeer->mUSBControllers->begin();
12016 it != mPeer->mUSBControllers->end();
12017 ++it)
12018 {
12019 (*it)->uninit();
12020 }
12021
12022 /* attach new list of controllers to our peer */
12023 mPeer->mUSBControllers.attach(newList);
12024 }
12025 else
12026 {
12027 /* we have no peer (our parent is the newly created machine);
12028 * just commit changes to devices */
12029 commitUSBControllers = true;
12030 }
12031 }
12032 else
12033 {
12034 /* the list of controllers itself is not changed,
12035 * just commit changes to controllers themselves */
12036 commitUSBControllers = true;
12037 }
12038
12039 if (commitUSBControllers)
12040 {
12041 for (USBControllerList::const_iterator
12042 it = mUSBControllers->begin();
12043 it != mUSBControllers->end();
12044 ++it)
12045 {
12046 (*it)->i_commit();
12047 }
12048 }
12049
12050 if (i_isSessionMachine())
12051 {
12052 /* attach new data to the primary machine and reshare it */
12053 mPeer->mUserData.attach(mUserData);
12054 mPeer->mHWData.attach(mHWData);
12055 /* mmMediumAttachments is reshared by fixupMedia */
12056 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12057 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12058 }
12059}
12060
12061/**
12062 * Copies all the hardware data from the given machine.
12063 *
12064 * Currently, only called when the VM is being restored from a snapshot. In
12065 * particular, this implies that the VM is not running during this method's
12066 * call.
12067 *
12068 * @note This method must be called from under this object's lock.
12069 *
12070 * @note This method doesn't call #i_commit(), so all data remains backed up and
12071 * unsaved.
12072 */
12073void Machine::i_copyFrom(Machine *aThat)
12074{
12075 AssertReturnVoid(!i_isSnapshotMachine());
12076 AssertReturnVoid(aThat->i_isSnapshotMachine());
12077
12078 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12079
12080 mHWData.assignCopy(aThat->mHWData);
12081
12082 // create copies of all shared folders (mHWData after attaching a copy
12083 // contains just references to original objects)
12084 for (HWData::SharedFolderList::iterator
12085 it = mHWData->mSharedFolders.begin();
12086 it != mHWData->mSharedFolders.end();
12087 ++it)
12088 {
12089 ComObjPtr<SharedFolder> folder;
12090 folder.createObject();
12091 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12092 AssertComRC(rc);
12093 *it = folder;
12094 }
12095
12096 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12097 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12098 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12099 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12100 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12101 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12102 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12103
12104 /* create private copies of all controllers */
12105 mStorageControllers.backup();
12106 mStorageControllers->clear();
12107 for (StorageControllerList::const_iterator
12108 it = aThat->mStorageControllers->begin();
12109 it != aThat->mStorageControllers->end();
12110 ++it)
12111 {
12112 ComObjPtr<StorageController> ctrl;
12113 ctrl.createObject();
12114 ctrl->initCopy(this, *it);
12115 mStorageControllers->push_back(ctrl);
12116 }
12117
12118 /* create private copies of all USB controllers */
12119 mUSBControllers.backup();
12120 mUSBControllers->clear();
12121 for (USBControllerList::const_iterator
12122 it = aThat->mUSBControllers->begin();
12123 it != aThat->mUSBControllers->end();
12124 ++it)
12125 {
12126 ComObjPtr<USBController> ctrl;
12127 ctrl.createObject();
12128 ctrl->initCopy(this, *it);
12129 mUSBControllers->push_back(ctrl);
12130 }
12131
12132 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12133 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12134 {
12135 if (mNetworkAdapters[slot].isNotNull())
12136 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12137 else
12138 {
12139 unconst(mNetworkAdapters[slot]).createObject();
12140 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12141 }
12142 }
12143 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12144 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12145 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12146 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12147}
12148
12149/**
12150 * Returns whether the given storage controller is hotplug capable.
12151 *
12152 * @returns true if the controller supports hotplugging
12153 * false otherwise.
12154 * @param enmCtrlType The controller type to check for.
12155 */
12156bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12157{
12158 ComPtr<ISystemProperties> systemProperties;
12159 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12160 if (FAILED(rc))
12161 return false;
12162
12163 BOOL aHotplugCapable = FALSE;
12164 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12165
12166 return RT_BOOL(aHotplugCapable);
12167}
12168
12169#ifdef VBOX_WITH_RESOURCE_USAGE_API
12170
12171void Machine::i_getDiskList(MediaList &list)
12172{
12173 for (MediumAttachmentList::const_iterator
12174 it = mMediumAttachments->begin();
12175 it != mMediumAttachments->end();
12176 ++it)
12177 {
12178 MediumAttachment *pAttach = *it;
12179 /* just in case */
12180 AssertContinue(pAttach);
12181
12182 AutoCaller localAutoCallerA(pAttach);
12183 if (FAILED(localAutoCallerA.rc())) continue;
12184
12185 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12186
12187 if (pAttach->i_getType() == DeviceType_HardDisk)
12188 list.push_back(pAttach->i_getMedium());
12189 }
12190}
12191
12192void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12193{
12194 AssertReturnVoid(isWriteLockOnCurrentThread());
12195 AssertPtrReturnVoid(aCollector);
12196
12197 pm::CollectorHAL *hal = aCollector->getHAL();
12198 /* Create sub metrics */
12199 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12200 "Percentage of processor time spent in user mode by the VM process.");
12201 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12202 "Percentage of processor time spent in kernel mode by the VM process.");
12203 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12204 "Size of resident portion of VM process in memory.");
12205 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12206 "Actual size of all VM disks combined.");
12207 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12208 "Network receive rate.");
12209 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12210 "Network transmit rate.");
12211 /* Create and register base metrics */
12212 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12213 cpuLoadUser, cpuLoadKernel);
12214 aCollector->registerBaseMetric(cpuLoad);
12215 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12216 ramUsageUsed);
12217 aCollector->registerBaseMetric(ramUsage);
12218 MediaList disks;
12219 i_getDiskList(disks);
12220 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12221 diskUsageUsed);
12222 aCollector->registerBaseMetric(diskUsage);
12223
12224 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12225 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12226 new pm::AggregateAvg()));
12227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12228 new pm::AggregateMin()));
12229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12230 new pm::AggregateMax()));
12231 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12232 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12233 new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12235 new pm::AggregateMin()));
12236 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12237 new pm::AggregateMax()));
12238
12239 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12240 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12241 new pm::AggregateAvg()));
12242 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12243 new pm::AggregateMin()));
12244 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12245 new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12248 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12249 new pm::AggregateAvg()));
12250 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12251 new pm::AggregateMin()));
12252 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12253 new pm::AggregateMax()));
12254
12255
12256 /* Guest metrics collector */
12257 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12258 aCollector->registerGuest(mCollectorGuest);
12259 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12260
12261 /* Create sub metrics */
12262 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12263 "Percentage of processor time spent in user mode as seen by the guest.");
12264 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12265 "Percentage of processor time spent in kernel mode as seen by the guest.");
12266 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12267 "Percentage of processor time spent idling as seen by the guest.");
12268
12269 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12270 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12271 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12272 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12273 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12274 pm::SubMetric *guestMemCache = new pm::SubMetric(
12275 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12276
12277 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12278 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12279
12280 /* Create and register base metrics */
12281 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12282 machineNetRx, machineNetTx);
12283 aCollector->registerBaseMetric(machineNetRate);
12284
12285 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12286 guestLoadUser, guestLoadKernel, guestLoadIdle);
12287 aCollector->registerBaseMetric(guestCpuLoad);
12288
12289 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12290 guestMemTotal, guestMemFree,
12291 guestMemBalloon, guestMemShared,
12292 guestMemCache, guestPagedTotal);
12293 aCollector->registerBaseMetric(guestCpuMem);
12294
12295 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12296 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12301 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12306 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12314
12315 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12316 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12319
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12329
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12346 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12349}
12350
12351void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12352{
12353 AssertReturnVoid(isWriteLockOnCurrentThread());
12354
12355 if (aCollector)
12356 {
12357 aCollector->unregisterMetricsFor(aMachine);
12358 aCollector->unregisterBaseMetricsFor(aMachine);
12359 }
12360}
12361
12362#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12363
12364
12365////////////////////////////////////////////////////////////////////////////////
12366
12367DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12368
12369HRESULT SessionMachine::FinalConstruct()
12370{
12371 LogFlowThisFunc(("\n"));
12372
12373 mClientToken = NULL;
12374
12375 return BaseFinalConstruct();
12376}
12377
12378void SessionMachine::FinalRelease()
12379{
12380 LogFlowThisFunc(("\n"));
12381
12382 Assert(!mClientToken);
12383 /* paranoia, should not hang around any more */
12384 if (mClientToken)
12385 {
12386 delete mClientToken;
12387 mClientToken = NULL;
12388 }
12389
12390 uninit(Uninit::Unexpected);
12391
12392 BaseFinalRelease();
12393}
12394
12395/**
12396 * @note Must be called only by Machine::LockMachine() from its own write lock.
12397 */
12398HRESULT SessionMachine::init(Machine *aMachine)
12399{
12400 LogFlowThisFuncEnter();
12401 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12402
12403 AssertReturn(aMachine, E_INVALIDARG);
12404
12405 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12406
12407 /* Enclose the state transition NotReady->InInit->Ready */
12408 AutoInitSpan autoInitSpan(this);
12409 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12410
12411 HRESULT rc = S_OK;
12412
12413 RT_ZERO(mAuthLibCtx);
12414
12415 /* create the machine client token */
12416 try
12417 {
12418 mClientToken = new ClientToken(aMachine, this);
12419 if (!mClientToken->isReady())
12420 {
12421 delete mClientToken;
12422 mClientToken = NULL;
12423 rc = E_FAIL;
12424 }
12425 }
12426 catch (std::bad_alloc &)
12427 {
12428 rc = E_OUTOFMEMORY;
12429 }
12430 if (FAILED(rc))
12431 return rc;
12432
12433 /* memorize the peer Machine */
12434 unconst(mPeer) = aMachine;
12435 /* share the parent pointer */
12436 unconst(mParent) = aMachine->mParent;
12437
12438 /* take the pointers to data to share */
12439 mData.share(aMachine->mData);
12440 mSSData.share(aMachine->mSSData);
12441
12442 mUserData.share(aMachine->mUserData);
12443 mHWData.share(aMachine->mHWData);
12444 mMediumAttachments.share(aMachine->mMediumAttachments);
12445
12446 mStorageControllers.allocate();
12447 for (StorageControllerList::const_iterator
12448 it = aMachine->mStorageControllers->begin();
12449 it != aMachine->mStorageControllers->end();
12450 ++it)
12451 {
12452 ComObjPtr<StorageController> ctl;
12453 ctl.createObject();
12454 ctl->init(this, *it);
12455 mStorageControllers->push_back(ctl);
12456 }
12457
12458 mUSBControllers.allocate();
12459 for (USBControllerList::const_iterator
12460 it = aMachine->mUSBControllers->begin();
12461 it != aMachine->mUSBControllers->end();
12462 ++it)
12463 {
12464 ComObjPtr<USBController> ctl;
12465 ctl.createObject();
12466 ctl->init(this, *it);
12467 mUSBControllers->push_back(ctl);
12468 }
12469
12470 unconst(mBIOSSettings).createObject();
12471 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12472 unconst(mRecordingSettings).createObject();
12473 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12474 /* create another GraphicsAdapter object that will be mutable */
12475 unconst(mGraphicsAdapter).createObject();
12476 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12477 /* create another VRDEServer object that will be mutable */
12478 unconst(mVRDEServer).createObject();
12479 mVRDEServer->init(this, aMachine->mVRDEServer);
12480 /* create another audio adapter object that will be mutable */
12481 unconst(mAudioAdapter).createObject();
12482 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12483 /* create a list of serial ports that will be mutable */
12484 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12485 {
12486 unconst(mSerialPorts[slot]).createObject();
12487 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12488 }
12489 /* create a list of parallel ports that will be mutable */
12490 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12491 {
12492 unconst(mParallelPorts[slot]).createObject();
12493 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12494 }
12495
12496 /* create another USB device filters object that will be mutable */
12497 unconst(mUSBDeviceFilters).createObject();
12498 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12499
12500 /* create a list of network adapters that will be mutable */
12501 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12502 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12503 {
12504 unconst(mNetworkAdapters[slot]).createObject();
12505 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12506 }
12507
12508 /* create another bandwidth control object that will be mutable */
12509 unconst(mBandwidthControl).createObject();
12510 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12511
12512 /* default is to delete saved state on Saved -> PoweredOff transition */
12513 mRemoveSavedState = true;
12514
12515 /* Confirm a successful initialization when it's the case */
12516 autoInitSpan.setSucceeded();
12517
12518 miNATNetworksStarted = 0;
12519
12520 LogFlowThisFuncLeave();
12521 return rc;
12522}
12523
12524/**
12525 * Uninitializes this session object. If the reason is other than
12526 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12527 * or the client watcher code.
12528 *
12529 * @param aReason uninitialization reason
12530 *
12531 * @note Locks mParent + this object for writing.
12532 */
12533void SessionMachine::uninit(Uninit::Reason aReason)
12534{
12535 LogFlowThisFuncEnter();
12536 LogFlowThisFunc(("reason=%d\n", aReason));
12537
12538 /*
12539 * Strongly reference ourselves to prevent this object deletion after
12540 * mData->mSession.mMachine.setNull() below (which can release the last
12541 * reference and call the destructor). Important: this must be done before
12542 * accessing any members (and before AutoUninitSpan that does it as well).
12543 * This self reference will be released as the very last step on return.
12544 */
12545 ComObjPtr<SessionMachine> selfRef;
12546 if (aReason != Uninit::Unexpected)
12547 selfRef = this;
12548
12549 /* Enclose the state transition Ready->InUninit->NotReady */
12550 AutoUninitSpan autoUninitSpan(this);
12551 if (autoUninitSpan.uninitDone())
12552 {
12553 LogFlowThisFunc(("Already uninitialized\n"));
12554 LogFlowThisFuncLeave();
12555 return;
12556 }
12557
12558 if (autoUninitSpan.initFailed())
12559 {
12560 /* We've been called by init() because it's failed. It's not really
12561 * necessary (nor it's safe) to perform the regular uninit sequence
12562 * below, the following is enough.
12563 */
12564 LogFlowThisFunc(("Initialization failed.\n"));
12565 /* destroy the machine client token */
12566 if (mClientToken)
12567 {
12568 delete mClientToken;
12569 mClientToken = NULL;
12570 }
12571 uninitDataAndChildObjects();
12572 mData.free();
12573 unconst(mParent) = NULL;
12574 unconst(mPeer) = NULL;
12575 LogFlowThisFuncLeave();
12576 return;
12577 }
12578
12579 MachineState_T lastState;
12580 {
12581 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12582 lastState = mData->mMachineState;
12583 }
12584 NOREF(lastState);
12585
12586#ifdef VBOX_WITH_USB
12587 // release all captured USB devices, but do this before requesting the locks below
12588 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12589 {
12590 /* Console::captureUSBDevices() is called in the VM process only after
12591 * setting the machine state to Starting or Restoring.
12592 * Console::detachAllUSBDevices() will be called upon successful
12593 * termination. So, we need to release USB devices only if there was
12594 * an abnormal termination of a running VM.
12595 *
12596 * This is identical to SessionMachine::DetachAllUSBDevices except
12597 * for the aAbnormal argument. */
12598 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12599 AssertComRC(rc);
12600 NOREF(rc);
12601
12602 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12603 if (service)
12604 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12605 }
12606#endif /* VBOX_WITH_USB */
12607
12608 // we need to lock this object in uninit() because the lock is shared
12609 // with mPeer (as well as data we modify below). mParent lock is needed
12610 // by several calls to it.
12611 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12612
12613#ifdef VBOX_WITH_RESOURCE_USAGE_API
12614 /*
12615 * It is safe to call Machine::i_unregisterMetrics() here because
12616 * PerformanceCollector::samplerCallback no longer accesses guest methods
12617 * holding the lock.
12618 */
12619 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12620 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12621 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12622 if (mCollectorGuest)
12623 {
12624 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12625 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12626 mCollectorGuest = NULL;
12627 }
12628#endif
12629
12630 if (aReason == Uninit::Abnormal)
12631 {
12632 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12633
12634 /* reset the state to Aborted */
12635 if (mData->mMachineState != MachineState_Aborted)
12636 i_setMachineState(MachineState_Aborted);
12637 }
12638
12639 // any machine settings modified?
12640 if (mData->flModifications)
12641 {
12642 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12643 i_rollback(false /* aNotify */);
12644 }
12645
12646 mData->mSession.mPID = NIL_RTPROCESS;
12647
12648 if (aReason == Uninit::Unexpected)
12649 {
12650 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12651 * client watcher thread to update the set of machines that have open
12652 * sessions. */
12653 mParent->i_updateClientWatcher();
12654 }
12655
12656 /* uninitialize all remote controls */
12657 if (mData->mSession.mRemoteControls.size())
12658 {
12659 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12660 mData->mSession.mRemoteControls.size()));
12661
12662 /* Always restart a the beginning, since the iterator is invalidated
12663 * by using erase(). */
12664 for (Data::Session::RemoteControlList::iterator
12665 it = mData->mSession.mRemoteControls.begin();
12666 it != mData->mSession.mRemoteControls.end();
12667 it = mData->mSession.mRemoteControls.begin())
12668 {
12669 ComPtr<IInternalSessionControl> pControl = *it;
12670 mData->mSession.mRemoteControls.erase(it);
12671 multilock.release();
12672 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12673 HRESULT rc = pControl->Uninitialize();
12674 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12675 if (FAILED(rc))
12676 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12677 multilock.acquire();
12678 }
12679 mData->mSession.mRemoteControls.clear();
12680 }
12681
12682 /* Remove all references to the NAT network service. The service will stop
12683 * if all references (also from other VMs) are removed. */
12684 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12685 {
12686 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12687 {
12688 BOOL enabled;
12689 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12690 if ( FAILED(hrc)
12691 || !enabled)
12692 continue;
12693
12694 NetworkAttachmentType_T type;
12695 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12696 if ( SUCCEEDED(hrc)
12697 && type == NetworkAttachmentType_NATNetwork)
12698 {
12699 Bstr name;
12700 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12701 if (SUCCEEDED(hrc))
12702 {
12703 multilock.release();
12704 Utf8Str strName(name);
12705 LogRel(("VM '%s' stops using NAT network '%s'\n",
12706 mUserData->s.strName.c_str(), strName.c_str()));
12707 mParent->i_natNetworkRefDec(strName);
12708 multilock.acquire();
12709 }
12710 }
12711 }
12712 }
12713
12714 /*
12715 * An expected uninitialization can come only from #i_checkForDeath().
12716 * Otherwise it means that something's gone really wrong (for example,
12717 * the Session implementation has released the VirtualBox reference
12718 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12719 * etc). However, it's also possible, that the client releases the IPC
12720 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12721 * but the VirtualBox release event comes first to the server process.
12722 * This case is practically possible, so we should not assert on an
12723 * unexpected uninit, just log a warning.
12724 */
12725
12726 if (aReason == Uninit::Unexpected)
12727 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12728
12729 if (aReason != Uninit::Normal)
12730 {
12731 mData->mSession.mDirectControl.setNull();
12732 }
12733 else
12734 {
12735 /* this must be null here (see #OnSessionEnd()) */
12736 Assert(mData->mSession.mDirectControl.isNull());
12737 Assert(mData->mSession.mState == SessionState_Unlocking);
12738 Assert(!mData->mSession.mProgress.isNull());
12739 }
12740 if (mData->mSession.mProgress)
12741 {
12742 if (aReason == Uninit::Normal)
12743 mData->mSession.mProgress->i_notifyComplete(S_OK);
12744 else
12745 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12746 COM_IIDOF(ISession),
12747 getComponentName(),
12748 tr("The VM session was aborted"));
12749 mData->mSession.mProgress.setNull();
12750 }
12751
12752 if (mConsoleTaskData.mProgress)
12753 {
12754 Assert(aReason == Uninit::Abnormal);
12755 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12756 COM_IIDOF(ISession),
12757 getComponentName(),
12758 tr("The VM session was aborted"));
12759 mConsoleTaskData.mProgress.setNull();
12760 }
12761
12762 /* remove the association between the peer machine and this session machine */
12763 Assert( (SessionMachine*)mData->mSession.mMachine == this
12764 || aReason == Uninit::Unexpected);
12765
12766 /* reset the rest of session data */
12767 mData->mSession.mLockType = LockType_Null;
12768 mData->mSession.mMachine.setNull();
12769 mData->mSession.mState = SessionState_Unlocked;
12770 mData->mSession.mName.setNull();
12771
12772 /* destroy the machine client token before leaving the exclusive lock */
12773 if (mClientToken)
12774 {
12775 delete mClientToken;
12776 mClientToken = NULL;
12777 }
12778
12779 /* fire an event */
12780 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12781
12782 uninitDataAndChildObjects();
12783
12784 /* free the essential data structure last */
12785 mData.free();
12786
12787 /* release the exclusive lock before setting the below two to NULL */
12788 multilock.release();
12789
12790 unconst(mParent) = NULL;
12791 unconst(mPeer) = NULL;
12792
12793 AuthLibUnload(&mAuthLibCtx);
12794
12795 LogFlowThisFuncLeave();
12796}
12797
12798// util::Lockable interface
12799////////////////////////////////////////////////////////////////////////////////
12800
12801/**
12802 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12803 * with the primary Machine instance (mPeer).
12804 */
12805RWLockHandle *SessionMachine::lockHandle() const
12806{
12807 AssertReturn(mPeer != NULL, NULL);
12808 return mPeer->lockHandle();
12809}
12810
12811// IInternalMachineControl methods
12812////////////////////////////////////////////////////////////////////////////////
12813
12814/**
12815 * Passes collected guest statistics to performance collector object
12816 */
12817HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12818 ULONG aCpuKernel, ULONG aCpuIdle,
12819 ULONG aMemTotal, ULONG aMemFree,
12820 ULONG aMemBalloon, ULONG aMemShared,
12821 ULONG aMemCache, ULONG aPageTotal,
12822 ULONG aAllocVMM, ULONG aFreeVMM,
12823 ULONG aBalloonedVMM, ULONG aSharedVMM,
12824 ULONG aVmNetRx, ULONG aVmNetTx)
12825{
12826#ifdef VBOX_WITH_RESOURCE_USAGE_API
12827 if (mCollectorGuest)
12828 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12829 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12830 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12831 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12832
12833 return S_OK;
12834#else
12835 NOREF(aValidStats);
12836 NOREF(aCpuUser);
12837 NOREF(aCpuKernel);
12838 NOREF(aCpuIdle);
12839 NOREF(aMemTotal);
12840 NOREF(aMemFree);
12841 NOREF(aMemBalloon);
12842 NOREF(aMemShared);
12843 NOREF(aMemCache);
12844 NOREF(aPageTotal);
12845 NOREF(aAllocVMM);
12846 NOREF(aFreeVMM);
12847 NOREF(aBalloonedVMM);
12848 NOREF(aSharedVMM);
12849 NOREF(aVmNetRx);
12850 NOREF(aVmNetTx);
12851 return E_NOTIMPL;
12852#endif
12853}
12854
12855////////////////////////////////////////////////////////////////////////////////
12856//
12857// SessionMachine task records
12858//
12859////////////////////////////////////////////////////////////////////////////////
12860
12861/**
12862 * Task record for saving the machine state.
12863 */
12864class SessionMachine::SaveStateTask
12865 : public Machine::Task
12866{
12867public:
12868 SaveStateTask(SessionMachine *m,
12869 Progress *p,
12870 const Utf8Str &t,
12871 Reason_T enmReason,
12872 const Utf8Str &strStateFilePath)
12873 : Task(m, p, t),
12874 m_enmReason(enmReason),
12875 m_strStateFilePath(strStateFilePath)
12876 {}
12877
12878private:
12879 void handler()
12880 {
12881 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12882 }
12883
12884 Reason_T m_enmReason;
12885 Utf8Str m_strStateFilePath;
12886
12887 friend class SessionMachine;
12888};
12889
12890/**
12891 * Task thread implementation for SessionMachine::SaveState(), called from
12892 * SessionMachine::taskHandler().
12893 *
12894 * @note Locks this object for writing.
12895 *
12896 * @param task
12897 * @return
12898 */
12899void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12900{
12901 LogFlowThisFuncEnter();
12902
12903 AutoCaller autoCaller(this);
12904 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12905 if (FAILED(autoCaller.rc()))
12906 {
12907 /* we might have been uninitialized because the session was accidentally
12908 * closed by the client, so don't assert */
12909 HRESULT rc = setError(E_FAIL,
12910 tr("The session has been accidentally closed"));
12911 task.m_pProgress->i_notifyComplete(rc);
12912 LogFlowThisFuncLeave();
12913 return;
12914 }
12915
12916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12917
12918 HRESULT rc = S_OK;
12919
12920 try
12921 {
12922 ComPtr<IInternalSessionControl> directControl;
12923 if (mData->mSession.mLockType == LockType_VM)
12924 directControl = mData->mSession.mDirectControl;
12925 if (directControl.isNull())
12926 throw setError(VBOX_E_INVALID_VM_STATE,
12927 tr("Trying to save state without a running VM"));
12928 alock.release();
12929 BOOL fSuspendedBySave;
12930 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12931 Assert(!fSuspendedBySave);
12932 alock.acquire();
12933
12934 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12935 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12936 throw E_FAIL);
12937
12938 if (SUCCEEDED(rc))
12939 {
12940 mSSData->strStateFilePath = task.m_strStateFilePath;
12941
12942 /* save all VM settings */
12943 rc = i_saveSettings(NULL);
12944 // no need to check whether VirtualBox.xml needs saving also since
12945 // we can't have a name change pending at this point
12946 }
12947 else
12948 {
12949 // On failure, set the state to the state we had at the beginning.
12950 i_setMachineState(task.m_machineStateBackup);
12951 i_updateMachineStateOnClient();
12952
12953 // Delete the saved state file (might have been already created).
12954 // No need to check whether this is shared with a snapshot here
12955 // because we certainly created a fresh saved state file here.
12956 RTFileDelete(task.m_strStateFilePath.c_str());
12957 }
12958 }
12959 catch (HRESULT aRC) { rc = aRC; }
12960
12961 task.m_pProgress->i_notifyComplete(rc);
12962
12963 LogFlowThisFuncLeave();
12964}
12965
12966/**
12967 * @note Locks this object for writing.
12968 */
12969HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12970{
12971 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12972}
12973
12974HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12975{
12976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12977
12978 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12979 if (FAILED(rc)) return rc;
12980
12981 if ( mData->mMachineState != MachineState_Running
12982 && mData->mMachineState != MachineState_Paused
12983 )
12984 return setError(VBOX_E_INVALID_VM_STATE,
12985 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12986 Global::stringifyMachineState(mData->mMachineState));
12987
12988 ComObjPtr<Progress> pProgress;
12989 pProgress.createObject();
12990 rc = pProgress->init(i_getVirtualBox(),
12991 static_cast<IMachine *>(this) /* aInitiator */,
12992 tr("Saving the execution state of the virtual machine"),
12993 FALSE /* aCancelable */);
12994 if (FAILED(rc))
12995 return rc;
12996
12997 Utf8Str strStateFilePath;
12998 i_composeSavedStateFilename(strStateFilePath);
12999
13000 /* create and start the task on a separate thread (note that it will not
13001 * start working until we release alock) */
13002 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13003 rc = pTask->createThread();
13004 if (FAILED(rc))
13005 return rc;
13006
13007 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13008 i_setMachineState(MachineState_Saving);
13009 i_updateMachineStateOnClient();
13010
13011 pProgress.queryInterfaceTo(aProgress.asOutParam());
13012
13013 return S_OK;
13014}
13015
13016/**
13017 * @note Locks this object for writing.
13018 */
13019HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13020{
13021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13022
13023 HRESULT rc = i_checkStateDependency(MutableStateDep);
13024 if (FAILED(rc)) return rc;
13025
13026 if ( mData->mMachineState != MachineState_PoweredOff
13027 && mData->mMachineState != MachineState_Teleported
13028 && mData->mMachineState != MachineState_Aborted
13029 )
13030 return setError(VBOX_E_INVALID_VM_STATE,
13031 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13032 Global::stringifyMachineState(mData->mMachineState));
13033
13034 com::Utf8Str stateFilePathFull;
13035 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13036 if (RT_FAILURE(vrc))
13037 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13038 tr("Invalid saved state file path '%s' (%Rrc)"),
13039 aSavedStateFile.c_str(),
13040 vrc);
13041
13042 mSSData->strStateFilePath = stateFilePathFull;
13043
13044 /* The below i_setMachineState() will detect the state transition and will
13045 * update the settings file */
13046
13047 return i_setMachineState(MachineState_Saved);
13048}
13049
13050/**
13051 * @note Locks this object for writing.
13052 */
13053HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13054{
13055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13056
13057 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13058 if (FAILED(rc)) return rc;
13059
13060 if (mData->mMachineState != MachineState_Saved)
13061 return setError(VBOX_E_INVALID_VM_STATE,
13062 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13063 Global::stringifyMachineState(mData->mMachineState));
13064
13065 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13066
13067 /*
13068 * Saved -> PoweredOff transition will be detected in the SessionMachine
13069 * and properly handled.
13070 */
13071 rc = i_setMachineState(MachineState_PoweredOff);
13072 return rc;
13073}
13074
13075
13076/**
13077 * @note Locks the same as #i_setMachineState() does.
13078 */
13079HRESULT SessionMachine::updateState(MachineState_T aState)
13080{
13081 return i_setMachineState(aState);
13082}
13083
13084/**
13085 * @note Locks this object for writing.
13086 */
13087HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13088{
13089 IProgress *pProgress(aProgress);
13090
13091 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13092
13093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13094
13095 if (mData->mSession.mState != SessionState_Locked)
13096 return VBOX_E_INVALID_OBJECT_STATE;
13097
13098 if (!mData->mSession.mProgress.isNull())
13099 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13100
13101 /* If we didn't reference the NAT network service yet, add a reference to
13102 * force a start */
13103 if (miNATNetworksStarted < 1)
13104 {
13105 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13106 {
13107 BOOL enabled;
13108 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13109 if ( FAILED(hrc)
13110 || !enabled)
13111 continue;
13112
13113 NetworkAttachmentType_T type;
13114 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13115 if ( SUCCEEDED(hrc)
13116 && type == NetworkAttachmentType_NATNetwork)
13117 {
13118 Bstr name;
13119 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13120 if (SUCCEEDED(hrc))
13121 {
13122 Utf8Str strName(name);
13123 LogRel(("VM '%s' starts using NAT network '%s'\n",
13124 mUserData->s.strName.c_str(), strName.c_str()));
13125 mPeer->lockHandle()->unlockWrite();
13126 mParent->i_natNetworkRefInc(strName);
13127#ifdef RT_LOCK_STRICT
13128 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13129#else
13130 mPeer->lockHandle()->lockWrite();
13131#endif
13132 }
13133 }
13134 }
13135 miNATNetworksStarted++;
13136 }
13137
13138 LogFlowThisFunc(("returns S_OK.\n"));
13139 return S_OK;
13140}
13141
13142/**
13143 * @note Locks this object for writing.
13144 */
13145HRESULT SessionMachine::endPowerUp(LONG aResult)
13146{
13147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13148
13149 if (mData->mSession.mState != SessionState_Locked)
13150 return VBOX_E_INVALID_OBJECT_STATE;
13151
13152 /* Finalize the LaunchVMProcess progress object. */
13153 if (mData->mSession.mProgress)
13154 {
13155 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13156 mData->mSession.mProgress.setNull();
13157 }
13158
13159 if (SUCCEEDED((HRESULT)aResult))
13160 {
13161#ifdef VBOX_WITH_RESOURCE_USAGE_API
13162 /* The VM has been powered up successfully, so it makes sense
13163 * now to offer the performance metrics for a running machine
13164 * object. Doing it earlier wouldn't be safe. */
13165 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13166 mData->mSession.mPID);
13167#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13168 }
13169
13170 return S_OK;
13171}
13172
13173/**
13174 * @note Locks this object for writing.
13175 */
13176HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13177{
13178 LogFlowThisFuncEnter();
13179
13180#ifdef VBOX_WITH_CLOUD_NET
13181 mPeer->i_disconnectFromCloudNetwork();
13182#endif /* VBOX_WITH_CLOUD_NET */
13183
13184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13185
13186 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13187 E_FAIL);
13188
13189 /* create a progress object to track operation completion */
13190 ComObjPtr<Progress> pProgress;
13191 pProgress.createObject();
13192 pProgress->init(i_getVirtualBox(),
13193 static_cast<IMachine *>(this) /* aInitiator */,
13194 tr("Stopping the virtual machine"),
13195 FALSE /* aCancelable */);
13196
13197 /* fill in the console task data */
13198 mConsoleTaskData.mLastState = mData->mMachineState;
13199 mConsoleTaskData.mProgress = pProgress;
13200
13201 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13202 i_setMachineState(MachineState_Stopping);
13203
13204 pProgress.queryInterfaceTo(aProgress.asOutParam());
13205
13206 return S_OK;
13207}
13208
13209/**
13210 * @note Locks this object for writing.
13211 */
13212HRESULT SessionMachine::endPoweringDown(LONG aResult,
13213 const com::Utf8Str &aErrMsg)
13214{
13215 LogFlowThisFuncEnter();
13216
13217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13218
13219 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13220 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13221 && mConsoleTaskData.mLastState != MachineState_Null,
13222 E_FAIL);
13223
13224 /*
13225 * On failure, set the state to the state we had when BeginPoweringDown()
13226 * was called (this is expected by Console::PowerDown() and the associated
13227 * task). On success the VM process already changed the state to
13228 * MachineState_PoweredOff, so no need to do anything.
13229 */
13230 if (FAILED(aResult))
13231 i_setMachineState(mConsoleTaskData.mLastState);
13232
13233 /* notify the progress object about operation completion */
13234 Assert(mConsoleTaskData.mProgress);
13235 if (SUCCEEDED(aResult))
13236 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13237 else
13238 {
13239 if (aErrMsg.length())
13240 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13241 COM_IIDOF(ISession),
13242 getComponentName(),
13243 aErrMsg.c_str());
13244 else
13245 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13246 }
13247
13248 /* clear out the temporary saved state data */
13249 mConsoleTaskData.mLastState = MachineState_Null;
13250 mConsoleTaskData.mProgress.setNull();
13251
13252 LogFlowThisFuncLeave();
13253 return S_OK;
13254}
13255
13256
13257/**
13258 * Goes through the USB filters of the given machine to see if the given
13259 * device matches any filter or not.
13260 *
13261 * @note Locks the same as USBController::hasMatchingFilter() does.
13262 */
13263HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13264 BOOL *aMatched,
13265 ULONG *aMaskedInterfaces)
13266{
13267 LogFlowThisFunc(("\n"));
13268
13269#ifdef VBOX_WITH_USB
13270 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13271#else
13272 NOREF(aDevice);
13273 NOREF(aMaskedInterfaces);
13274 *aMatched = FALSE;
13275#endif
13276
13277 return S_OK;
13278}
13279
13280/**
13281 * @note Locks the same as Host::captureUSBDevice() does.
13282 */
13283HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13284{
13285 LogFlowThisFunc(("\n"));
13286
13287#ifdef VBOX_WITH_USB
13288 /* if captureDeviceForVM() fails, it must have set extended error info */
13289 clearError();
13290 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13291 if (FAILED(rc)) return rc;
13292
13293 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13294 AssertReturn(service, E_FAIL);
13295 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13296#else
13297 RT_NOREF(aId, aCaptureFilename);
13298 return E_NOTIMPL;
13299#endif
13300}
13301
13302/**
13303 * @note Locks the same as Host::detachUSBDevice() does.
13304 */
13305HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13306 BOOL aDone)
13307{
13308 LogFlowThisFunc(("\n"));
13309
13310#ifdef VBOX_WITH_USB
13311 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13312 AssertReturn(service, E_FAIL);
13313 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13314#else
13315 NOREF(aId);
13316 NOREF(aDone);
13317 return E_NOTIMPL;
13318#endif
13319}
13320
13321/**
13322 * Inserts all machine filters to the USB proxy service and then calls
13323 * Host::autoCaptureUSBDevices().
13324 *
13325 * Called by Console from the VM process upon VM startup.
13326 *
13327 * @note Locks what called methods lock.
13328 */
13329HRESULT SessionMachine::autoCaptureUSBDevices()
13330{
13331 LogFlowThisFunc(("\n"));
13332
13333#ifdef VBOX_WITH_USB
13334 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13335 AssertComRC(rc);
13336 NOREF(rc);
13337
13338 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13339 AssertReturn(service, E_FAIL);
13340 return service->autoCaptureDevicesForVM(this);
13341#else
13342 return S_OK;
13343#endif
13344}
13345
13346/**
13347 * Removes all machine filters from the USB proxy service and then calls
13348 * Host::detachAllUSBDevices().
13349 *
13350 * Called by Console from the VM process upon normal VM termination or by
13351 * SessionMachine::uninit() upon abnormal VM termination (from under the
13352 * Machine/SessionMachine lock).
13353 *
13354 * @note Locks what called methods lock.
13355 */
13356HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13357{
13358 LogFlowThisFunc(("\n"));
13359
13360#ifdef VBOX_WITH_USB
13361 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13362 AssertComRC(rc);
13363 NOREF(rc);
13364
13365 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13366 AssertReturn(service, E_FAIL);
13367 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13368#else
13369 NOREF(aDone);
13370 return S_OK;
13371#endif
13372}
13373
13374/**
13375 * @note Locks this object for writing.
13376 */
13377HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13378 ComPtr<IProgress> &aProgress)
13379{
13380 LogFlowThisFuncEnter();
13381
13382 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13383 /*
13384 * We don't assert below because it might happen that a non-direct session
13385 * informs us it is closed right after we've been uninitialized -- it's ok.
13386 */
13387
13388 /* get IInternalSessionControl interface */
13389 ComPtr<IInternalSessionControl> control(aSession);
13390
13391 ComAssertRet(!control.isNull(), E_INVALIDARG);
13392
13393 /* Creating a Progress object requires the VirtualBox lock, and
13394 * thus locking it here is required by the lock order rules. */
13395 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13396
13397 if (control == mData->mSession.mDirectControl)
13398 {
13399 /* The direct session is being normally closed by the client process
13400 * ----------------------------------------------------------------- */
13401
13402 /* go to the closing state (essential for all open*Session() calls and
13403 * for #i_checkForDeath()) */
13404 Assert(mData->mSession.mState == SessionState_Locked);
13405 mData->mSession.mState = SessionState_Unlocking;
13406
13407 /* set direct control to NULL to release the remote instance */
13408 mData->mSession.mDirectControl.setNull();
13409 LogFlowThisFunc(("Direct control is set to NULL\n"));
13410
13411 if (mData->mSession.mProgress)
13412 {
13413 /* finalize the progress, someone might wait if a frontend
13414 * closes the session before powering on the VM. */
13415 mData->mSession.mProgress->notifyComplete(E_FAIL,
13416 COM_IIDOF(ISession),
13417 getComponentName(),
13418 tr("The VM session was closed before any attempt to power it on"));
13419 mData->mSession.mProgress.setNull();
13420 }
13421
13422 /* Create the progress object the client will use to wait until
13423 * #i_checkForDeath() is called to uninitialize this session object after
13424 * it releases the IPC semaphore.
13425 * Note! Because we're "reusing" mProgress here, this must be a proxy
13426 * object just like for LaunchVMProcess. */
13427 Assert(mData->mSession.mProgress.isNull());
13428 ComObjPtr<ProgressProxy> progress;
13429 progress.createObject();
13430 ComPtr<IUnknown> pPeer(mPeer);
13431 progress->init(mParent, pPeer,
13432 Bstr(tr("Closing session")).raw(),
13433 FALSE /* aCancelable */);
13434 progress.queryInterfaceTo(aProgress.asOutParam());
13435 mData->mSession.mProgress = progress;
13436 }
13437 else
13438 {
13439 /* the remote session is being normally closed */
13440 bool found = false;
13441 for (Data::Session::RemoteControlList::iterator
13442 it = mData->mSession.mRemoteControls.begin();
13443 it != mData->mSession.mRemoteControls.end();
13444 ++it)
13445 {
13446 if (control == *it)
13447 {
13448 found = true;
13449 // This MUST be erase(it), not remove(*it) as the latter
13450 // triggers a very nasty use after free due to the place where
13451 // the value "lives".
13452 mData->mSession.mRemoteControls.erase(it);
13453 break;
13454 }
13455 }
13456 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13457 E_INVALIDARG);
13458 }
13459
13460 /* signal the client watcher thread, because the client is going away */
13461 mParent->i_updateClientWatcher();
13462
13463 LogFlowThisFuncLeave();
13464 return S_OK;
13465}
13466
13467HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13468{
13469#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13470 ULONG uID;
13471 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13472 if (RT_SUCCESS(rc))
13473 {
13474 if (aID)
13475 *aID = uID;
13476 return S_OK;
13477 }
13478 return E_FAIL;
13479#else
13480 RT_NOREF(aParms, aID);
13481 ReturnComNotImplemented();
13482#endif
13483}
13484
13485HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13486{
13487#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13488 return mParent->i_onClipboardAreaUnregister(aID);
13489#else
13490 RT_NOREF(aID);
13491 ReturnComNotImplemented();
13492#endif
13493}
13494
13495HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13496{
13497#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13498 return mParent->i_onClipboardAreaAttach(aID);
13499#else
13500 RT_NOREF(aID);
13501 ReturnComNotImplemented();
13502#endif
13503}
13504HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13505{
13506#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13507 return mParent->i_onClipboardAreaDetach(aID);
13508#else
13509 RT_NOREF(aID);
13510 ReturnComNotImplemented();
13511#endif
13512}
13513
13514HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13515{
13516#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13517 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13518 if (aID)
13519 *aID = uID;
13520 return S_OK;
13521#else
13522 RT_NOREF(aID);
13523 ReturnComNotImplemented();
13524#endif
13525}
13526
13527HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13528{
13529#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13530 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13531 if (aRefCount)
13532 *aRefCount = uRefCount;
13533 return S_OK;
13534#else
13535 RT_NOREF(aID, aRefCount);
13536 ReturnComNotImplemented();
13537#endif
13538}
13539
13540HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13541 std::vector<com::Utf8Str> &aValues,
13542 std::vector<LONG64> &aTimestamps,
13543 std::vector<com::Utf8Str> &aFlags)
13544{
13545 LogFlowThisFunc(("\n"));
13546
13547#ifdef VBOX_WITH_GUEST_PROPS
13548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13549
13550 size_t cEntries = mHWData->mGuestProperties.size();
13551 aNames.resize(cEntries);
13552 aValues.resize(cEntries);
13553 aTimestamps.resize(cEntries);
13554 aFlags.resize(cEntries);
13555
13556 size_t i = 0;
13557 for (HWData::GuestPropertyMap::const_iterator
13558 it = mHWData->mGuestProperties.begin();
13559 it != mHWData->mGuestProperties.end();
13560 ++it, ++i)
13561 {
13562 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13563 aNames[i] = it->first;
13564 aValues[i] = it->second.strValue;
13565 aTimestamps[i] = it->second.mTimestamp;
13566
13567 /* If it is NULL, keep it NULL. */
13568 if (it->second.mFlags)
13569 {
13570 GuestPropWriteFlags(it->second.mFlags, szFlags);
13571 aFlags[i] = szFlags;
13572 }
13573 else
13574 aFlags[i] = "";
13575 }
13576 return S_OK;
13577#else
13578 ReturnComNotImplemented();
13579#endif
13580}
13581
13582HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13583 const com::Utf8Str &aValue,
13584 LONG64 aTimestamp,
13585 const com::Utf8Str &aFlags)
13586{
13587 LogFlowThisFunc(("\n"));
13588
13589#ifdef VBOX_WITH_GUEST_PROPS
13590 try
13591 {
13592 /*
13593 * Convert input up front.
13594 */
13595 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13596 if (aFlags.length())
13597 {
13598 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13599 AssertRCReturn(vrc, E_INVALIDARG);
13600 }
13601
13602 /*
13603 * Now grab the object lock, validate the state and do the update.
13604 */
13605
13606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13607
13608 if (!Global::IsOnline(mData->mMachineState))
13609 {
13610 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13611 VBOX_E_INVALID_VM_STATE);
13612 }
13613
13614 i_setModified(IsModified_MachineData);
13615 mHWData.backup();
13616
13617 bool fDelete = !aValue.length();
13618 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13619 if (it != mHWData->mGuestProperties.end())
13620 {
13621 if (!fDelete)
13622 {
13623 it->second.strValue = aValue;
13624 it->second.mTimestamp = aTimestamp;
13625 it->second.mFlags = fFlags;
13626 }
13627 else
13628 mHWData->mGuestProperties.erase(it);
13629
13630 mData->mGuestPropertiesModified = TRUE;
13631 }
13632 else if (!fDelete)
13633 {
13634 HWData::GuestProperty prop;
13635 prop.strValue = aValue;
13636 prop.mTimestamp = aTimestamp;
13637 prop.mFlags = fFlags;
13638
13639 mHWData->mGuestProperties[aName] = prop;
13640 mData->mGuestPropertiesModified = TRUE;
13641 }
13642
13643 alock.release();
13644
13645 mParent->i_onGuestPropertyChange(mData->mUuid,
13646 Bstr(aName).raw(),
13647 Bstr(aValue).raw(),
13648 Bstr(aFlags).raw());
13649 }
13650 catch (...)
13651 {
13652 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13653 }
13654 return S_OK;
13655#else
13656 ReturnComNotImplemented();
13657#endif
13658}
13659
13660
13661HRESULT SessionMachine::lockMedia()
13662{
13663 AutoMultiWriteLock2 alock(this->lockHandle(),
13664 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13665
13666 AssertReturn( mData->mMachineState == MachineState_Starting
13667 || mData->mMachineState == MachineState_Restoring
13668 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13669
13670 clearError();
13671 alock.release();
13672 return i_lockMedia();
13673}
13674
13675HRESULT SessionMachine::unlockMedia()
13676{
13677 HRESULT hrc = i_unlockMedia();
13678 return hrc;
13679}
13680
13681HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13682 ComPtr<IMediumAttachment> &aNewAttachment)
13683{
13684 // request the host lock first, since might be calling Host methods for getting host drives;
13685 // next, protect the media tree all the while we're in here, as well as our member variables
13686 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13687 this->lockHandle(),
13688 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13689
13690 IMediumAttachment *iAttach = aAttachment;
13691 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13692
13693 Utf8Str ctrlName;
13694 LONG lPort;
13695 LONG lDevice;
13696 bool fTempEject;
13697 {
13698 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13699
13700 /* Need to query the details first, as the IMediumAttachment reference
13701 * might be to the original settings, which we are going to change. */
13702 ctrlName = pAttach->i_getControllerName();
13703 lPort = pAttach->i_getPort();
13704 lDevice = pAttach->i_getDevice();
13705 fTempEject = pAttach->i_getTempEject();
13706 }
13707
13708 if (!fTempEject)
13709 {
13710 /* Remember previously mounted medium. The medium before taking the
13711 * backup is not necessarily the same thing. */
13712 ComObjPtr<Medium> oldmedium;
13713 oldmedium = pAttach->i_getMedium();
13714
13715 i_setModified(IsModified_Storage);
13716 mMediumAttachments.backup();
13717
13718 // The backup operation makes the pAttach reference point to the
13719 // old settings. Re-get the correct reference.
13720 pAttach = i_findAttachment(*mMediumAttachments.data(),
13721 ctrlName,
13722 lPort,
13723 lDevice);
13724
13725 {
13726 AutoCaller autoAttachCaller(this);
13727 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13728
13729 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13730 if (!oldmedium.isNull())
13731 oldmedium->i_removeBackReference(mData->mUuid);
13732
13733 pAttach->i_updateMedium(NULL);
13734 pAttach->i_updateEjected();
13735 }
13736
13737 i_setModified(IsModified_Storage);
13738 }
13739 else
13740 {
13741 {
13742 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13743 pAttach->i_updateEjected();
13744 }
13745 }
13746
13747 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13748
13749 return S_OK;
13750}
13751
13752HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13753 com::Utf8Str &aResult)
13754{
13755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13756
13757 HRESULT hr = S_OK;
13758
13759 if (!mAuthLibCtx.hAuthLibrary)
13760 {
13761 /* Load the external authentication library. */
13762 Bstr authLibrary;
13763 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13764
13765 Utf8Str filename = authLibrary;
13766
13767 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13768 if (RT_FAILURE(vrc))
13769 hr = setErrorBoth(E_FAIL, vrc,
13770 tr("Could not load the external authentication library '%s' (%Rrc)"),
13771 filename.c_str(), vrc);
13772 }
13773
13774 /* The auth library might need the machine lock. */
13775 alock.release();
13776
13777 if (FAILED(hr))
13778 return hr;
13779
13780 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13781 {
13782 enum VRDEAuthParams
13783 {
13784 parmUuid = 1,
13785 parmGuestJudgement,
13786 parmUser,
13787 parmPassword,
13788 parmDomain,
13789 parmClientId
13790 };
13791
13792 AuthResult result = AuthResultAccessDenied;
13793
13794 Guid uuid(aAuthParams[parmUuid]);
13795 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13796 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13797
13798 result = AuthLibAuthenticate(&mAuthLibCtx,
13799 uuid.raw(), guestJudgement,
13800 aAuthParams[parmUser].c_str(),
13801 aAuthParams[parmPassword].c_str(),
13802 aAuthParams[parmDomain].c_str(),
13803 u32ClientId);
13804
13805 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13806 size_t cbPassword = aAuthParams[parmPassword].length();
13807 if (cbPassword)
13808 {
13809 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13810 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13811 }
13812
13813 if (result == AuthResultAccessGranted)
13814 aResult = "granted";
13815 else
13816 aResult = "denied";
13817
13818 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13819 aAuthParams[parmUser].c_str(), aResult.c_str()));
13820 }
13821 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13822 {
13823 enum VRDEAuthDisconnectParams
13824 {
13825 parmUuid = 1,
13826 parmClientId
13827 };
13828
13829 Guid uuid(aAuthParams[parmUuid]);
13830 uint32_t u32ClientId = 0;
13831 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13832 }
13833 else
13834 {
13835 hr = E_INVALIDARG;
13836 }
13837
13838 return hr;
13839}
13840
13841// public methods only for internal purposes
13842/////////////////////////////////////////////////////////////////////////////
13843
13844#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13845/**
13846 * Called from the client watcher thread to check for expected or unexpected
13847 * death of the client process that has a direct session to this machine.
13848 *
13849 * On Win32 and on OS/2, this method is called only when we've got the
13850 * mutex (i.e. the client has either died or terminated normally) so it always
13851 * returns @c true (the client is terminated, the session machine is
13852 * uninitialized).
13853 *
13854 * On other platforms, the method returns @c true if the client process has
13855 * terminated normally or abnormally and the session machine was uninitialized,
13856 * and @c false if the client process is still alive.
13857 *
13858 * @note Locks this object for writing.
13859 */
13860bool SessionMachine::i_checkForDeath()
13861{
13862 Uninit::Reason reason;
13863 bool terminated = false;
13864
13865 /* Enclose autoCaller with a block because calling uninit() from under it
13866 * will deadlock. */
13867 {
13868 AutoCaller autoCaller(this);
13869 if (!autoCaller.isOk())
13870 {
13871 /* return true if not ready, to cause the client watcher to exclude
13872 * the corresponding session from watching */
13873 LogFlowThisFunc(("Already uninitialized!\n"));
13874 return true;
13875 }
13876
13877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13878
13879 /* Determine the reason of death: if the session state is Closing here,
13880 * everything is fine. Otherwise it means that the client did not call
13881 * OnSessionEnd() before it released the IPC semaphore. This may happen
13882 * either because the client process has abnormally terminated, or
13883 * because it simply forgot to call ISession::Close() before exiting. We
13884 * threat the latter also as an abnormal termination (see
13885 * Session::uninit() for details). */
13886 reason = mData->mSession.mState == SessionState_Unlocking ?
13887 Uninit::Normal :
13888 Uninit::Abnormal;
13889
13890 if (mClientToken)
13891 terminated = mClientToken->release();
13892 } /* AutoCaller block */
13893
13894 if (terminated)
13895 uninit(reason);
13896
13897 return terminated;
13898}
13899
13900void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13901{
13902 LogFlowThisFunc(("\n"));
13903
13904 strTokenId.setNull();
13905
13906 AutoCaller autoCaller(this);
13907 AssertComRCReturnVoid(autoCaller.rc());
13908
13909 Assert(mClientToken);
13910 if (mClientToken)
13911 mClientToken->getId(strTokenId);
13912}
13913#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13914IToken *SessionMachine::i_getToken()
13915{
13916 LogFlowThisFunc(("\n"));
13917
13918 AutoCaller autoCaller(this);
13919 AssertComRCReturn(autoCaller.rc(), NULL);
13920
13921 Assert(mClientToken);
13922 if (mClientToken)
13923 return mClientToken->getToken();
13924 else
13925 return NULL;
13926}
13927#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13928
13929Machine::ClientToken *SessionMachine::i_getClientToken()
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), NULL);
13935
13936 return mClientToken;
13937}
13938
13939
13940/**
13941 * @note Locks this object for reading.
13942 */
13943HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13944{
13945 LogFlowThisFunc(("\n"));
13946
13947 AutoCaller autoCaller(this);
13948 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13949
13950 ComPtr<IInternalSessionControl> directControl;
13951 {
13952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13953 if (mData->mSession.mLockType == LockType_VM)
13954 directControl = mData->mSession.mDirectControl;
13955 }
13956
13957 /* ignore notifications sent after #OnSessionEnd() is called */
13958 if (!directControl)
13959 return S_OK;
13960
13961 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13962}
13963
13964/**
13965 * @note Locks this object for reading.
13966 */
13967HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13968 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13969 IN_BSTR aGuestIp, LONG aGuestPort)
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973 AutoCaller autoCaller(this);
13974 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13975
13976 ComPtr<IInternalSessionControl> directControl;
13977 {
13978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13979 if (mData->mSession.mLockType == LockType_VM)
13980 directControl = mData->mSession.mDirectControl;
13981 }
13982
13983 /* ignore notifications sent after #OnSessionEnd() is called */
13984 if (!directControl)
13985 return S_OK;
13986 /*
13987 * instead acting like callback we ask IVirtualBox deliver corresponding event
13988 */
13989
13990 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13991 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13992 return S_OK;
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 if (mData->mSession.mLockType == LockType_VM)
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015
14016 return directControl->OnAudioAdapterChange(audioAdapter);
14017}
14018
14019/**
14020 * @note Locks this object for reading.
14021 */
14022HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14028
14029 ComPtr<IInternalSessionControl> directControl;
14030 {
14031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14032 if (mData->mSession.mLockType == LockType_VM)
14033 directControl = mData->mSession.mDirectControl;
14034 }
14035
14036 /* ignore notifications sent after #OnSessionEnd() is called */
14037 if (!directControl)
14038 return S_OK;
14039
14040 return directControl->OnSerialPortChange(serialPort);
14041}
14042
14043/**
14044 * @note Locks this object for reading.
14045 */
14046HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14047{
14048 LogFlowThisFunc(("\n"));
14049
14050 AutoCaller autoCaller(this);
14051 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14052
14053 ComPtr<IInternalSessionControl> directControl;
14054 {
14055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14056 if (mData->mSession.mLockType == LockType_VM)
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnParallelPortChange(parallelPort);
14065}
14066
14067/**
14068 * @note Locks this object for reading.
14069 */
14070HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14071{
14072 LogFlowThisFunc(("\n"));
14073
14074 AutoCaller autoCaller(this);
14075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14076
14077 ComPtr<IInternalSessionControl> directControl;
14078 {
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080 if (mData->mSession.mLockType == LockType_VM)
14081 directControl = mData->mSession.mDirectControl;
14082 }
14083
14084 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14085
14086 /* ignore notifications sent after #OnSessionEnd() is called */
14087 if (!directControl)
14088 return S_OK;
14089
14090 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14091}
14092
14093/**
14094 * @note Locks this object for reading.
14095 */
14096HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14097{
14098 LogFlowThisFunc(("\n"));
14099
14100 AutoCaller autoCaller(this);
14101 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14102
14103 ComPtr<IInternalSessionControl> directControl;
14104 {
14105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14106 if (mData->mSession.mLockType == LockType_VM)
14107 directControl = mData->mSession.mDirectControl;
14108 }
14109
14110 mParent->i_onMediumChanged(aAttachment);
14111
14112 /* ignore notifications sent after #OnSessionEnd() is called */
14113 if (!directControl)
14114 return S_OK;
14115
14116 return directControl->OnMediumChange(aAttachment, aForce);
14117}
14118
14119HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14120{
14121 LogFlowThisFunc(("\n"));
14122
14123 AutoCaller autoCaller(this);
14124 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14125
14126 ComPtr<IInternalSessionControl> directControl;
14127 {
14128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14129 if (mData->mSession.mLockType == LockType_VM)
14130 directControl = mData->mSession.mDirectControl;
14131 }
14132
14133 /* ignore notifications sent after #OnSessionEnd() is called */
14134 if (!directControl)
14135 return S_OK;
14136
14137 return directControl->OnVMProcessPriorityChange(aPriority);
14138}
14139
14140/**
14141 * @note Locks this object for reading.
14142 */
14143HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14144{
14145 LogFlowThisFunc(("\n"));
14146
14147 AutoCaller autoCaller(this);
14148 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14149
14150 ComPtr<IInternalSessionControl> directControl;
14151 {
14152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14153 if (mData->mSession.mLockType == LockType_VM)
14154 directControl = mData->mSession.mDirectControl;
14155 }
14156
14157 /* ignore notifications sent after #OnSessionEnd() is called */
14158 if (!directControl)
14159 return S_OK;
14160
14161 return directControl->OnCPUChange(aCPU, aRemove);
14162}
14163
14164HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14165{
14166 LogFlowThisFunc(("\n"));
14167
14168 AutoCaller autoCaller(this);
14169 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14170
14171 ComPtr<IInternalSessionControl> directControl;
14172 {
14173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14174 if (mData->mSession.mLockType == LockType_VM)
14175 directControl = mData->mSession.mDirectControl;
14176 }
14177
14178 /* ignore notifications sent after #OnSessionEnd() is called */
14179 if (!directControl)
14180 return S_OK;
14181
14182 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14183}
14184
14185/**
14186 * @note Locks this object for reading.
14187 */
14188HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14189{
14190 LogFlowThisFunc(("\n"));
14191
14192 AutoCaller autoCaller(this);
14193 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14194
14195 ComPtr<IInternalSessionControl> directControl;
14196 {
14197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14198 if (mData->mSession.mLockType == LockType_VM)
14199 directControl = mData->mSession.mDirectControl;
14200 }
14201
14202 /* ignore notifications sent after #OnSessionEnd() is called */
14203 if (!directControl)
14204 return S_OK;
14205
14206 return directControl->OnVRDEServerChange(aRestart);
14207}
14208
14209/**
14210 * @note Locks this object for reading.
14211 */
14212HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14213{
14214 LogFlowThisFunc(("\n"));
14215
14216 AutoCaller autoCaller(this);
14217 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14218
14219 ComPtr<IInternalSessionControl> directControl;
14220 {
14221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14222 if (mData->mSession.mLockType == LockType_VM)
14223 directControl = mData->mSession.mDirectControl;
14224 }
14225
14226 /* ignore notifications sent after #OnSessionEnd() is called */
14227 if (!directControl)
14228 return S_OK;
14229
14230 return directControl->OnRecordingChange(aEnable);
14231}
14232
14233/**
14234 * @note Locks this object for reading.
14235 */
14236HRESULT SessionMachine::i_onUSBControllerChange()
14237{
14238 LogFlowThisFunc(("\n"));
14239
14240 AutoCaller autoCaller(this);
14241 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14242
14243 ComPtr<IInternalSessionControl> directControl;
14244 {
14245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14246 if (mData->mSession.mLockType == LockType_VM)
14247 directControl = mData->mSession.mDirectControl;
14248 }
14249
14250 /* ignore notifications sent after #OnSessionEnd() is called */
14251 if (!directControl)
14252 return S_OK;
14253
14254 return directControl->OnUSBControllerChange();
14255}
14256
14257/**
14258 * @note Locks this object for reading.
14259 */
14260HRESULT SessionMachine::i_onSharedFolderChange()
14261{
14262 LogFlowThisFunc(("\n"));
14263
14264 AutoCaller autoCaller(this);
14265 AssertComRCReturnRC(autoCaller.rc());
14266
14267 ComPtr<IInternalSessionControl> directControl;
14268 {
14269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14270 if (mData->mSession.mLockType == LockType_VM)
14271 directControl = mData->mSession.mDirectControl;
14272 }
14273
14274 /* ignore notifications sent after #OnSessionEnd() is called */
14275 if (!directControl)
14276 return S_OK;
14277
14278 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14279}
14280
14281/**
14282 * @note Locks this object for reading.
14283 */
14284HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14285{
14286 LogFlowThisFunc(("\n"));
14287
14288 AutoCaller autoCaller(this);
14289 AssertComRCReturnRC(autoCaller.rc());
14290
14291 ComPtr<IInternalSessionControl> directControl;
14292 {
14293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14294 if (mData->mSession.mLockType == LockType_VM)
14295 directControl = mData->mSession.mDirectControl;
14296 }
14297
14298 /* ignore notifications sent after #OnSessionEnd() is called */
14299 if (!directControl)
14300 return S_OK;
14301
14302 return directControl->OnClipboardModeChange(aClipboardMode);
14303}
14304
14305/**
14306 * @note Locks this object for reading.
14307 */
14308HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14309{
14310 LogFlowThisFunc(("\n"));
14311
14312 AutoCaller autoCaller(this);
14313 AssertComRCReturnRC(autoCaller.rc());
14314
14315 ComPtr<IInternalSessionControl> directControl;
14316 {
14317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14318 if (mData->mSession.mLockType == LockType_VM)
14319 directControl = mData->mSession.mDirectControl;
14320 }
14321
14322 /* ignore notifications sent after #OnSessionEnd() is called */
14323 if (!directControl)
14324 return S_OK;
14325
14326 return directControl->OnClipboardFileTransferModeChange(aEnable);
14327}
14328
14329/**
14330 * @note Locks this object for reading.
14331 */
14332HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14333{
14334 LogFlowThisFunc(("\n"));
14335
14336 AutoCaller autoCaller(this);
14337 AssertComRCReturnRC(autoCaller.rc());
14338
14339 ComPtr<IInternalSessionControl> directControl;
14340 {
14341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14342 if (mData->mSession.mLockType == LockType_VM)
14343 directControl = mData->mSession.mDirectControl;
14344 }
14345
14346 /* ignore notifications sent after #OnSessionEnd() is called */
14347 if (!directControl)
14348 return S_OK;
14349
14350 return directControl->OnDnDModeChange(aDnDMode);
14351}
14352
14353/**
14354 * @note Locks this object for reading.
14355 */
14356HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14357{
14358 LogFlowThisFunc(("\n"));
14359
14360 AutoCaller autoCaller(this);
14361 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14362
14363 ComPtr<IInternalSessionControl> directControl;
14364 {
14365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14366 if (mData->mSession.mLockType == LockType_VM)
14367 directControl = mData->mSession.mDirectControl;
14368 }
14369
14370 /* ignore notifications sent after #OnSessionEnd() is called */
14371 if (!directControl)
14372 return S_OK;
14373
14374 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14375}
14376
14377/**
14378 * @note Locks this object for reading.
14379 */
14380HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14381{
14382 LogFlowThisFunc(("\n"));
14383
14384 AutoCaller autoCaller(this);
14385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14386
14387 ComPtr<IInternalSessionControl> directControl;
14388 {
14389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14390 if (mData->mSession.mLockType == LockType_VM)
14391 directControl = mData->mSession.mDirectControl;
14392 }
14393
14394 /* ignore notifications sent after #OnSessionEnd() is called */
14395 if (!directControl)
14396 return S_OK;
14397
14398 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14399}
14400
14401/**
14402 * Returns @c true if this machine's USB controller reports it has a matching
14403 * filter for the given USB device and @c false otherwise.
14404 *
14405 * @note locks this object for reading.
14406 */
14407bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14408{
14409 AutoCaller autoCaller(this);
14410 /* silently return if not ready -- this method may be called after the
14411 * direct machine session has been called */
14412 if (!autoCaller.isOk())
14413 return false;
14414
14415#ifdef VBOX_WITH_USB
14416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14417
14418 switch (mData->mMachineState)
14419 {
14420 case MachineState_Starting:
14421 case MachineState_Restoring:
14422 case MachineState_TeleportingIn:
14423 case MachineState_Paused:
14424 case MachineState_Running:
14425 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14426 * elsewhere... */
14427 alock.release();
14428 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14429 default: break;
14430 }
14431#else
14432 NOREF(aDevice);
14433 NOREF(aMaskedIfs);
14434#endif
14435 return false;
14436}
14437
14438/**
14439 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14440 */
14441HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14442 IVirtualBoxErrorInfo *aError,
14443 ULONG aMaskedIfs,
14444 const com::Utf8Str &aCaptureFilename)
14445{
14446 LogFlowThisFunc(("\n"));
14447
14448 AutoCaller autoCaller(this);
14449
14450 /* This notification may happen after the machine object has been
14451 * uninitialized (the session was closed), so don't assert. */
14452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14453
14454 ComPtr<IInternalSessionControl> directControl;
14455 {
14456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14457 if (mData->mSession.mLockType == LockType_VM)
14458 directControl = mData->mSession.mDirectControl;
14459 }
14460
14461 /* fail on notifications sent after #OnSessionEnd() is called, it is
14462 * expected by the caller */
14463 if (!directControl)
14464 return E_FAIL;
14465
14466 /* No locks should be held at this point. */
14467 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14468 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14469
14470 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14471}
14472
14473/**
14474 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14475 */
14476HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14477 IVirtualBoxErrorInfo *aError)
14478{
14479 LogFlowThisFunc(("\n"));
14480
14481 AutoCaller autoCaller(this);
14482
14483 /* This notification may happen after the machine object has been
14484 * uninitialized (the session was closed), so don't assert. */
14485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14486
14487 ComPtr<IInternalSessionControl> directControl;
14488 {
14489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14490 if (mData->mSession.mLockType == LockType_VM)
14491 directControl = mData->mSession.mDirectControl;
14492 }
14493
14494 /* fail on notifications sent after #OnSessionEnd() is called, it is
14495 * expected by the caller */
14496 if (!directControl)
14497 return E_FAIL;
14498
14499 /* No locks should be held at this point. */
14500 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14501 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14502
14503 return directControl->OnUSBDeviceDetach(aId, aError);
14504}
14505
14506// protected methods
14507/////////////////////////////////////////////////////////////////////////////
14508
14509/**
14510 * Deletes the given file if it is no longer in use by either the current machine state
14511 * (if the machine is "saved") or any of the machine's snapshots.
14512 *
14513 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14514 * but is different for each SnapshotMachine. When calling this, the order of calling this
14515 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14516 * is therefore critical. I know, it's all rather messy.
14517 *
14518 * @param strStateFile
14519 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14520 * the test for whether the saved state file is in use.
14521 */
14522void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14523 Snapshot *pSnapshotToIgnore)
14524{
14525 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14526 if ( (strStateFile.isNotEmpty())
14527 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14528 )
14529 // ... and it must also not be shared with other snapshots
14530 if ( !mData->mFirstSnapshot
14531 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14532 // this checks the SnapshotMachine's state file paths
14533 )
14534 RTFileDelete(strStateFile.c_str());
14535}
14536
14537/**
14538 * Locks the attached media.
14539 *
14540 * All attached hard disks are locked for writing and DVD/floppy are locked for
14541 * reading. Parents of attached hard disks (if any) are locked for reading.
14542 *
14543 * This method also performs accessibility check of all media it locks: if some
14544 * media is inaccessible, the method will return a failure and a bunch of
14545 * extended error info objects per each inaccessible medium.
14546 *
14547 * Note that this method is atomic: if it returns a success, all media are
14548 * locked as described above; on failure no media is locked at all (all
14549 * succeeded individual locks will be undone).
14550 *
14551 * The caller is responsible for doing the necessary state sanity checks.
14552 *
14553 * The locks made by this method must be undone by calling #unlockMedia() when
14554 * no more needed.
14555 */
14556HRESULT SessionMachine::i_lockMedia()
14557{
14558 AutoCaller autoCaller(this);
14559 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14560
14561 AutoMultiWriteLock2 alock(this->lockHandle(),
14562 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14563
14564 /* bail out if trying to lock things with already set up locking */
14565 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14566
14567 MultiResult mrc(S_OK);
14568
14569 /* Collect locking information for all medium objects attached to the VM. */
14570 for (MediumAttachmentList::const_iterator
14571 it = mMediumAttachments->begin();
14572 it != mMediumAttachments->end();
14573 ++it)
14574 {
14575 MediumAttachment *pAtt = *it;
14576 DeviceType_T devType = pAtt->i_getType();
14577 Medium *pMedium = pAtt->i_getMedium();
14578
14579 MediumLockList *pMediumLockList(new MediumLockList());
14580 // There can be attachments without a medium (floppy/dvd), and thus
14581 // it's impossible to create a medium lock list. It still makes sense
14582 // to have the empty medium lock list in the map in case a medium is
14583 // attached later.
14584 if (pMedium != NULL)
14585 {
14586 MediumType_T mediumType = pMedium->i_getType();
14587 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14588 || mediumType == MediumType_Shareable;
14589 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14590
14591 alock.release();
14592 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14593 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14594 false /* fMediumLockWriteAll */,
14595 NULL,
14596 *pMediumLockList);
14597 alock.acquire();
14598 if (FAILED(mrc))
14599 {
14600 delete pMediumLockList;
14601 mData->mSession.mLockedMedia.Clear();
14602 break;
14603 }
14604 }
14605
14606 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14607 if (FAILED(rc))
14608 {
14609 mData->mSession.mLockedMedia.Clear();
14610 mrc = setError(rc,
14611 tr("Collecting locking information for all attached media failed"));
14612 break;
14613 }
14614 }
14615
14616 if (SUCCEEDED(mrc))
14617 {
14618 /* Now lock all media. If this fails, nothing is locked. */
14619 alock.release();
14620 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14621 alock.acquire();
14622 if (FAILED(rc))
14623 {
14624 mrc = setError(rc,
14625 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14626 }
14627 }
14628
14629 return mrc;
14630}
14631
14632/**
14633 * Undoes the locks made by by #lockMedia().
14634 */
14635HRESULT SessionMachine::i_unlockMedia()
14636{
14637 AutoCaller autoCaller(this);
14638 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14639
14640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14641
14642 /* we may be holding important error info on the current thread;
14643 * preserve it */
14644 ErrorInfoKeeper eik;
14645
14646 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14647 AssertComRC(rc);
14648 return rc;
14649}
14650
14651/**
14652 * Helper to change the machine state (reimplementation).
14653 *
14654 * @note Locks this object for writing.
14655 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14656 * it can cause crashes in random places due to unexpectedly committing
14657 * the current settings. The caller is responsible for that. The call
14658 * to saveStateSettings is fine, because this method does not commit.
14659 */
14660HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14661{
14662 LogFlowThisFuncEnter();
14663 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14664
14665 AutoCaller autoCaller(this);
14666 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14667
14668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14669
14670 MachineState_T oldMachineState = mData->mMachineState;
14671
14672 AssertMsgReturn(oldMachineState != aMachineState,
14673 ("oldMachineState=%s, aMachineState=%s\n",
14674 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14675 E_FAIL);
14676
14677 HRESULT rc = S_OK;
14678
14679 int stsFlags = 0;
14680 bool deleteSavedState = false;
14681
14682 /* detect some state transitions */
14683
14684 if ( ( oldMachineState == MachineState_Saved
14685 && aMachineState == MachineState_Restoring)
14686 || ( ( oldMachineState == MachineState_PoweredOff
14687 || oldMachineState == MachineState_Teleported
14688 || oldMachineState == MachineState_Aborted
14689 )
14690 && ( aMachineState == MachineState_TeleportingIn
14691 || aMachineState == MachineState_Starting
14692 )
14693 )
14694 )
14695 {
14696 /* The EMT thread is about to start */
14697
14698 /* Nothing to do here for now... */
14699
14700 /// @todo NEWMEDIA don't let mDVDDrive and other children
14701 /// change anything when in the Starting/Restoring state
14702 }
14703 else if ( ( oldMachineState == MachineState_Running
14704 || oldMachineState == MachineState_Paused
14705 || oldMachineState == MachineState_Teleporting
14706 || oldMachineState == MachineState_OnlineSnapshotting
14707 || oldMachineState == MachineState_LiveSnapshotting
14708 || oldMachineState == MachineState_Stuck
14709 || oldMachineState == MachineState_Starting
14710 || oldMachineState == MachineState_Stopping
14711 || oldMachineState == MachineState_Saving
14712 || oldMachineState == MachineState_Restoring
14713 || oldMachineState == MachineState_TeleportingPausedVM
14714 || oldMachineState == MachineState_TeleportingIn
14715 )
14716 && ( aMachineState == MachineState_PoweredOff
14717 || aMachineState == MachineState_Saved
14718 || aMachineState == MachineState_Teleported
14719 || aMachineState == MachineState_Aborted
14720 )
14721 )
14722 {
14723 /* The EMT thread has just stopped, unlock attached media. Note that as
14724 * opposed to locking that is done from Console, we do unlocking here
14725 * because the VM process may have aborted before having a chance to
14726 * properly unlock all media it locked. */
14727
14728 unlockMedia();
14729 }
14730
14731 if (oldMachineState == MachineState_Restoring)
14732 {
14733 if (aMachineState != MachineState_Saved)
14734 {
14735 /*
14736 * delete the saved state file once the machine has finished
14737 * restoring from it (note that Console sets the state from
14738 * Restoring to Saved if the VM couldn't restore successfully,
14739 * to give the user an ability to fix an error and retry --
14740 * we keep the saved state file in this case)
14741 */
14742 deleteSavedState = true;
14743 }
14744 }
14745 else if ( oldMachineState == MachineState_Saved
14746 && ( aMachineState == MachineState_PoweredOff
14747 || aMachineState == MachineState_Aborted
14748 || aMachineState == MachineState_Teleported
14749 )
14750 )
14751 {
14752 /*
14753 * delete the saved state after SessionMachine::ForgetSavedState() is called
14754 * or if the VM process (owning a direct VM session) crashed while the
14755 * VM was Saved
14756 */
14757
14758 /// @todo (dmik)
14759 // Not sure that deleting the saved state file just because of the
14760 // client death before it attempted to restore the VM is a good
14761 // thing. But when it crashes we need to go to the Aborted state
14762 // which cannot have the saved state file associated... The only
14763 // way to fix this is to make the Aborted condition not a VM state
14764 // but a bool flag: i.e., when a crash occurs, set it to true and
14765 // change the state to PoweredOff or Saved depending on the
14766 // saved state presence.
14767
14768 deleteSavedState = true;
14769 mData->mCurrentStateModified = TRUE;
14770 stsFlags |= SaveSTS_CurStateModified;
14771 }
14772
14773 if ( aMachineState == MachineState_Starting
14774 || aMachineState == MachineState_Restoring
14775 || aMachineState == MachineState_TeleportingIn
14776 )
14777 {
14778 /* set the current state modified flag to indicate that the current
14779 * state is no more identical to the state in the
14780 * current snapshot */
14781 if (!mData->mCurrentSnapshot.isNull())
14782 {
14783 mData->mCurrentStateModified = TRUE;
14784 stsFlags |= SaveSTS_CurStateModified;
14785 }
14786 }
14787
14788 if (deleteSavedState)
14789 {
14790 if (mRemoveSavedState)
14791 {
14792 Assert(!mSSData->strStateFilePath.isEmpty());
14793
14794 // it is safe to delete the saved state file if ...
14795 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14796 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14797 // ... none of the snapshots share the saved state file
14798 )
14799 RTFileDelete(mSSData->strStateFilePath.c_str());
14800 }
14801
14802 mSSData->strStateFilePath.setNull();
14803 stsFlags |= SaveSTS_StateFilePath;
14804 }
14805
14806 /* redirect to the underlying peer machine */
14807 mPeer->i_setMachineState(aMachineState);
14808
14809 if ( oldMachineState != MachineState_RestoringSnapshot
14810 && ( aMachineState == MachineState_PoweredOff
14811 || aMachineState == MachineState_Teleported
14812 || aMachineState == MachineState_Aborted
14813 || aMachineState == MachineState_Saved))
14814 {
14815 /* the machine has stopped execution
14816 * (or the saved state file was adopted) */
14817 stsFlags |= SaveSTS_StateTimeStamp;
14818 }
14819
14820 if ( ( oldMachineState == MachineState_PoweredOff
14821 || oldMachineState == MachineState_Aborted
14822 || oldMachineState == MachineState_Teleported
14823 )
14824 && aMachineState == MachineState_Saved)
14825 {
14826 /* the saved state file was adopted */
14827 Assert(!mSSData->strStateFilePath.isEmpty());
14828 stsFlags |= SaveSTS_StateFilePath;
14829 }
14830
14831#ifdef VBOX_WITH_GUEST_PROPS
14832 if ( aMachineState == MachineState_PoweredOff
14833 || aMachineState == MachineState_Aborted
14834 || aMachineState == MachineState_Teleported)
14835 {
14836 /* Make sure any transient guest properties get removed from the
14837 * property store on shutdown. */
14838 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14839
14840 /* remove it from the settings representation */
14841 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14842 for (settings::GuestPropertiesList::iterator
14843 it = llGuestProperties.begin();
14844 it != llGuestProperties.end();
14845 /*nothing*/)
14846 {
14847 const settings::GuestProperty &prop = *it;
14848 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14849 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14850 {
14851 it = llGuestProperties.erase(it);
14852 fNeedsSaving = true;
14853 }
14854 else
14855 {
14856 ++it;
14857 }
14858 }
14859
14860 /* Additionally remove it from the HWData representation. Required to
14861 * keep everything in sync, as this is what the API keeps using. */
14862 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14863 for (HWData::GuestPropertyMap::iterator
14864 it = llHWGuestProperties.begin();
14865 it != llHWGuestProperties.end();
14866 /*nothing*/)
14867 {
14868 uint32_t fFlags = it->second.mFlags;
14869 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14870 {
14871 /* iterator where we need to continue after the erase call
14872 * (C++03 is a fact still, and it doesn't return the iterator
14873 * which would allow continuing) */
14874 HWData::GuestPropertyMap::iterator it2 = it;
14875 ++it2;
14876 llHWGuestProperties.erase(it);
14877 it = it2;
14878 fNeedsSaving = true;
14879 }
14880 else
14881 {
14882 ++it;
14883 }
14884 }
14885
14886 if (fNeedsSaving)
14887 {
14888 mData->mCurrentStateModified = TRUE;
14889 stsFlags |= SaveSTS_CurStateModified;
14890 }
14891 }
14892#endif /* VBOX_WITH_GUEST_PROPS */
14893
14894 rc = i_saveStateSettings(stsFlags);
14895
14896 if ( ( oldMachineState != MachineState_PoweredOff
14897 && oldMachineState != MachineState_Aborted
14898 && oldMachineState != MachineState_Teleported
14899 )
14900 && ( aMachineState == MachineState_PoweredOff
14901 || aMachineState == MachineState_Aborted
14902 || aMachineState == MachineState_Teleported
14903 )
14904 )
14905 {
14906 /* we've been shut down for any reason */
14907 /* no special action so far */
14908 }
14909
14910 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14911 LogFlowThisFuncLeave();
14912 return rc;
14913}
14914
14915/**
14916 * Sends the current machine state value to the VM process.
14917 *
14918 * @note Locks this object for reading, then calls a client process.
14919 */
14920HRESULT SessionMachine::i_updateMachineStateOnClient()
14921{
14922 AutoCaller autoCaller(this);
14923 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14924
14925 ComPtr<IInternalSessionControl> directControl;
14926 {
14927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14928 AssertReturn(!!mData, E_FAIL);
14929 if (mData->mSession.mLockType == LockType_VM)
14930 directControl = mData->mSession.mDirectControl;
14931
14932 /* directControl may be already set to NULL here in #OnSessionEnd()
14933 * called too early by the direct session process while there is still
14934 * some operation (like deleting the snapshot) in progress. The client
14935 * process in this case is waiting inside Session::close() for the
14936 * "end session" process object to complete, while #uninit() called by
14937 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14938 * operation to complete. For now, we accept this inconsistent behavior
14939 * and simply do nothing here. */
14940
14941 if (mData->mSession.mState == SessionState_Unlocking)
14942 return S_OK;
14943 }
14944
14945 /* ignore notifications sent after #OnSessionEnd() is called */
14946 if (!directControl)
14947 return S_OK;
14948
14949 return directControl->UpdateMachineState(mData->mMachineState);
14950}
14951
14952
14953/*static*/
14954HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14955{
14956 va_list args;
14957 va_start(args, pcszMsg);
14958 HRESULT rc = setErrorInternal(aResultCode,
14959 getStaticClassIID(),
14960 getStaticComponentName(),
14961 Utf8Str(pcszMsg, args),
14962 false /* aWarning */,
14963 true /* aLogIt */);
14964 va_end(args);
14965 return rc;
14966}
14967
14968
14969HRESULT Machine::updateState(MachineState_T aState)
14970{
14971 NOREF(aState);
14972 ReturnComNotImplemented();
14973}
14974
14975HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14976{
14977 NOREF(aProgress);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::endPowerUp(LONG aResult)
14982{
14983 NOREF(aResult);
14984 ReturnComNotImplemented();
14985}
14986
14987HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14988{
14989 NOREF(aProgress);
14990 ReturnComNotImplemented();
14991}
14992
14993HRESULT Machine::endPoweringDown(LONG aResult,
14994 const com::Utf8Str &aErrMsg)
14995{
14996 NOREF(aResult);
14997 NOREF(aErrMsg);
14998 ReturnComNotImplemented();
14999}
15000
15001HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15002 BOOL *aMatched,
15003 ULONG *aMaskedInterfaces)
15004{
15005 NOREF(aDevice);
15006 NOREF(aMatched);
15007 NOREF(aMaskedInterfaces);
15008 ReturnComNotImplemented();
15009
15010}
15011
15012HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15013{
15014 NOREF(aId); NOREF(aCaptureFilename);
15015 ReturnComNotImplemented();
15016}
15017
15018HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15019 BOOL aDone)
15020{
15021 NOREF(aId);
15022 NOREF(aDone);
15023 ReturnComNotImplemented();
15024}
15025
15026HRESULT Machine::autoCaptureUSBDevices()
15027{
15028 ReturnComNotImplemented();
15029}
15030
15031HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15032{
15033 NOREF(aDone);
15034 ReturnComNotImplemented();
15035}
15036
15037HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15038 ComPtr<IProgress> &aProgress)
15039{
15040 NOREF(aSession);
15041 NOREF(aProgress);
15042 ReturnComNotImplemented();
15043}
15044
15045HRESULT Machine::finishOnlineMergeMedium()
15046{
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
15051{
15052 RT_NOREF(aParms, aID);
15053 ReturnComNotImplemented();
15054}
15055
15056HRESULT Machine::clipboardAreaUnregister(ULONG aID)
15057{
15058 RT_NOREF(aID);
15059 ReturnComNotImplemented();
15060}
15061
15062HRESULT Machine::clipboardAreaAttach(ULONG aID)
15063{
15064 RT_NOREF(aID);
15065 ReturnComNotImplemented();
15066}
15067HRESULT Machine::clipboardAreaDetach(ULONG aID)
15068{
15069 RT_NOREF(aID);
15070 ReturnComNotImplemented();
15071}
15072
15073HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
15074{
15075 RT_NOREF(aID);
15076 ReturnComNotImplemented();
15077}
15078
15079HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
15080{
15081 RT_NOREF(aID, aRefCount);
15082 ReturnComNotImplemented();
15083}
15084
15085HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15086 std::vector<com::Utf8Str> &aValues,
15087 std::vector<LONG64> &aTimestamps,
15088 std::vector<com::Utf8Str> &aFlags)
15089{
15090 NOREF(aNames);
15091 NOREF(aValues);
15092 NOREF(aTimestamps);
15093 NOREF(aFlags);
15094 ReturnComNotImplemented();
15095}
15096
15097HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15098 const com::Utf8Str &aValue,
15099 LONG64 aTimestamp,
15100 const com::Utf8Str &aFlags)
15101{
15102 NOREF(aName);
15103 NOREF(aValue);
15104 NOREF(aTimestamp);
15105 NOREF(aFlags);
15106 ReturnComNotImplemented();
15107}
15108
15109HRESULT Machine::lockMedia()
15110{
15111 ReturnComNotImplemented();
15112}
15113
15114HRESULT Machine::unlockMedia()
15115{
15116 ReturnComNotImplemented();
15117}
15118
15119HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15120 ComPtr<IMediumAttachment> &aNewAttachment)
15121{
15122 NOREF(aAttachment);
15123 NOREF(aNewAttachment);
15124 ReturnComNotImplemented();
15125}
15126
15127HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15128 ULONG aCpuUser,
15129 ULONG aCpuKernel,
15130 ULONG aCpuIdle,
15131 ULONG aMemTotal,
15132 ULONG aMemFree,
15133 ULONG aMemBalloon,
15134 ULONG aMemShared,
15135 ULONG aMemCache,
15136 ULONG aPagedTotal,
15137 ULONG aMemAllocTotal,
15138 ULONG aMemFreeTotal,
15139 ULONG aMemBalloonTotal,
15140 ULONG aMemSharedTotal,
15141 ULONG aVmNetRx,
15142 ULONG aVmNetTx)
15143{
15144 NOREF(aValidStats);
15145 NOREF(aCpuUser);
15146 NOREF(aCpuKernel);
15147 NOREF(aCpuIdle);
15148 NOREF(aMemTotal);
15149 NOREF(aMemFree);
15150 NOREF(aMemBalloon);
15151 NOREF(aMemShared);
15152 NOREF(aMemCache);
15153 NOREF(aPagedTotal);
15154 NOREF(aMemAllocTotal);
15155 NOREF(aMemFreeTotal);
15156 NOREF(aMemBalloonTotal);
15157 NOREF(aMemSharedTotal);
15158 NOREF(aVmNetRx);
15159 NOREF(aVmNetTx);
15160 ReturnComNotImplemented();
15161}
15162
15163HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15164 com::Utf8Str &aResult)
15165{
15166 NOREF(aAuthParams);
15167 NOREF(aResult);
15168 ReturnComNotImplemented();
15169}
15170
15171com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15172{
15173 com::Utf8Str strControllerName = "Unknown";
15174 switch (aBusType)
15175 {
15176 case StorageBus_IDE:
15177 {
15178 strControllerName = "IDE";
15179 break;
15180 }
15181 case StorageBus_SATA:
15182 {
15183 strControllerName = "SATA";
15184 break;
15185 }
15186 case StorageBus_SCSI:
15187 {
15188 strControllerName = "SCSI";
15189 break;
15190 }
15191 case StorageBus_Floppy:
15192 {
15193 strControllerName = "Floppy";
15194 break;
15195 }
15196 case StorageBus_SAS:
15197 {
15198 strControllerName = "SAS";
15199 break;
15200 }
15201 case StorageBus_USB:
15202 {
15203 strControllerName = "USB";
15204 break;
15205 }
15206 default:
15207 break;
15208 }
15209 return strControllerName;
15210}
15211
15212HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15213{
15214 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15215
15216 AutoCaller autoCaller(this);
15217 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15218
15219 HRESULT rc = S_OK;
15220
15221 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15222 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15223 rc = getUSBDeviceFilters(usbDeviceFilters);
15224 if (FAILED(rc)) return rc;
15225
15226 NOREF(aFlags);
15227 com::Utf8Str osTypeId;
15228 ComObjPtr<GuestOSType> osType = NULL;
15229
15230 /* Get the guest os type as a string from the VB. */
15231 rc = getOSTypeId(osTypeId);
15232 if (FAILED(rc)) return rc;
15233
15234 /* Get the os type obj that coresponds, can be used to get
15235 * the defaults for this guest OS. */
15236 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15237 if (FAILED(rc)) return rc;
15238
15239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15240
15241 /* Let the OS type select 64-bit ness. */
15242 mHWData->mLongMode = osType->i_is64Bit()
15243 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15244
15245 /* Let the OS type enable the X2APIC */
15246 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15247
15248 /* This one covers IOAPICEnabled. */
15249 mBIOSSettings->i_applyDefaults(osType);
15250
15251 /* Initialize default record settings. */
15252 mRecordingSettings->i_applyDefaults();
15253
15254 /* Initialize default BIOS settings here */
15255 /* Hardware virtualization must be ON by default */
15256 mHWData->mAPIC = true;
15257 mHWData->mHWVirtExEnabled = true;
15258
15259 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15260 if (FAILED(rc)) return rc;
15261
15262 /* Graphics stuff. */
15263 GraphicsControllerType_T graphicsController;
15264 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15265 if (FAILED(rc)) return rc;
15266
15267 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15268 if (FAILED(rc)) return rc;
15269
15270 ULONG vramSize;
15271 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15275 if (FAILED(rc)) return rc;
15276
15277 BOOL fAccelerate2DVideoEnabled;
15278 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15279 if (FAILED(rc)) return rc;
15280
15281 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15282 if (FAILED(rc)) return rc;
15283
15284 BOOL fAccelerate3DEnabled;
15285 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15286 if (FAILED(rc)) return rc;
15287
15288 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15289 if (FAILED(rc)) return rc;
15290
15291 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15292 if (FAILED(rc)) return rc;
15293
15294 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15295 if (FAILED(rc)) return rc;
15296
15297 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15298 if (FAILED(rc)) return rc;
15299
15300 BOOL mRTCUseUTC;
15301 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15302 if (FAILED(rc)) return rc;
15303
15304 setRTCUseUTC(mRTCUseUTC);
15305 if (FAILED(rc)) return rc;
15306
15307 /* the setter does more than just the assignment, so use it */
15308 ChipsetType_T enmChipsetType;
15309 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15310 if (FAILED(rc)) return rc;
15311
15312 rc = COMSETTER(ChipsetType)(enmChipsetType);
15313 if (FAILED(rc)) return rc;
15314
15315 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15316 if (FAILED(rc)) return rc;
15317
15318 /* Apply network adapters defaults */
15319 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15320 mNetworkAdapters[slot]->i_applyDefaults(osType);
15321
15322 /* Apply serial port defaults */
15323 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15324 mSerialPorts[slot]->i_applyDefaults(osType);
15325
15326 /* Apply parallel port defaults - not OS dependent*/
15327 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15328 mParallelPorts[slot]->i_applyDefaults();
15329
15330 /* Audio stuff. */
15331 AudioControllerType_T audioController;
15332 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15333 if (FAILED(rc)) return rc;
15334
15335 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15336 if (FAILED(rc)) return rc;
15337
15338 AudioCodecType_T audioCodec;
15339 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15340 if (FAILED(rc)) return rc;
15341
15342 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15343 if (FAILED(rc)) return rc;
15344
15345 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15346 if (FAILED(rc)) return rc;
15347
15348 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15349 if (FAILED(rc)) return rc;
15350
15351 /* Storage Controllers */
15352 StorageControllerType_T hdStorageControllerType;
15353 StorageBus_T hdStorageBusType;
15354 StorageControllerType_T dvdStorageControllerType;
15355 StorageBus_T dvdStorageBusType;
15356 BOOL recommendedFloppy;
15357 ComPtr<IStorageController> floppyController;
15358 ComPtr<IStorageController> hdController;
15359 ComPtr<IStorageController> dvdController;
15360 Utf8Str strFloppyName, strDVDName, strHDName;
15361
15362 /* GUI auto generates controller names using bus type. Do the same*/
15363 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15364
15365 /* Floppy recommended? add one. */
15366 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15367 if (FAILED(rc)) return rc;
15368 if (recommendedFloppy)
15369 {
15370 rc = addStorageController(strFloppyName,
15371 StorageBus_Floppy,
15372 floppyController);
15373 if (FAILED(rc)) return rc;
15374 }
15375
15376 /* Setup one DVD storage controller. */
15377 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15378 if (FAILED(rc)) return rc;
15379
15380 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15381 if (FAILED(rc)) return rc;
15382
15383 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15384
15385 rc = addStorageController(strDVDName,
15386 dvdStorageBusType,
15387 dvdController);
15388 if (FAILED(rc)) return rc;
15389
15390 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15391 if (FAILED(rc)) return rc;
15392
15393 /* Setup one HDD storage controller. */
15394 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15395 if (FAILED(rc)) return rc;
15396
15397 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15398 if (FAILED(rc)) return rc;
15399
15400 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15401
15402 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15403 {
15404 rc = addStorageController(strHDName,
15405 hdStorageBusType,
15406 hdController);
15407 if (FAILED(rc)) return rc;
15408
15409 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15410 if (FAILED(rc)) return rc;
15411 }
15412 else
15413 {
15414 /* The HD controller is the same as DVD: */
15415 hdController = dvdController;
15416 }
15417
15418 /* Limit the AHCI port count if it's used because windows has trouble with
15419 * too many ports and other guest (OS X in particular) may take extra long
15420 * boot: */
15421
15422 // pParent = static_cast<Medium*>(aP)
15423 IStorageController *temp = hdController;
15424 ComObjPtr<StorageController> storageController;
15425 storageController = static_cast<StorageController *>(temp);
15426
15427 // tempHDController = aHDController;
15428 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15429 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15430 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15431 storageController->COMSETTER(PortCount)(1);
15432
15433 /* USB stuff */
15434
15435 bool ohciEnabled = false;
15436
15437 ComPtr<IUSBController> usbController;
15438 BOOL recommendedUSB3;
15439 BOOL recommendedUSB;
15440 BOOL usbProxyAvailable;
15441
15442 getUSBProxyAvailable(&usbProxyAvailable);
15443 if (FAILED(rc)) return rc;
15444
15445 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15446 if (FAILED(rc)) return rc;
15447 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15448 if (FAILED(rc)) return rc;
15449
15450 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15451 {
15452#ifdef VBOX_WITH_EXTPACK
15453 /* USB 3.0 is only available if the proper ExtPack is installed. */
15454 ExtPackManager *aManager = mParent->i_getExtPackManager();
15455 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15456 {
15457 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15458 if (FAILED(rc)) return rc;
15459
15460 /* xHci includes OHCI */
15461 ohciEnabled = true;
15462 }
15463#endif
15464 }
15465 if ( !ohciEnabled
15466 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15467 {
15468 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15469 if (FAILED(rc)) return rc;
15470 ohciEnabled = true;
15471
15472#ifdef VBOX_WITH_EXTPACK
15473 /* USB 2.0 is only available if the proper ExtPack is installed.
15474 * Note. Configuring EHCI here and providing messages about
15475 * the missing extpack isn't exactly clean, but it is a
15476 * necessary evil to patch over legacy compatability issues
15477 * introduced by the new distribution model. */
15478 ExtPackManager *manager = mParent->i_getExtPackManager();
15479 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15480 {
15481 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15482 if (FAILED(rc)) return rc;
15483 }
15484#endif
15485 }
15486
15487 /* Set recommended human interface device types: */
15488 BOOL recommendedUSBHID;
15489 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15490 if (FAILED(rc)) return rc;
15491
15492 if (recommendedUSBHID)
15493 {
15494 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15495 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15496 if (!ohciEnabled && !usbDeviceFilters.isNull())
15497 {
15498 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15499 if (FAILED(rc)) return rc;
15500 }
15501 }
15502
15503 BOOL recommendedUSBTablet;
15504 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15505 if (FAILED(rc)) return rc;
15506
15507 if (recommendedUSBTablet)
15508 {
15509 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15510 if (!ohciEnabled && !usbDeviceFilters.isNull())
15511 {
15512 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15513 if (FAILED(rc)) return rc;
15514 }
15515 }
15516 return S_OK;
15517}
15518
15519/* This isn't handled entirely by the wrapper generator yet. */
15520#ifdef VBOX_WITH_XPCOM
15521NS_DECL_CLASSINFO(SessionMachine)
15522NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15523
15524NS_DECL_CLASSINFO(SnapshotMachine)
15525NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15526#endif
Note: See TracBrowser for help on using the repository browser.

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