VirtualBox

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

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

Main/GraphicsAdapter: Split off a few attributes from Machine interface, which affects quite a few other interfaces.
Frontends/VirtualBox+VBoxManage+VBoxSDL+VBoxShell: Adapt accordingly.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 539.3 KB
Line 
1/* $Id: MachineImpl.cpp 81964 2019-11-18 20:42:02Z 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 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
178 mVRAMSize = 8;
179 mAccelerate3DEnabled = false;
180 mAccelerate2DVideoEnabled = false;
181 mMonitorCount = 1;
182 mHWVirtExEnabled = true;
183 mHWVirtExNestedPagingEnabled = true;
184#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
185 mHWVirtExLargePagesEnabled = true;
186#else
187 /* Not supported on 32 bits hosts. */
188 mHWVirtExLargePagesEnabled = false;
189#endif
190 mHWVirtExVPIDEnabled = true;
191 mHWVirtExUXEnabled = true;
192 mHWVirtExForceEnabled = false;
193 mHWVirtExUseNativeApi = false;
194#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
195 mPAEEnabled = true;
196#else
197 mPAEEnabled = false;
198#endif
199 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
200 mTripleFaultReset = false;
201 mAPIC = true;
202 mX2APIC = false;
203 mIBPBOnVMExit = false;
204 mIBPBOnVMEntry = false;
205 mSpecCtrl = false;
206 mSpecCtrlByHost = false;
207 mL1DFlushOnSched = true;
208 mL1DFlushOnVMEntry = false;
209 mMDSClearOnSched = true;
210 mMDSClearOnVMEntry = false;
211 mNestedHWVirt = false;
212 mHPETEnabled = false;
213 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
214 mCpuIdPortabilityLevel = 0;
215 mCpuProfile = "host";
216
217 /* default boot order: floppy - DVD - HDD */
218 mBootOrder[0] = DeviceType_Floppy;
219 mBootOrder[1] = DeviceType_DVD;
220 mBootOrder[2] = DeviceType_HardDisk;
221 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
222 mBootOrder[i] = DeviceType_Null;
223
224 mClipboardMode = ClipboardMode_Disabled;
225 mClipboardFileTransfersEnabled = FALSE;
226
227 mDnDMode = DnDMode_Disabled;
228
229 mFirmwareType = FirmwareType_BIOS;
230 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
231 mPointingHIDType = PointingHIDType_PS2Mouse;
232 mChipsetType = ChipsetType_PIIX3;
233 mParavirtProvider = ParavirtProvider_Default;
234 mEmulatedUSBCardReaderEnabled = FALSE;
235
236 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
237 mCPUAttached[i] = false;
238
239 mIOCacheEnabled = true;
240 mIOCacheSize = 5; /* 5MB */
241}
242
243Machine::HWData::~HWData()
244{
245}
246
247/////////////////////////////////////////////////////////////////////////////
248// Machine class
249/////////////////////////////////////////////////////////////////////////////
250
251// constructor / destructor
252/////////////////////////////////////////////////////////////////////////////
253
254Machine::Machine() :
255#ifdef VBOX_WITH_RESOURCE_USAGE_API
256 mCollectorGuest(NULL),
257#endif
258 mPeer(NULL),
259 mParent(NULL),
260 mSerialPorts(),
261 mParallelPorts(),
262 uRegistryNeedsSaving(0)
263{}
264
265Machine::~Machine()
266{}
267
268HRESULT Machine::FinalConstruct()
269{
270 LogFlowThisFunc(("\n"));
271 return BaseFinalConstruct();
272}
273
274void Machine::FinalRelease()
275{
276 LogFlowThisFunc(("\n"));
277 uninit();
278 BaseFinalRelease();
279}
280
281/**
282 * Initializes a new machine instance; this init() variant creates a new, empty machine.
283 * This gets called from VirtualBox::CreateMachine().
284 *
285 * @param aParent Associated parent object
286 * @param strConfigFile Local file system path to the VM settings file (can
287 * be relative to the VirtualBox config directory).
288 * @param strName name for the machine
289 * @param llGroups list of groups for the machine
290 * @param strOsType OS Type string (stored as is if aOsType is NULL).
291 * @param aOsType OS Type of this machine or NULL.
292 * @param aId UUID for the new machine.
293 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
294 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
295 * scheme (includes the UUID).
296 *
297 * @return Success indicator. if not S_OK, the machine object is invalid
298 */
299HRESULT Machine::init(VirtualBox *aParent,
300 const Utf8Str &strConfigFile,
301 const Utf8Str &strName,
302 const StringsList &llGroups,
303 const Utf8Str &strOsType,
304 GuestOSType *aOsType,
305 const Guid &aId,
306 bool fForceOverwrite,
307 bool fDirectoryIncludesUUID)
308{
309 LogFlowThisFuncEnter();
310 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
311
312 /* Enclose the state transition NotReady->InInit->Ready */
313 AutoInitSpan autoInitSpan(this);
314 AssertReturn(autoInitSpan.isOk(), E_FAIL);
315
316 HRESULT rc = initImpl(aParent, strConfigFile);
317 if (FAILED(rc)) return rc;
318
319 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
320 if (FAILED(rc)) return rc;
321
322 if (SUCCEEDED(rc))
323 {
324 // create an empty machine config
325 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
326
327 rc = initDataAndChildObjects();
328 }
329
330 if (SUCCEEDED(rc))
331 {
332 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
333 mData->mAccessible = TRUE;
334
335 unconst(mData->mUuid) = aId;
336
337 mUserData->s.strName = strName;
338
339 if (llGroups.size())
340 mUserData->s.llGroups = llGroups;
341
342 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
343 // the "name sync" flag determines whether the machine directory gets renamed along
344 // with the machine file; say so if the settings file name is the same as the
345 // settings file parent directory (machine directory)
346 mUserData->s.fNameSync = i_isInOwnDir();
347
348 // initialize the default snapshots folder
349 rc = COMSETTER(SnapshotFolder)(NULL);
350 AssertComRC(rc);
351
352 if (aOsType)
353 {
354 /* Store OS type */
355 mUserData->s.strOsType = aOsType->i_id();
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->i_is64Bit()
359 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
360
361 /* Let the OS type enable the X2APIC */
362 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
363 }
364 else if (!strOsType.isEmpty())
365 {
366 /* Store OS type */
367 mUserData->s.strOsType = strOsType;
368
369 /* No guest OS type object. Pick some plausible defaults which the
370 * host can handle. There's no way to know or validate anything. */
371 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
372 mHWData->mX2APIC = false;
373 }
374
375 /* Apply BIOS defaults. */
376 mBIOSSettings->i_applyDefaults(aOsType);
377
378 /* Apply record defaults. */
379 mRecordingSettings->i_applyDefaults();
380
381 /* Apply network adapters defaults */
382 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
383 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
384
385 /* Apply serial port defaults */
386 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
387 mSerialPorts[slot]->i_applyDefaults(aOsType);
388
389 /* Apply parallel port defaults */
390 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
391 mParallelPorts[slot]->i_applyDefaults();
392
393 /* At this point the changing of the current state modification
394 * flag is allowed. */
395 i_allowStateModification();
396
397 /* commit all changes made during the initialization */
398 i_commit();
399 }
400
401 /* Confirm a successful initialization when it's the case */
402 if (SUCCEEDED(rc))
403 {
404 if (mData->mAccessible)
405 autoInitSpan.setSucceeded();
406 else
407 autoInitSpan.setLimited();
408 }
409
410 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
411 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
412 mData->mRegistered,
413 mData->mAccessible,
414 rc));
415
416 LogFlowThisFuncLeave();
417
418 return rc;
419}
420
421/**
422 * Initializes a new instance with data from machine XML (formerly Init_Registered).
423 * Gets called in two modes:
424 *
425 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
426 * UUID is specified and we mark the machine as "registered";
427 *
428 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
429 * and the machine remains unregistered until RegisterMachine() is called.
430 *
431 * @param aParent Associated parent object
432 * @param strConfigFile Local file system path to the VM settings file (can
433 * be relative to the VirtualBox config directory).
434 * @param aId UUID of the machine or NULL (see above).
435 *
436 * @return Success indicator. if not S_OK, the machine object is invalid
437 */
438HRESULT Machine::initFromSettings(VirtualBox *aParent,
439 const Utf8Str &strConfigFile,
440 const Guid *aId)
441{
442 LogFlowThisFuncEnter();
443 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
444
445 /* Enclose the state transition NotReady->InInit->Ready */
446 AutoInitSpan autoInitSpan(this);
447 AssertReturn(autoInitSpan.isOk(), E_FAIL);
448
449 HRESULT rc = initImpl(aParent, strConfigFile);
450 if (FAILED(rc)) return rc;
451
452 if (aId)
453 {
454 // loading a registered VM:
455 unconst(mData->mUuid) = *aId;
456 mData->mRegistered = TRUE;
457 // now load the settings from XML:
458 rc = i_registeredInit();
459 // this calls initDataAndChildObjects() and loadSettings()
460 }
461 else
462 {
463 // opening an unregistered VM (VirtualBox::OpenMachine()):
464 rc = initDataAndChildObjects();
465
466 if (SUCCEEDED(rc))
467 {
468 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
469 mData->mAccessible = TRUE;
470
471 try
472 {
473 // load and parse machine XML; this will throw on XML or logic errors
474 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
475
476 // reject VM UUID duplicates, they can happen if someone
477 // tries to register an already known VM config again
478 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
479 true /* fPermitInaccessible */,
480 false /* aDoSetError */,
481 NULL) != VBOX_E_OBJECT_NOT_FOUND)
482 {
483 throw setError(E_FAIL,
484 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
485 mData->m_strConfigFile.c_str());
486 }
487
488 // use UUID from machine config
489 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
490
491 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
492 NULL /* puuidRegistry */);
493 if (FAILED(rc)) throw rc;
494
495 /* At this point the changing of the current state modification
496 * flag is allowed. */
497 i_allowStateModification();
498
499 i_commit();
500 }
501 catch (HRESULT err)
502 {
503 /* we assume that error info is set by the thrower */
504 rc = err;
505 }
506 catch (...)
507 {
508 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
509 }
510 }
511 }
512
513 /* Confirm a successful initialization when it's the case */
514 if (SUCCEEDED(rc))
515 {
516 if (mData->mAccessible)
517 autoInitSpan.setSucceeded();
518 else
519 {
520 autoInitSpan.setLimited();
521
522 // uninit media from this machine's media registry, or else
523 // reloading the settings will fail
524 mParent->i_unregisterMachineMedia(i_getId());
525 }
526 }
527
528 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
529 "rc=%08X\n",
530 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
531 mData->mRegistered, mData->mAccessible, rc));
532
533 LogFlowThisFuncLeave();
534
535 return rc;
536}
537
538/**
539 * Initializes a new instance from a machine config that is already in memory
540 * (import OVF case). Since we are importing, the UUID in the machine
541 * config is ignored and we always generate a fresh one.
542 *
543 * @param aParent Associated parent object.
544 * @param strName Name for the new machine; this overrides what is specified in config.
545 * @param strSettingsFilename File name of .vbox file.
546 * @param config Machine configuration loaded and parsed from XML.
547 *
548 * @return Success indicator. if not S_OK, the machine object is invalid
549 */
550HRESULT Machine::init(VirtualBox *aParent,
551 const Utf8Str &strName,
552 const Utf8Str &strSettingsFilename,
553 const settings::MachineConfigFile &config)
554{
555 LogFlowThisFuncEnter();
556
557 /* Enclose the state transition NotReady->InInit->Ready */
558 AutoInitSpan autoInitSpan(this);
559 AssertReturn(autoInitSpan.isOk(), E_FAIL);
560
561 HRESULT rc = initImpl(aParent, strSettingsFilename);
562 if (FAILED(rc)) return rc;
563
564 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
565 if (FAILED(rc)) return rc;
566
567 rc = initDataAndChildObjects();
568
569 if (SUCCEEDED(rc))
570 {
571 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
572 mData->mAccessible = TRUE;
573
574 // create empty machine config for instance data
575 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
576
577 // generate fresh UUID, ignore machine config
578 unconst(mData->mUuid).create();
579
580 rc = i_loadMachineDataFromSettings(config,
581 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
582
583 // override VM name as well, it may be different
584 mUserData->s.strName = strName;
585
586 if (SUCCEEDED(rc))
587 {
588 /* At this point the changing of the current state modification
589 * flag is allowed. */
590 i_allowStateModification();
591
592 /* commit all changes made during the initialization */
593 i_commit();
594 }
595 }
596
597 /* Confirm a successful initialization when it's the case */
598 if (SUCCEEDED(rc))
599 {
600 if (mData->mAccessible)
601 autoInitSpan.setSucceeded();
602 else
603 {
604 /* Ignore all errors from unregistering, they would destroy
605- * the more interesting error information we already have,
606- * pinpointing the issue with the VM config. */
607 ErrorInfoKeeper eik;
608
609 autoInitSpan.setLimited();
610
611 // uninit media from this machine's media registry, or else
612 // reloading the settings will fail
613 mParent->i_unregisterMachineMedia(i_getId());
614 }
615 }
616
617 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
618 "rc=%08X\n",
619 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
620 mData->mRegistered, mData->mAccessible, rc));
621
622 LogFlowThisFuncLeave();
623
624 return rc;
625}
626
627/**
628 * Shared code between the various init() implementations.
629 * @param aParent The VirtualBox object.
630 * @param strConfigFile Settings file.
631 * @return
632 */
633HRESULT Machine::initImpl(VirtualBox *aParent,
634 const Utf8Str &strConfigFile)
635{
636 LogFlowThisFuncEnter();
637
638 AssertReturn(aParent, E_INVALIDARG);
639 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
640
641 HRESULT rc = S_OK;
642
643 /* share the parent weakly */
644 unconst(mParent) = aParent;
645
646 /* allocate the essential machine data structure (the rest will be
647 * allocated later by initDataAndChildObjects() */
648 mData.allocate();
649
650 /* memorize the config file name (as provided) */
651 mData->m_strConfigFile = strConfigFile;
652
653 /* get the full file name */
654 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
655 if (RT_FAILURE(vrc1))
656 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
657 tr("Invalid machine settings file name '%s' (%Rrc)"),
658 strConfigFile.c_str(),
659 vrc1);
660
661 LogFlowThisFuncLeave();
662
663 return rc;
664}
665
666/**
667 * Tries to create a machine settings file in the path stored in the machine
668 * instance data. Used when a new machine is created to fail gracefully if
669 * the settings file could not be written (e.g. because machine dir is read-only).
670 * @return
671 */
672HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
673{
674 HRESULT rc = S_OK;
675
676 // when we create a new machine, we must be able to create the settings file
677 RTFILE f = NIL_RTFILE;
678 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
679 if ( RT_SUCCESS(vrc)
680 || vrc == VERR_SHARING_VIOLATION
681 )
682 {
683 if (RT_SUCCESS(vrc))
684 RTFileClose(f);
685 if (!fForceOverwrite)
686 rc = setError(VBOX_E_FILE_ERROR,
687 tr("Machine settings file '%s' already exists"),
688 mData->m_strConfigFileFull.c_str());
689 else
690 {
691 /* try to delete the config file, as otherwise the creation
692 * of a new settings file will fail. */
693 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
694 if (RT_FAILURE(vrc2))
695 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
696 tr("Could not delete the existing settings file '%s' (%Rrc)"),
697 mData->m_strConfigFileFull.c_str(), vrc2);
698 }
699 }
700 else if ( vrc != VERR_FILE_NOT_FOUND
701 && vrc != VERR_PATH_NOT_FOUND
702 )
703 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
704 tr("Invalid machine settings file name '%s' (%Rrc)"),
705 mData->m_strConfigFileFull.c_str(),
706 vrc);
707 return rc;
708}
709
710/**
711 * Initializes the registered machine by loading the settings file.
712 * This method is separated from #init() in order to make it possible to
713 * retry the operation after VirtualBox startup instead of refusing to
714 * startup the whole VirtualBox server in case if the settings file of some
715 * registered VM is invalid or inaccessible.
716 *
717 * @note Must be always called from this object's write lock
718 * (unless called from #init() that doesn't need any locking).
719 * @note Locks the mUSBController method for writing.
720 * @note Subclasses must not call this method.
721 */
722HRESULT Machine::i_registeredInit()
723{
724 AssertReturn(!i_isSessionMachine(), E_FAIL);
725 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
726 AssertReturn(mData->mUuid.isValid(), E_FAIL);
727 AssertReturn(!mData->mAccessible, E_FAIL);
728
729 HRESULT rc = initDataAndChildObjects();
730
731 if (SUCCEEDED(rc))
732 {
733 /* Temporarily reset the registered flag in order to let setters
734 * potentially called from loadSettings() succeed (isMutable() used in
735 * all setters will return FALSE for a Machine instance if mRegistered
736 * is TRUE). */
737 mData->mRegistered = FALSE;
738
739 try
740 {
741 // load and parse machine XML; this will throw on XML or logic errors
742 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
743
744 if (mData->mUuid != mData->pMachineConfigFile->uuid)
745 throw setError(E_FAIL,
746 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
747 mData->pMachineConfigFile->uuid.raw(),
748 mData->m_strConfigFileFull.c_str(),
749 mData->mUuid.toString().c_str(),
750 mParent->i_settingsFilePath().c_str());
751
752 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
753 NULL /* const Guid *puuidRegistry */);
754 if (FAILED(rc)) throw rc;
755 }
756 catch (HRESULT err)
757 {
758 /* we assume that error info is set by the thrower */
759 rc = err;
760 }
761 catch (...)
762 {
763 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
764 }
765
766 /* Restore the registered flag (even on failure) */
767 mData->mRegistered = TRUE;
768 }
769
770 if (SUCCEEDED(rc))
771 {
772 /* Set mAccessible to TRUE only if we successfully locked and loaded
773 * the settings file */
774 mData->mAccessible = TRUE;
775
776 /* commit all changes made during loading the settings file */
777 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
778 /// @todo r=klaus for some reason the settings loading logic backs up
779 // the settings, and therefore a commit is needed. Should probably be changed.
780 }
781 else
782 {
783 /* If the machine is registered, then, instead of returning a
784 * failure, we mark it as inaccessible and set the result to
785 * success to give it a try later */
786
787 /* fetch the current error info */
788 mData->mAccessError = com::ErrorInfo();
789 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
790
791 /* rollback all changes */
792 i_rollback(false /* aNotify */);
793
794 // uninit media from this machine's media registry, or else
795 // reloading the settings will fail
796 mParent->i_unregisterMachineMedia(i_getId());
797
798 /* uninitialize the common part to make sure all data is reset to
799 * default (null) values */
800 uninitDataAndChildObjects();
801
802 rc = S_OK;
803 }
804
805 return rc;
806}
807
808/**
809 * Uninitializes the instance.
810 * Called either from FinalRelease() or by the parent when it gets destroyed.
811 *
812 * @note The caller of this method must make sure that this object
813 * a) doesn't have active callers on the current thread and b) is not locked
814 * by the current thread; otherwise uninit() will hang either a) due to
815 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
816 * a dead-lock caused by this thread waiting for all callers on the other
817 * threads are done but preventing them from doing so by holding a lock.
818 */
819void Machine::uninit()
820{
821 LogFlowThisFuncEnter();
822
823 Assert(!isWriteLockOnCurrentThread());
824
825 Assert(!uRegistryNeedsSaving);
826 if (uRegistryNeedsSaving)
827 {
828 AutoCaller autoCaller(this);
829 if (SUCCEEDED(autoCaller.rc()))
830 {
831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
832 i_saveSettings(NULL, Machine::SaveS_Force);
833 }
834 }
835
836 /* Enclose the state transition Ready->InUninit->NotReady */
837 AutoUninitSpan autoUninitSpan(this);
838 if (autoUninitSpan.uninitDone())
839 return;
840
841 Assert(!i_isSnapshotMachine());
842 Assert(!i_isSessionMachine());
843 Assert(!!mData);
844
845 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
846 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
847
848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
849
850 if (!mData->mSession.mMachine.isNull())
851 {
852 /* Theoretically, this can only happen if the VirtualBox server has been
853 * terminated while there were clients running that owned open direct
854 * sessions. Since in this case we are definitely called by
855 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
856 * won't happen on the client watcher thread (because it has a
857 * VirtualBox caller for the duration of the
858 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
859 * cannot happen until the VirtualBox caller is released). This is
860 * important, because SessionMachine::uninit() cannot correctly operate
861 * after we return from this method (it expects the Machine instance is
862 * still valid). We'll call it ourselves below.
863 */
864 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
865 (SessionMachine*)mData->mSession.mMachine));
866
867 if (Global::IsOnlineOrTransient(mData->mMachineState))
868 {
869 Log1WarningThisFunc(("Setting state to Aborted!\n"));
870 /* set machine state using SessionMachine reimplementation */
871 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
872 }
873
874 /*
875 * Uninitialize SessionMachine using public uninit() to indicate
876 * an unexpected uninitialization.
877 */
878 mData->mSession.mMachine->uninit();
879 /* SessionMachine::uninit() must set mSession.mMachine to null */
880 Assert(mData->mSession.mMachine.isNull());
881 }
882
883 // uninit media from this machine's media registry, if they're still there
884 Guid uuidMachine(i_getId());
885
886 /* the lock is no more necessary (SessionMachine is uninitialized) */
887 alock.release();
888
889 /* XXX This will fail with
890 * "cannot be closed because it is still attached to 1 virtual machines"
891 * because at this point we did not call uninitDataAndChildObjects() yet
892 * and therefore also removeBackReference() for all these mediums was not called! */
893
894 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
895 mParent->i_unregisterMachineMedia(uuidMachine);
896
897 // has machine been modified?
898 if (mData->flModifications)
899 {
900 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
901 i_rollback(false /* aNotify */);
902 }
903
904 if (mData->mAccessible)
905 uninitDataAndChildObjects();
906
907 /* free the essential data structure last */
908 mData.free();
909
910 LogFlowThisFuncLeave();
911}
912
913// Wrapped IMachine properties
914/////////////////////////////////////////////////////////////////////////////
915HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
916{
917 /* mParent is constant during life time, no need to lock */
918 ComObjPtr<VirtualBox> pVirtualBox(mParent);
919 aParent = pVirtualBox;
920
921 return S_OK;
922}
923
924
925HRESULT Machine::getAccessible(BOOL *aAccessible)
926{
927 /* In some cases (medium registry related), it is necessary to be able to
928 * go through the list of all machines. Happens when an inaccessible VM
929 * has a sensible medium registry. */
930 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
932
933 HRESULT rc = S_OK;
934
935 if (!mData->mAccessible)
936 {
937 /* try to initialize the VM once more if not accessible */
938
939 AutoReinitSpan autoReinitSpan(this);
940 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
941
942#ifdef DEBUG
943 LogFlowThisFunc(("Dumping media backreferences\n"));
944 mParent->i_dumpAllBackRefs();
945#endif
946
947 if (mData->pMachineConfigFile)
948 {
949 // reset the XML file to force loadSettings() (called from i_registeredInit())
950 // to parse it again; the file might have changed
951 delete mData->pMachineConfigFile;
952 mData->pMachineConfigFile = NULL;
953 }
954
955 rc = i_registeredInit();
956
957 if (SUCCEEDED(rc) && mData->mAccessible)
958 {
959 autoReinitSpan.setSucceeded();
960
961 /* make sure interesting parties will notice the accessibility
962 * state change */
963 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
964 mParent->i_onMachineDataChange(mData->mUuid);
965 }
966 }
967
968 if (SUCCEEDED(rc))
969 *aAccessible = mData->mAccessible;
970
971 LogFlowThisFuncLeave();
972
973 return rc;
974}
975
976HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
977{
978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
979
980 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
981 {
982 /* return shortly */
983 aAccessError = NULL;
984 return S_OK;
985 }
986
987 HRESULT rc = S_OK;
988
989 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
990 rc = errorInfo.createObject();
991 if (SUCCEEDED(rc))
992 {
993 errorInfo->init(mData->mAccessError.getResultCode(),
994 mData->mAccessError.getInterfaceID().ref(),
995 Utf8Str(mData->mAccessError.getComponent()).c_str(),
996 Utf8Str(mData->mAccessError.getText()));
997 aAccessError = errorInfo;
998 }
999
1000 return rc;
1001}
1002
1003HRESULT Machine::getName(com::Utf8Str &aName)
1004{
1005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1006
1007 aName = mUserData->s.strName;
1008
1009 return S_OK;
1010}
1011
1012HRESULT Machine::setName(const com::Utf8Str &aName)
1013{
1014 // prohibit setting a UUID only as the machine name, or else it can
1015 // never be found by findMachine()
1016 Guid test(aName);
1017
1018 if (test.isValid())
1019 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1020
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 HRESULT rc = i_checkStateDependency(MutableStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strName = aName;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aDescription = mUserData->s.strDescription;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1043{
1044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1045
1046 // this can be done in principle in any state as it doesn't affect the VM
1047 // significantly, but play safe by not messing around while complex
1048 // activities are going on
1049 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1050 if (FAILED(rc)) return rc;
1051
1052 i_setModified(IsModified_MachineData);
1053 mUserData.backup();
1054 mUserData->s.strDescription = aDescription;
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::getId(com::Guid &aId)
1060{
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 aId = mData->mUuid;
1064
1065 return S_OK;
1066}
1067
1068HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1069{
1070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1071 aGroups.resize(mUserData->s.llGroups.size());
1072 size_t i = 0;
1073 for (StringsList::const_iterator
1074 it = mUserData->s.llGroups.begin();
1075 it != mUserData->s.llGroups.end();
1076 ++it, ++i)
1077 aGroups[i] = (*it);
1078
1079 return S_OK;
1080}
1081
1082HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1083{
1084 StringsList llGroups;
1085 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1086 if (FAILED(rc))
1087 return rc;
1088
1089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1090
1091 rc = i_checkStateDependency(MutableOrSavedStateDep);
1092 if (FAILED(rc)) return rc;
1093
1094 i_setModified(IsModified_MachineData);
1095 mUserData.backup();
1096 mUserData->s.llGroups = llGroups;
1097
1098 return S_OK;
1099}
1100
1101HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1102{
1103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1104
1105 aOSTypeId = mUserData->s.strOsType;
1106
1107 return S_OK;
1108}
1109
1110HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1111{
1112 /* look up the object by Id to check it is valid */
1113 ComObjPtr<GuestOSType> pGuestOSType;
1114 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1115
1116 /* when setting, always use the "etalon" value for consistency -- lookup
1117 * by ID is case-insensitive and the input value may have different case */
1118 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1119
1120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 HRESULT rc = i_checkStateDependency(MutableStateDep);
1123 if (FAILED(rc)) return rc;
1124
1125 i_setModified(IsModified_MachineData);
1126 mUserData.backup();
1127 mUserData->s.strOsType = osTypeId;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1133{
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 *aFirmwareType = mHWData->mFirmwareType;
1137
1138 return S_OK;
1139}
1140
1141HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1142{
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 HRESULT rc = i_checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 i_setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mFirmwareType = aFirmwareType;
1151 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1152 alock.release();
1153
1154 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1155
1156 return S_OK;
1157}
1158
1159HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1160{
1161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1164
1165 return S_OK;
1166}
1167
1168HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1169{
1170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1171
1172 HRESULT rc = i_checkStateDependency(MutableStateDep);
1173 if (FAILED(rc)) return rc;
1174
1175 i_setModified(IsModified_MachineData);
1176 mHWData.backup();
1177 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1178
1179 return S_OK;
1180}
1181
1182HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1183{
1184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1185
1186 *aPointingHIDType = mHWData->mPointingHIDType;
1187
1188 return S_OK;
1189}
1190
1191HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1192{
1193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1194
1195 HRESULT rc = i_checkStateDependency(MutableStateDep);
1196 if (FAILED(rc)) return rc;
1197
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mPointingHIDType = aPointingHIDType;
1201
1202 return S_OK;
1203}
1204
1205HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1206{
1207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 *aChipsetType = mHWData->mChipsetType;
1210
1211 return S_OK;
1212}
1213
1214HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1215{
1216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1217
1218 HRESULT rc = i_checkStateDependency(MutableStateDep);
1219 if (FAILED(rc)) return rc;
1220
1221 if (aChipsetType != mHWData->mChipsetType)
1222 {
1223 i_setModified(IsModified_MachineData);
1224 mHWData.backup();
1225 mHWData->mChipsetType = aChipsetType;
1226
1227 // Resize network adapter array, to be finalized on commit/rollback.
1228 // We must not throw away entries yet, otherwise settings are lost
1229 // without a way to roll back.
1230 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1231 size_t oldCount = mNetworkAdapters.size();
1232 if (newCount > oldCount)
1233 {
1234 mNetworkAdapters.resize(newCount);
1235 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1236 {
1237 unconst(mNetworkAdapters[slot]).createObject();
1238 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1239 }
1240 }
1241 }
1242
1243 return S_OK;
1244}
1245
1246HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1247{
1248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 aParavirtDebug = mHWData->mParavirtDebug;
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1255{
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = i_checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 /** @todo Parse/validate options? */
1262 if (aParavirtDebug != mHWData->mParavirtDebug)
1263 {
1264 i_setModified(IsModified_MachineData);
1265 mHWData.backup();
1266 mHWData->mParavirtDebug = aParavirtDebug;
1267 }
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1273{
1274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 *aParavirtProvider = mHWData->mParavirtProvider;
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1282{
1283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 HRESULT rc = i_checkStateDependency(MutableStateDep);
1286 if (FAILED(rc)) return rc;
1287
1288 if (aParavirtProvider != mHWData->mParavirtProvider)
1289 {
1290 i_setModified(IsModified_MachineData);
1291 mHWData.backup();
1292 mHWData->mParavirtProvider = aParavirtProvider;
1293 }
1294
1295 return S_OK;
1296}
1297
1298HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1299{
1300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1301
1302 *aParavirtProvider = mHWData->mParavirtProvider;
1303 switch (mHWData->mParavirtProvider)
1304 {
1305 case ParavirtProvider_None:
1306 case ParavirtProvider_HyperV:
1307 case ParavirtProvider_KVM:
1308 case ParavirtProvider_Minimal:
1309 break;
1310
1311 /* Resolve dynamic provider types to the effective types. */
1312 default:
1313 {
1314 ComObjPtr<GuestOSType> pGuestOSType;
1315 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1316 pGuestOSType);
1317 if (FAILED(hrc2) || pGuestOSType.isNull())
1318 {
1319 *aParavirtProvider = ParavirtProvider_None;
1320 break;
1321 }
1322
1323 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1324 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1325
1326 switch (mHWData->mParavirtProvider)
1327 {
1328 case ParavirtProvider_Legacy:
1329 {
1330 if (fOsXGuest)
1331 *aParavirtProvider = ParavirtProvider_Minimal;
1332 else
1333 *aParavirtProvider = ParavirtProvider_None;
1334 break;
1335 }
1336
1337 case ParavirtProvider_Default:
1338 {
1339 if (fOsXGuest)
1340 *aParavirtProvider = ParavirtProvider_Minimal;
1341 else if ( mUserData->s.strOsType == "Windows10"
1342 || mUserData->s.strOsType == "Windows10_64"
1343 || mUserData->s.strOsType == "Windows81"
1344 || mUserData->s.strOsType == "Windows81_64"
1345 || mUserData->s.strOsType == "Windows8"
1346 || mUserData->s.strOsType == "Windows8_64"
1347 || mUserData->s.strOsType == "Windows7"
1348 || mUserData->s.strOsType == "Windows7_64"
1349 || mUserData->s.strOsType == "WindowsVista"
1350 || mUserData->s.strOsType == "WindowsVista_64"
1351 || mUserData->s.strOsType == "Windows2012"
1352 || mUserData->s.strOsType == "Windows2012_64"
1353 || mUserData->s.strOsType == "Windows2008"
1354 || mUserData->s.strOsType == "Windows2008_64")
1355 {
1356 *aParavirtProvider = ParavirtProvider_HyperV;
1357 }
1358 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1359 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1360 || mUserData->s.strOsType == "Linux"
1361 || mUserData->s.strOsType == "Linux_64"
1362 || mUserData->s.strOsType == "ArchLinux"
1363 || mUserData->s.strOsType == "ArchLinux_64"
1364 || mUserData->s.strOsType == "Debian"
1365 || mUserData->s.strOsType == "Debian_64"
1366 || mUserData->s.strOsType == "Fedora"
1367 || mUserData->s.strOsType == "Fedora_64"
1368 || mUserData->s.strOsType == "Gentoo"
1369 || mUserData->s.strOsType == "Gentoo_64"
1370 || mUserData->s.strOsType == "Mandriva"
1371 || mUserData->s.strOsType == "Mandriva_64"
1372 || mUserData->s.strOsType == "OpenSUSE"
1373 || mUserData->s.strOsType == "OpenSUSE_64"
1374 || mUserData->s.strOsType == "Oracle"
1375 || mUserData->s.strOsType == "Oracle_64"
1376 || mUserData->s.strOsType == "RedHat"
1377 || mUserData->s.strOsType == "RedHat_64"
1378 || mUserData->s.strOsType == "Turbolinux"
1379 || mUserData->s.strOsType == "Turbolinux_64"
1380 || mUserData->s.strOsType == "Ubuntu"
1381 || mUserData->s.strOsType == "Ubuntu_64"
1382 || mUserData->s.strOsType == "Xandros"
1383 || mUserData->s.strOsType == "Xandros_64")
1384 {
1385 *aParavirtProvider = ParavirtProvider_KVM;
1386 }
1387 else
1388 *aParavirtProvider = ParavirtProvider_None;
1389 break;
1390 }
1391
1392 default: AssertFailedBreak(); /* Shut up MSC. */
1393 }
1394 break;
1395 }
1396 }
1397
1398 Assert( *aParavirtProvider == ParavirtProvider_None
1399 || *aParavirtProvider == ParavirtProvider_Minimal
1400 || *aParavirtProvider == ParavirtProvider_HyperV
1401 || *aParavirtProvider == ParavirtProvider_KVM);
1402 return S_OK;
1403}
1404
1405HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1406{
1407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1408
1409 aHardwareVersion = mHWData->mHWVersion;
1410
1411 return S_OK;
1412}
1413
1414HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1415{
1416 /* check known version */
1417 Utf8Str hwVersion = aHardwareVersion;
1418 if ( hwVersion.compare("1") != 0
1419 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1420 return setError(E_INVALIDARG,
1421 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1422
1423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 HRESULT rc = i_checkStateDependency(MutableStateDep);
1426 if (FAILED(rc)) return rc;
1427
1428 i_setModified(IsModified_MachineData);
1429 mHWData.backup();
1430 mHWData->mHWVersion = aHardwareVersion;
1431
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 if (!mHWData->mHardwareUUID.isZero())
1440 aHardwareUUID = mHWData->mHardwareUUID;
1441 else
1442 aHardwareUUID = mData->mUuid;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1448{
1449 if (!aHardwareUUID.isValid())
1450 return E_INVALIDARG;
1451
1452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 HRESULT rc = i_checkStateDependency(MutableStateDep);
1455 if (FAILED(rc)) return rc;
1456
1457 i_setModified(IsModified_MachineData);
1458 mHWData.backup();
1459 if (aHardwareUUID == mData->mUuid)
1460 mHWData->mHardwareUUID.clear();
1461 else
1462 mHWData->mHardwareUUID = aHardwareUUID;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aMemorySize = mHWData->mMemorySize;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setMemorySize(ULONG aMemorySize)
1477{
1478 /* check RAM limits */
1479 if ( aMemorySize < MM_RAM_MIN_IN_MB
1480 || aMemorySize > MM_RAM_MAX_IN_MB
1481 )
1482 return setError(E_INVALIDARG,
1483 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1484 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1485
1486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 HRESULT rc = i_checkStateDependency(MutableStateDep);
1489 if (FAILED(rc)) return rc;
1490
1491 i_setModified(IsModified_MachineData);
1492 mHWData.backup();
1493 mHWData->mMemorySize = aMemorySize;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1499{
1500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 *aCPUCount = mHWData->mCPUCount;
1503
1504 return S_OK;
1505}
1506
1507HRESULT Machine::setCPUCount(ULONG aCPUCount)
1508{
1509 /* check CPU limits */
1510 if ( aCPUCount < SchemaDefs::MinCPUCount
1511 || aCPUCount > SchemaDefs::MaxCPUCount
1512 )
1513 return setError(E_INVALIDARG,
1514 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1515 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1516
1517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1518
1519 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1520 if (mHWData->mCPUHotPlugEnabled)
1521 {
1522 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1523 {
1524 if (mHWData->mCPUAttached[idx])
1525 return setError(E_INVALIDARG,
1526 tr("There is still a CPU attached to socket %lu."
1527 "Detach the CPU before removing the socket"),
1528 aCPUCount, idx+1);
1529 }
1530 }
1531
1532 HRESULT rc = i_checkStateDependency(MutableStateDep);
1533 if (FAILED(rc)) return rc;
1534
1535 i_setModified(IsModified_MachineData);
1536 mHWData.backup();
1537 mHWData->mCPUCount = aCPUCount;
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1543{
1544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1547
1548 return S_OK;
1549}
1550
1551HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1552{
1553 HRESULT rc = S_OK;
1554
1555 /* check throttle limits */
1556 if ( aCPUExecutionCap < 1
1557 || aCPUExecutionCap > 100
1558 )
1559 return setError(E_INVALIDARG,
1560 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1561 aCPUExecutionCap, 1, 100);
1562
1563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1564
1565 alock.release();
1566 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1567 alock.acquire();
1568 if (FAILED(rc)) return rc;
1569
1570 i_setModified(IsModified_MachineData);
1571 mHWData.backup();
1572 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1573
1574 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1575 if (Global::IsOnline(mData->mMachineState))
1576 i_saveSettings(NULL);
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1582{
1583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1584
1585 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1586
1587 return S_OK;
1588}
1589
1590HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1591{
1592 HRESULT rc = S_OK;
1593
1594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 rc = i_checkStateDependency(MutableStateDep);
1597 if (FAILED(rc)) return rc;
1598
1599 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1600 {
1601 if (aCPUHotPlugEnabled)
1602 {
1603 i_setModified(IsModified_MachineData);
1604 mHWData.backup();
1605
1606 /* Add the amount of CPUs currently attached */
1607 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1608 mHWData->mCPUAttached[i] = true;
1609 }
1610 else
1611 {
1612 /*
1613 * We can disable hotplug only if the amount of maximum CPUs is equal
1614 * to the amount of attached CPUs
1615 */
1616 unsigned cCpusAttached = 0;
1617 unsigned iHighestId = 0;
1618
1619 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1620 {
1621 if (mHWData->mCPUAttached[i])
1622 {
1623 cCpusAttached++;
1624 iHighestId = i;
1625 }
1626 }
1627
1628 if ( (cCpusAttached != mHWData->mCPUCount)
1629 || (iHighestId >= mHWData->mCPUCount))
1630 return setError(E_INVALIDARG,
1631 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1632
1633 i_setModified(IsModified_MachineData);
1634 mHWData.backup();
1635 }
1636 }
1637
1638 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1639
1640 return rc;
1641}
1642
1643HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1644{
1645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1648
1649 return S_OK;
1650}
1651
1652HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1653{
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1657 if (SUCCEEDED(hrc))
1658 {
1659 i_setModified(IsModified_MachineData);
1660 mHWData.backup();
1661 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1662 }
1663 return hrc;
1664}
1665
1666HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1667{
1668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1669 aCPUProfile = mHWData->mCpuProfile;
1670 return S_OK;
1671}
1672
1673HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1674{
1675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1676 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1677 if (SUCCEEDED(hrc))
1678 {
1679 i_setModified(IsModified_MachineData);
1680 mHWData.backup();
1681 /* Empty equals 'host'. */
1682 if (aCPUProfile.isNotEmpty())
1683 mHWData->mCpuProfile = aCPUProfile;
1684 else
1685 mHWData->mCpuProfile = "host";
1686 }
1687 return hrc;
1688}
1689
1690HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1691{
1692#ifdef VBOX_WITH_USB_CARDREADER
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694
1695 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1696
1697 return S_OK;
1698#else
1699 NOREF(aEmulatedUSBCardReaderEnabled);
1700 return E_NOTIMPL;
1701#endif
1702}
1703
1704HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1705{
1706#ifdef VBOX_WITH_USB_CARDREADER
1707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1710 if (FAILED(rc)) return rc;
1711
1712 i_setModified(IsModified_MachineData);
1713 mHWData.backup();
1714 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1715
1716 return S_OK;
1717#else
1718 NOREF(aEmulatedUSBCardReaderEnabled);
1719 return E_NOTIMPL;
1720#endif
1721}
1722
1723HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1724{
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726
1727 *aHPETEnabled = mHWData->mHPETEnabled;
1728
1729 return S_OK;
1730}
1731
1732HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1733{
1734 HRESULT rc = S_OK;
1735
1736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 rc = i_checkStateDependency(MutableStateDep);
1739 if (FAILED(rc)) return rc;
1740
1741 i_setModified(IsModified_MachineData);
1742 mHWData.backup();
1743
1744 mHWData->mHPETEnabled = aHPETEnabled;
1745
1746 return rc;
1747}
1748
1749/** @todo this method should not be public */
1750HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1751{
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1755
1756 return S_OK;
1757}
1758
1759/**
1760 * Set the memory balloon size.
1761 *
1762 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1763 * we have to make sure that we never call IGuest from here.
1764 */
1765HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1766{
1767 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1768#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1769 /* check limits */
1770 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1771 return setError(E_INVALIDARG,
1772 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1773 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1774
1775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1776
1777 i_setModified(IsModified_MachineData);
1778 mHWData.backup();
1779 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1780
1781 return S_OK;
1782#else
1783 NOREF(aMemoryBalloonSize);
1784 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1785#endif
1786}
1787
1788HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1789{
1790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1791
1792 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1793 return S_OK;
1794}
1795
1796HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1797{
1798#ifdef VBOX_WITH_PAGE_SHARING
1799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1800
1801 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1802 i_setModified(IsModified_MachineData);
1803 mHWData.backup();
1804 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1805 return S_OK;
1806#else
1807 NOREF(aPageFusionEnabled);
1808 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1809#endif
1810}
1811
1812HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1813{
1814 /* mBIOSSettings is constant during life time, no need to lock */
1815 aBIOSSettings = mBIOSSettings;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 aRecordingSettings = mRecordingSettings;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832
1833 aGraphicsAdapter = mGraphicsAdapter;
1834
1835 return S_OK;
1836}
1837
1838HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1839{
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841
1842 switch (aProperty)
1843 {
1844 case CPUPropertyType_PAE:
1845 *aValue = mHWData->mPAEEnabled;
1846 break;
1847
1848 case CPUPropertyType_LongMode:
1849 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1850 *aValue = TRUE;
1851 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1852 *aValue = FALSE;
1853#if HC_ARCH_BITS == 64
1854 else
1855 *aValue = TRUE;
1856#else
1857 else
1858 {
1859 *aValue = FALSE;
1860
1861 ComObjPtr<GuestOSType> pGuestOSType;
1862 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1863 pGuestOSType);
1864 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1865 {
1866 if (pGuestOSType->i_is64Bit())
1867 {
1868 ComObjPtr<Host> pHost = mParent->i_host();
1869 alock.release();
1870
1871 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1872 if (FAILED(hrc2))
1873 *aValue = FALSE;
1874 }
1875 }
1876 }
1877#endif
1878 break;
1879
1880 case CPUPropertyType_TripleFaultReset:
1881 *aValue = mHWData->mTripleFaultReset;
1882 break;
1883
1884 case CPUPropertyType_APIC:
1885 *aValue = mHWData->mAPIC;
1886 break;
1887
1888 case CPUPropertyType_X2APIC:
1889 *aValue = mHWData->mX2APIC;
1890 break;
1891
1892 case CPUPropertyType_IBPBOnVMExit:
1893 *aValue = mHWData->mIBPBOnVMExit;
1894 break;
1895
1896 case CPUPropertyType_IBPBOnVMEntry:
1897 *aValue = mHWData->mIBPBOnVMEntry;
1898 break;
1899
1900 case CPUPropertyType_SpecCtrl:
1901 *aValue = mHWData->mSpecCtrl;
1902 break;
1903
1904 case CPUPropertyType_SpecCtrlByHost:
1905 *aValue = mHWData->mSpecCtrlByHost;
1906 break;
1907
1908 case CPUPropertyType_HWVirt:
1909 *aValue = mHWData->mNestedHWVirt;
1910 break;
1911
1912 case CPUPropertyType_L1DFlushOnEMTScheduling:
1913 *aValue = mHWData->mL1DFlushOnSched;
1914 break;
1915
1916 case CPUPropertyType_L1DFlushOnVMEntry:
1917 *aValue = mHWData->mL1DFlushOnVMEntry;
1918 break;
1919
1920 case CPUPropertyType_MDSClearOnEMTScheduling:
1921 *aValue = mHWData->mMDSClearOnSched;
1922 break;
1923
1924 case CPUPropertyType_MDSClearOnVMEntry:
1925 *aValue = mHWData->mMDSClearOnVMEntry;
1926 break;
1927
1928 default:
1929 return E_INVALIDARG;
1930 }
1931 return S_OK;
1932}
1933
1934HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1935{
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 HRESULT rc = i_checkStateDependency(MutableStateDep);
1939 if (FAILED(rc)) return rc;
1940
1941 switch (aProperty)
1942 {
1943 case CPUPropertyType_PAE:
1944 i_setModified(IsModified_MachineData);
1945 mHWData.backup();
1946 mHWData->mPAEEnabled = !!aValue;
1947 break;
1948
1949 case CPUPropertyType_LongMode:
1950 i_setModified(IsModified_MachineData);
1951 mHWData.backup();
1952 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1953 break;
1954
1955 case CPUPropertyType_TripleFaultReset:
1956 i_setModified(IsModified_MachineData);
1957 mHWData.backup();
1958 mHWData->mTripleFaultReset = !!aValue;
1959 break;
1960
1961 case CPUPropertyType_APIC:
1962 if (mHWData->mX2APIC)
1963 aValue = TRUE;
1964 i_setModified(IsModified_MachineData);
1965 mHWData.backup();
1966 mHWData->mAPIC = !!aValue;
1967 break;
1968
1969 case CPUPropertyType_X2APIC:
1970 i_setModified(IsModified_MachineData);
1971 mHWData.backup();
1972 mHWData->mX2APIC = !!aValue;
1973 if (aValue)
1974 mHWData->mAPIC = !!aValue;
1975 break;
1976
1977 case CPUPropertyType_IBPBOnVMExit:
1978 i_setModified(IsModified_MachineData);
1979 mHWData.backup();
1980 mHWData->mIBPBOnVMExit = !!aValue;
1981 break;
1982
1983 case CPUPropertyType_IBPBOnVMEntry:
1984 i_setModified(IsModified_MachineData);
1985 mHWData.backup();
1986 mHWData->mIBPBOnVMEntry = !!aValue;
1987 break;
1988
1989 case CPUPropertyType_SpecCtrl:
1990 i_setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mSpecCtrl = !!aValue;
1993 break;
1994
1995 case CPUPropertyType_SpecCtrlByHost:
1996 i_setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mSpecCtrlByHost = !!aValue;
1999 break;
2000
2001 case CPUPropertyType_HWVirt:
2002 i_setModified(IsModified_MachineData);
2003 mHWData.backup();
2004 mHWData->mNestedHWVirt = !!aValue;
2005 break;
2006
2007 case CPUPropertyType_L1DFlushOnEMTScheduling:
2008 i_setModified(IsModified_MachineData);
2009 mHWData.backup();
2010 mHWData->mL1DFlushOnSched = !!aValue;
2011 break;
2012
2013 case CPUPropertyType_L1DFlushOnVMEntry:
2014 i_setModified(IsModified_MachineData);
2015 mHWData.backup();
2016 mHWData->mL1DFlushOnVMEntry = !!aValue;
2017 break;
2018
2019 case CPUPropertyType_MDSClearOnEMTScheduling:
2020 i_setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mMDSClearOnSched = !!aValue;
2023 break;
2024
2025 case CPUPropertyType_MDSClearOnVMEntry:
2026 i_setModified(IsModified_MachineData);
2027 mHWData.backup();
2028 mHWData->mMDSClearOnVMEntry = !!aValue;
2029 break;
2030
2031 default:
2032 return E_INVALIDARG;
2033 }
2034 return S_OK;
2035}
2036
2037HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2038 ULONG *aValEcx, ULONG *aValEdx)
2039{
2040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2041 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2042 {
2043 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2044 it != mHWData->mCpuIdLeafList.end();
2045 ++it)
2046 {
2047 if (aOrdinal == 0)
2048 {
2049 const settings::CpuIdLeaf &rLeaf= *it;
2050 *aIdx = rLeaf.idx;
2051 *aSubIdx = rLeaf.idxSub;
2052 *aValEax = rLeaf.uEax;
2053 *aValEbx = rLeaf.uEbx;
2054 *aValEcx = rLeaf.uEcx;
2055 *aValEdx = rLeaf.uEdx;
2056 return S_OK;
2057 }
2058 aOrdinal--;
2059 }
2060 }
2061 return E_INVALIDARG;
2062}
2063
2064HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2065{
2066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2067
2068 /*
2069 * Search the list.
2070 */
2071 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2072 {
2073 const settings::CpuIdLeaf &rLeaf= *it;
2074 if ( rLeaf.idx == aIdx
2075 && ( aSubIdx == UINT32_MAX
2076 || rLeaf.idxSub == aSubIdx) )
2077 {
2078 *aValEax = rLeaf.uEax;
2079 *aValEbx = rLeaf.uEbx;
2080 *aValEcx = rLeaf.uEcx;
2081 *aValEdx = rLeaf.uEdx;
2082 return S_OK;
2083 }
2084 }
2085
2086 return E_INVALIDARG;
2087}
2088
2089
2090HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2091{
2092 /*
2093 * Validate input before taking locks and checking state.
2094 */
2095 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2096 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2097 if ( aIdx >= UINT32_C(0x20)
2098 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2099 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2100 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2101
2102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2103 HRESULT rc = i_checkStateDependency(MutableStateDep);
2104 if (FAILED(rc)) return rc;
2105
2106 /*
2107 * Impose a maximum number of leaves.
2108 */
2109 if (mHWData->mCpuIdLeafList.size() > 256)
2110 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2111
2112 /*
2113 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2114 */
2115 i_setModified(IsModified_MachineData);
2116 mHWData.backup();
2117
2118 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2119 {
2120 settings::CpuIdLeaf &rLeaf= *it;
2121 if ( rLeaf.idx == aIdx
2122 && ( aSubIdx == UINT32_MAX
2123 || rLeaf.idxSub == aSubIdx) )
2124 it = mHWData->mCpuIdLeafList.erase(it);
2125 else
2126 ++it;
2127 }
2128
2129 settings::CpuIdLeaf NewLeaf;
2130 NewLeaf.idx = aIdx;
2131 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2132 NewLeaf.uEax = aValEax;
2133 NewLeaf.uEbx = aValEbx;
2134 NewLeaf.uEcx = aValEcx;
2135 NewLeaf.uEdx = aValEdx;
2136 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2137 return S_OK;
2138}
2139
2140HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2141{
2142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2143
2144 HRESULT rc = i_checkStateDependency(MutableStateDep);
2145 if (FAILED(rc)) return rc;
2146
2147 /*
2148 * Do the removal.
2149 */
2150 bool fModified = mHWData.isBackedUp();
2151 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2152 {
2153 settings::CpuIdLeaf &rLeaf= *it;
2154 if ( rLeaf.idx == aIdx
2155 && ( aSubIdx == UINT32_MAX
2156 || rLeaf.idxSub == aSubIdx) )
2157 {
2158 if (!fModified)
2159 {
2160 fModified = true;
2161 i_setModified(IsModified_MachineData);
2162 mHWData.backup();
2163 // Start from the beginning, since mHWData.backup() creates
2164 // a new list, causing iterator mixup. This makes sure that
2165 // the settings are not unnecessarily marked as modified,
2166 // at the price of extra list walking.
2167 it = mHWData->mCpuIdLeafList.begin();
2168 }
2169 else
2170 it = mHWData->mCpuIdLeafList.erase(it);
2171 }
2172 else
2173 ++it;
2174 }
2175
2176 return S_OK;
2177}
2178
2179HRESULT Machine::removeAllCPUIDLeaves()
2180{
2181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2182
2183 HRESULT rc = i_checkStateDependency(MutableStateDep);
2184 if (FAILED(rc)) return rc;
2185
2186 if (mHWData->mCpuIdLeafList.size() > 0)
2187 {
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190
2191 mHWData->mCpuIdLeafList.clear();
2192 }
2193
2194 return S_OK;
2195}
2196HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2197{
2198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 switch(aProperty)
2201 {
2202 case HWVirtExPropertyType_Enabled:
2203 *aValue = mHWData->mHWVirtExEnabled;
2204 break;
2205
2206 case HWVirtExPropertyType_VPID:
2207 *aValue = mHWData->mHWVirtExVPIDEnabled;
2208 break;
2209
2210 case HWVirtExPropertyType_NestedPaging:
2211 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2212 break;
2213
2214 case HWVirtExPropertyType_UnrestrictedExecution:
2215 *aValue = mHWData->mHWVirtExUXEnabled;
2216 break;
2217
2218 case HWVirtExPropertyType_LargePages:
2219 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2220#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2221 *aValue = FALSE;
2222#endif
2223 break;
2224
2225 case HWVirtExPropertyType_Force:
2226 *aValue = mHWData->mHWVirtExForceEnabled;
2227 break;
2228
2229 case HWVirtExPropertyType_UseNativeApi:
2230 *aValue = mHWData->mHWVirtExUseNativeApi;
2231 break;
2232
2233 default:
2234 return E_INVALIDARG;
2235 }
2236 return S_OK;
2237}
2238
2239HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2240{
2241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2242
2243 HRESULT rc = i_checkStateDependency(MutableStateDep);
2244 if (FAILED(rc)) return rc;
2245
2246 switch (aProperty)
2247 {
2248 case HWVirtExPropertyType_Enabled:
2249 i_setModified(IsModified_MachineData);
2250 mHWData.backup();
2251 mHWData->mHWVirtExEnabled = !!aValue;
2252 break;
2253
2254 case HWVirtExPropertyType_VPID:
2255 i_setModified(IsModified_MachineData);
2256 mHWData.backup();
2257 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2258 break;
2259
2260 case HWVirtExPropertyType_NestedPaging:
2261 i_setModified(IsModified_MachineData);
2262 mHWData.backup();
2263 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2264 break;
2265
2266 case HWVirtExPropertyType_UnrestrictedExecution:
2267 i_setModified(IsModified_MachineData);
2268 mHWData.backup();
2269 mHWData->mHWVirtExUXEnabled = !!aValue;
2270 break;
2271
2272 case HWVirtExPropertyType_LargePages:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2276 break;
2277
2278 case HWVirtExPropertyType_Force:
2279 i_setModified(IsModified_MachineData);
2280 mHWData.backup();
2281 mHWData->mHWVirtExForceEnabled = !!aValue;
2282 break;
2283
2284 case HWVirtExPropertyType_UseNativeApi:
2285 i_setModified(IsModified_MachineData);
2286 mHWData.backup();
2287 mHWData->mHWVirtExUseNativeApi = !!aValue;
2288 break;
2289
2290 default:
2291 return E_INVALIDARG;
2292 }
2293
2294 return S_OK;
2295}
2296
2297HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2298{
2299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2300
2301 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2302
2303 return S_OK;
2304}
2305
2306HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2307{
2308 /** @todo (r=dmik):
2309 * 1. Allow to change the name of the snapshot folder containing snapshots
2310 * 2. Rename the folder on disk instead of just changing the property
2311 * value (to be smart and not to leave garbage). Note that it cannot be
2312 * done here because the change may be rolled back. Thus, the right
2313 * place is #saveSettings().
2314 */
2315
2316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2317
2318 HRESULT rc = i_checkStateDependency(MutableStateDep);
2319 if (FAILED(rc)) return rc;
2320
2321 if (!mData->mCurrentSnapshot.isNull())
2322 return setError(E_FAIL,
2323 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2324
2325 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2326
2327 if (strSnapshotFolder.isEmpty())
2328 strSnapshotFolder = "Snapshots";
2329 int vrc = i_calculateFullPath(strSnapshotFolder,
2330 strSnapshotFolder);
2331 if (RT_FAILURE(vrc))
2332 return setErrorBoth(E_FAIL, vrc,
2333 tr("Invalid snapshot folder '%s' (%Rrc)"),
2334 strSnapshotFolder.c_str(), vrc);
2335
2336 i_setModified(IsModified_MachineData);
2337 mUserData.backup();
2338
2339 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2340
2341 return S_OK;
2342}
2343
2344HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2345{
2346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2347
2348 aMediumAttachments.resize(mMediumAttachments->size());
2349 size_t i = 0;
2350 for (MediumAttachmentList::const_iterator
2351 it = mMediumAttachments->begin();
2352 it != mMediumAttachments->end();
2353 ++it, ++i)
2354 aMediumAttachments[i] = *it;
2355
2356 return S_OK;
2357}
2358
2359HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2360{
2361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 Assert(!!mVRDEServer);
2364
2365 aVRDEServer = mVRDEServer;
2366
2367 return S_OK;
2368}
2369
2370HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2371{
2372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 aAudioAdapter = mAudioAdapter;
2375
2376 return S_OK;
2377}
2378
2379HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2380{
2381#ifdef VBOX_WITH_VUSB
2382 clearError();
2383 MultiResult rc(S_OK);
2384
2385# ifdef VBOX_WITH_USB
2386 rc = mParent->i_host()->i_checkUSBProxyService();
2387 if (FAILED(rc)) return rc;
2388# endif
2389
2390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2391
2392 aUSBControllers.resize(mUSBControllers->size());
2393 size_t i = 0;
2394 for (USBControllerList::const_iterator
2395 it = mUSBControllers->begin();
2396 it != mUSBControllers->end();
2397 ++it, ++i)
2398 aUSBControllers[i] = *it;
2399
2400 return S_OK;
2401#else
2402 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2403 * extended error info to indicate that USB is simply not available
2404 * (w/o treating it as a failure), for example, as in OSE */
2405 NOREF(aUSBControllers);
2406 ReturnComNotImplemented();
2407#endif /* VBOX_WITH_VUSB */
2408}
2409
2410HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2411{
2412#ifdef VBOX_WITH_VUSB
2413 clearError();
2414 MultiResult rc(S_OK);
2415
2416# ifdef VBOX_WITH_USB
2417 rc = mParent->i_host()->i_checkUSBProxyService();
2418 if (FAILED(rc)) return rc;
2419# endif
2420
2421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 aUSBDeviceFilters = mUSBDeviceFilters;
2424 return rc;
2425#else
2426 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2427 * extended error info to indicate that USB is simply not available
2428 * (w/o treating it as a failure), for example, as in OSE */
2429 NOREF(aUSBDeviceFilters);
2430 ReturnComNotImplemented();
2431#endif /* VBOX_WITH_VUSB */
2432}
2433
2434HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2435{
2436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2437
2438 aSettingsFilePath = mData->m_strConfigFileFull;
2439
2440 return S_OK;
2441}
2442
2443HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2444{
2445 RT_NOREF(aSettingsFilePath);
2446 ReturnComNotImplemented();
2447}
2448
2449HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2450{
2451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2452
2453 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2454 if (FAILED(rc)) return rc;
2455
2456 if (!mData->pMachineConfigFile->fileExists())
2457 // this is a new machine, and no config file exists yet:
2458 *aSettingsModified = TRUE;
2459 else
2460 *aSettingsModified = (mData->flModifications != 0);
2461
2462 return S_OK;
2463}
2464
2465HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2466{
2467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 *aSessionState = mData->mSession.mState;
2470
2471 return S_OK;
2472}
2473
2474HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2475{
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 aSessionName = mData->mSession.mName;
2479
2480 return S_OK;
2481}
2482
2483HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2484{
2485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 *aSessionPID = mData->mSession.mPID;
2488
2489 return S_OK;
2490}
2491
2492HRESULT Machine::getState(MachineState_T *aState)
2493{
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 *aState = mData->mMachineState;
2497 Assert(mData->mMachineState != MachineState_Null);
2498
2499 return S_OK;
2500}
2501
2502HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2503{
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2507
2508 return S_OK;
2509}
2510
2511HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2512{
2513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 aStateFilePath = mSSData->strStateFilePath;
2516
2517 return S_OK;
2518}
2519
2520HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2521{
2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2523
2524 i_getLogFolder(aLogFolder);
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 aCurrentSnapshot = mData->mCurrentSnapshot;
2534
2535 return S_OK;
2536}
2537
2538HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2539{
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2543 ? 0
2544 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2545
2546 return S_OK;
2547}
2548
2549HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2550{
2551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2552
2553 /* Note: for machines with no snapshots, we always return FALSE
2554 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2555 * reasons :) */
2556
2557 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2558 ? FALSE
2559 : mData->mCurrentStateModified;
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2565{
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568 aSharedFolders.resize(mHWData->mSharedFolders.size());
2569 size_t i = 0;
2570 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2571 it = mHWData->mSharedFolders.begin();
2572 it != mHWData->mSharedFolders.end();
2573 ++it, ++i)
2574 aSharedFolders[i] = *it;
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 *aClipboardMode = mHWData->mClipboardMode;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2589{
2590 HRESULT rc = S_OK;
2591
2592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 alock.release();
2595 rc = i_onClipboardModeChange(aClipboardMode);
2596 alock.acquire();
2597 if (FAILED(rc)) return rc;
2598
2599 i_setModified(IsModified_MachineData);
2600 mHWData.backup();
2601 mHWData->mClipboardMode = aClipboardMode;
2602
2603 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2604 if (Global::IsOnline(mData->mMachineState))
2605 i_saveSettings(NULL);
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2611{
2612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2613
2614 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2615
2616 return S_OK;
2617}
2618
2619HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2620{
2621 HRESULT rc = S_OK;
2622
2623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2624
2625 alock.release();
2626 rc = i_onClipboardFileTransferModeChange(aEnabled);
2627 alock.acquire();
2628 if (FAILED(rc)) return rc;
2629
2630 i_setModified(IsModified_MachineData);
2631 mHWData.backup();
2632 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2633
2634 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2635 if (Global::IsOnline(mData->mMachineState))
2636 i_saveSettings(NULL);
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2642{
2643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2644
2645 *aDnDMode = mHWData->mDnDMode;
2646
2647 return S_OK;
2648}
2649
2650HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2651{
2652 HRESULT rc = S_OK;
2653
2654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 alock.release();
2657 rc = i_onDnDModeChange(aDnDMode);
2658
2659 alock.acquire();
2660 if (FAILED(rc)) return rc;
2661
2662 i_setModified(IsModified_MachineData);
2663 mHWData.backup();
2664 mHWData->mDnDMode = aDnDMode;
2665
2666 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2667 if (Global::IsOnline(mData->mMachineState))
2668 i_saveSettings(NULL);
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aStorageControllers.resize(mStorageControllers->size());
2678 size_t i = 0;
2679 for (StorageControllerList::const_iterator
2680 it = mStorageControllers->begin();
2681 it != mStorageControllers->end();
2682 ++it, ++i)
2683 aStorageControllers[i] = *it;
2684
2685 return S_OK;
2686}
2687
2688HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2689{
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 *aEnabled = mUserData->s.fTeleporterEnabled;
2693
2694 return S_OK;
2695}
2696
2697HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2698{
2699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2700
2701 /* Only allow it to be set to true when PoweredOff or Aborted.
2702 (Clearing it is always permitted.) */
2703 if ( aTeleporterEnabled
2704 && mData->mRegistered
2705 && ( !i_isSessionMachine()
2706 || ( mData->mMachineState != MachineState_PoweredOff
2707 && mData->mMachineState != MachineState_Teleported
2708 && mData->mMachineState != MachineState_Aborted
2709 )
2710 )
2711 )
2712 return setError(VBOX_E_INVALID_VM_STATE,
2713 tr("The machine is not powered off (state is %s)"),
2714 Global::stringifyMachineState(mData->mMachineState));
2715
2716 i_setModified(IsModified_MachineData);
2717 mUserData.backup();
2718 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2719
2720 return S_OK;
2721}
2722
2723HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2724{
2725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2726
2727 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2733{
2734 if (aTeleporterPort >= _64K)
2735 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2736
2737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2740 if (FAILED(rc)) return rc;
2741
2742 i_setModified(IsModified_MachineData);
2743 mUserData.backup();
2744 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2745
2746 return S_OK;
2747}
2748
2749HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2750{
2751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2752
2753 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2759{
2760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2763 if (FAILED(rc)) return rc;
2764
2765 i_setModified(IsModified_MachineData);
2766 mUserData.backup();
2767 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2768
2769 return S_OK;
2770}
2771
2772HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2776
2777 return S_OK;
2778}
2779
2780HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2781{
2782 /*
2783 * Hash the password first.
2784 */
2785 com::Utf8Str aT = aTeleporterPassword;
2786
2787 if (!aT.isEmpty())
2788 {
2789 if (VBoxIsPasswordHashed(&aT))
2790 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2791 VBoxHashPassword(&aT);
2792 }
2793
2794 /*
2795 * Do the update.
2796 */
2797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2798 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2799 if (SUCCEEDED(hrc))
2800 {
2801 i_setModified(IsModified_MachineData);
2802 mUserData.backup();
2803 mUserData->s.strTeleporterPassword = aT;
2804 }
2805
2806 return hrc;
2807}
2808
2809HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2819{
2820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 /* Only allow it to be set to true when PoweredOff or Aborted.
2823 (Clearing it is always permitted.) */
2824 if ( aRTCUseUTC
2825 && mData->mRegistered
2826 && ( !i_isSessionMachine()
2827 || ( mData->mMachineState != MachineState_PoweredOff
2828 && mData->mMachineState != MachineState_Teleported
2829 && mData->mMachineState != MachineState_Aborted
2830 )
2831 )
2832 )
2833 return setError(VBOX_E_INVALID_VM_STATE,
2834 tr("The machine is not powered off (state is %s)"),
2835 Global::stringifyMachineState(mData->mMachineState));
2836
2837 i_setModified(IsModified_MachineData);
2838 mUserData.backup();
2839 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2840
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2854{
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 HRESULT rc = i_checkStateDependency(MutableStateDep);
2858 if (FAILED(rc)) return rc;
2859
2860 i_setModified(IsModified_MachineData);
2861 mHWData.backup();
2862 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2863
2864 return S_OK;
2865}
2866
2867HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870
2871 *aIOCacheSize = mHWData->mIOCacheSize;
2872
2873 return S_OK;
2874}
2875
2876HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2877{
2878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 HRESULT rc = i_checkStateDependency(MutableStateDep);
2881 if (FAILED(rc)) return rc;
2882
2883 i_setModified(IsModified_MachineData);
2884 mHWData.backup();
2885 mHWData->mIOCacheSize = aIOCacheSize;
2886
2887 return S_OK;
2888}
2889
2890
2891/**
2892 * @note Locks objects!
2893 */
2894HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2895 LockType_T aLockType)
2896{
2897 /* check the session state */
2898 SessionState_T state;
2899 HRESULT rc = aSession->COMGETTER(State)(&state);
2900 if (FAILED(rc)) return rc;
2901
2902 if (state != SessionState_Unlocked)
2903 return setError(VBOX_E_INVALID_OBJECT_STATE,
2904 tr("The given session is busy"));
2905
2906 // get the client's IInternalSessionControl interface
2907 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2908 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2909 E_INVALIDARG);
2910
2911 // session name (only used in some code paths)
2912 Utf8Str strSessionName;
2913
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 if (!mData->mRegistered)
2917 return setError(E_UNEXPECTED,
2918 tr("The machine '%s' is not registered"),
2919 mUserData->s.strName.c_str());
2920
2921 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2922
2923 SessionState_T oldState = mData->mSession.mState;
2924 /* Hack: in case the session is closing and there is a progress object
2925 * which allows waiting for the session to be closed, take the opportunity
2926 * and do a limited wait (max. 1 second). This helps a lot when the system
2927 * is busy and thus session closing can take a little while. */
2928 if ( mData->mSession.mState == SessionState_Unlocking
2929 && mData->mSession.mProgress)
2930 {
2931 alock.release();
2932 mData->mSession.mProgress->WaitForCompletion(1000);
2933 alock.acquire();
2934 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2935 }
2936
2937 // try again now
2938 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2939 // (i.e. session machine exists)
2940 && (aLockType == LockType_Shared) // caller wants a shared link to the
2941 // existing session that holds the write lock:
2942 )
2943 {
2944 // OK, share the session... we are now dealing with three processes:
2945 // 1) VBoxSVC (where this code runs);
2946 // 2) process C: the caller's client process (who wants a shared session);
2947 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2948
2949 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2950 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2951 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2952 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2953 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2954
2955 /*
2956 * Release the lock before calling the client process. It's safe here
2957 * since the only thing to do after we get the lock again is to add
2958 * the remote control to the list (which doesn't directly influence
2959 * anything).
2960 */
2961 alock.release();
2962
2963 // get the console of the session holding the write lock (this is a remote call)
2964 ComPtr<IConsole> pConsoleW;
2965 if (mData->mSession.mLockType == LockType_VM)
2966 {
2967 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2968 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2969 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2970 if (FAILED(rc))
2971 // the failure may occur w/o any error info (from RPC), so provide one
2972 return setError(VBOX_E_VM_ERROR,
2973 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
2974 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2975 }
2976
2977 // share the session machine and W's console with the caller's session
2978 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2979 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2980 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2981
2982 if (FAILED(rc))
2983 // the failure may occur w/o any error info (from RPC), so provide one
2984 return setError(VBOX_E_VM_ERROR,
2985 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
2986 alock.acquire();
2987
2988 // need to revalidate the state after acquiring the lock again
2989 if (mData->mSession.mState != SessionState_Locked)
2990 {
2991 pSessionControl->Uninitialize();
2992 return setError(VBOX_E_INVALID_SESSION_STATE,
2993 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2994 mUserData->s.strName.c_str());
2995 }
2996
2997 // add the caller's session to the list
2998 mData->mSession.mRemoteControls.push_back(pSessionControl);
2999 }
3000 else if ( mData->mSession.mState == SessionState_Locked
3001 || mData->mSession.mState == SessionState_Unlocking
3002 )
3003 {
3004 // sharing not permitted, or machine still unlocking:
3005 return setError(VBOX_E_INVALID_OBJECT_STATE,
3006 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3007 mUserData->s.strName.c_str());
3008 }
3009 else
3010 {
3011 // machine is not locked: then write-lock the machine (create the session machine)
3012
3013 // must not be busy
3014 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3015
3016 // get the caller's session PID
3017 RTPROCESS pid = NIL_RTPROCESS;
3018 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3019 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3020 Assert(pid != NIL_RTPROCESS);
3021
3022 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3023
3024 if (fLaunchingVMProcess)
3025 {
3026 if (mData->mSession.mPID == NIL_RTPROCESS)
3027 {
3028 // two or more clients racing for a lock, the one which set the
3029 // session state to Spawning will win, the others will get an
3030 // error as we can't decide here if waiting a little would help
3031 // (only for shared locks this would avoid an error)
3032 return setError(VBOX_E_INVALID_OBJECT_STATE,
3033 tr("The machine '%s' already has a lock request pending"),
3034 mUserData->s.strName.c_str());
3035 }
3036
3037 // this machine is awaiting for a spawning session to be opened:
3038 // then the calling process must be the one that got started by
3039 // LaunchVMProcess()
3040
3041 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3042 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3043
3044#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3045 /* Hardened windows builds spawns three processes when a VM is
3046 launched, the 3rd one is the one that will end up here. */
3047 RTPROCESS ppid;
3048 int rc = RTProcQueryParent(pid, &ppid);
3049 if (RT_SUCCESS(rc))
3050 rc = RTProcQueryParent(ppid, &ppid);
3051 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3052 || rc == VERR_ACCESS_DENIED)
3053 {
3054 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3055 mData->mSession.mPID = pid;
3056 }
3057#endif
3058
3059 if (mData->mSession.mPID != pid)
3060 return setError(E_ACCESSDENIED,
3061 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3062 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3063 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3064 }
3065
3066 // create the mutable SessionMachine from the current machine
3067 ComObjPtr<SessionMachine> sessionMachine;
3068 sessionMachine.createObject();
3069 rc = sessionMachine->init(this);
3070 AssertComRC(rc);
3071
3072 /* NOTE: doing return from this function after this point but
3073 * before the end is forbidden since it may call SessionMachine::uninit()
3074 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3075 * lock while still holding the Machine lock in alock so that a deadlock
3076 * is possible due to the wrong lock order. */
3077
3078 if (SUCCEEDED(rc))
3079 {
3080 /*
3081 * Set the session state to Spawning to protect against subsequent
3082 * attempts to open a session and to unregister the machine after
3083 * we release the lock.
3084 */
3085 SessionState_T origState = mData->mSession.mState;
3086 mData->mSession.mState = SessionState_Spawning;
3087
3088#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3089 /* Get the client token ID to be passed to the client process */
3090 Utf8Str strTokenId;
3091 sessionMachine->i_getTokenId(strTokenId);
3092 Assert(!strTokenId.isEmpty());
3093#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3094 /* Get the client token to be passed to the client process */
3095 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3096 /* The token is now "owned" by pToken, fix refcount */
3097 if (!pToken.isNull())
3098 pToken->Release();
3099#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3100
3101 /*
3102 * Release the lock before calling the client process -- it will call
3103 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3104 * because the state is Spawning, so that LaunchVMProcess() and
3105 * LockMachine() calls will fail. This method, called before we
3106 * acquire the lock again, will fail because of the wrong PID.
3107 *
3108 * Note that mData->mSession.mRemoteControls accessed outside
3109 * the lock may not be modified when state is Spawning, so it's safe.
3110 */
3111 alock.release();
3112
3113 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3114#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3115 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3116#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3117 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3118 /* Now the token is owned by the client process. */
3119 pToken.setNull();
3120#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3121 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3122
3123 /* The failure may occur w/o any error info (from RPC), so provide one */
3124 if (FAILED(rc))
3125 setError(VBOX_E_VM_ERROR,
3126 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3127
3128 // get session name, either to remember or to compare against
3129 // the already known session name.
3130 {
3131 Bstr bstrSessionName;
3132 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3133 if (SUCCEEDED(rc2))
3134 strSessionName = bstrSessionName;
3135 }
3136
3137 if ( SUCCEEDED(rc)
3138 && fLaunchingVMProcess
3139 )
3140 {
3141 /* complete the remote session initialization */
3142
3143 /* get the console from the direct session */
3144 ComPtr<IConsole> console;
3145 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3146 ComAssertComRC(rc);
3147
3148 if (SUCCEEDED(rc) && !console)
3149 {
3150 ComAssert(!!console);
3151 rc = E_FAIL;
3152 }
3153
3154 /* assign machine & console to the remote session */
3155 if (SUCCEEDED(rc))
3156 {
3157 /*
3158 * after LaunchVMProcess(), the first and the only
3159 * entry in remoteControls is that remote session
3160 */
3161 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3162 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3163 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3164
3165 /* The failure may occur w/o any error info (from RPC), so provide one */
3166 if (FAILED(rc))
3167 setError(VBOX_E_VM_ERROR,
3168 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3169 }
3170
3171 if (FAILED(rc))
3172 pSessionControl->Uninitialize();
3173 }
3174
3175 /* acquire the lock again */
3176 alock.acquire();
3177
3178 /* Restore the session state */
3179 mData->mSession.mState = origState;
3180 }
3181
3182 // finalize spawning anyway (this is why we don't return on errors above)
3183 if (fLaunchingVMProcess)
3184 {
3185 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3186 /* Note that the progress object is finalized later */
3187 /** @todo Consider checking mData->mSession.mProgress for cancellation
3188 * around here. */
3189
3190 /* We don't reset mSession.mPID here because it is necessary for
3191 * SessionMachine::uninit() to reap the child process later. */
3192
3193 if (FAILED(rc))
3194 {
3195 /* Close the remote session, remove the remote control from the list
3196 * and reset session state to Closed (@note keep the code in sync
3197 * with the relevant part in checkForSpawnFailure()). */
3198
3199 Assert(mData->mSession.mRemoteControls.size() == 1);
3200 if (mData->mSession.mRemoteControls.size() == 1)
3201 {
3202 ErrorInfoKeeper eik;
3203 mData->mSession.mRemoteControls.front()->Uninitialize();
3204 }
3205
3206 mData->mSession.mRemoteControls.clear();
3207 mData->mSession.mState = SessionState_Unlocked;
3208 }
3209 }
3210 else
3211 {
3212 /* memorize PID of the directly opened session */
3213 if (SUCCEEDED(rc))
3214 mData->mSession.mPID = pid;
3215 }
3216
3217 if (SUCCEEDED(rc))
3218 {
3219 mData->mSession.mLockType = aLockType;
3220 /* memorize the direct session control and cache IUnknown for it */
3221 mData->mSession.mDirectControl = pSessionControl;
3222 mData->mSession.mState = SessionState_Locked;
3223 if (!fLaunchingVMProcess)
3224 mData->mSession.mName = strSessionName;
3225 /* associate the SessionMachine with this Machine */
3226 mData->mSession.mMachine = sessionMachine;
3227
3228 /* request an IUnknown pointer early from the remote party for later
3229 * identity checks (it will be internally cached within mDirectControl
3230 * at least on XPCOM) */
3231 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3232 NOREF(unk);
3233 }
3234
3235 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3236 * would break the lock order */
3237 alock.release();
3238
3239 /* uninitialize the created session machine on failure */
3240 if (FAILED(rc))
3241 sessionMachine->uninit();
3242 }
3243
3244 if (SUCCEEDED(rc))
3245 {
3246 /*
3247 * tell the client watcher thread to update the set of
3248 * machines that have open sessions
3249 */
3250 mParent->i_updateClientWatcher();
3251
3252 if (oldState != SessionState_Locked)
3253 /* fire an event */
3254 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3255 }
3256
3257 return rc;
3258}
3259
3260/**
3261 * @note Locks objects!
3262 */
3263HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3264 const com::Utf8Str &aName,
3265 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3266 ComPtr<IProgress> &aProgress)
3267{
3268 Utf8Str strFrontend(aName);
3269 /* "emergencystop" doesn't need the session, so skip the checks/interface
3270 * retrieval. This code doesn't quite fit in here, but introducing a
3271 * special API method would be even more effort, and would require explicit
3272 * support by every API client. It's better to hide the feature a bit. */
3273 if (strFrontend != "emergencystop")
3274 CheckComArgNotNull(aSession);
3275
3276 HRESULT rc = S_OK;
3277 if (strFrontend.isEmpty())
3278 {
3279 Bstr bstrFrontend;
3280 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3281 if (FAILED(rc))
3282 return rc;
3283 strFrontend = bstrFrontend;
3284 if (strFrontend.isEmpty())
3285 {
3286 ComPtr<ISystemProperties> systemProperties;
3287 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3288 if (FAILED(rc))
3289 return rc;
3290 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3291 if (FAILED(rc))
3292 return rc;
3293 strFrontend = bstrFrontend;
3294 }
3295 /* paranoia - emergencystop is not a valid default */
3296 if (strFrontend == "emergencystop")
3297 strFrontend = Utf8Str::Empty;
3298 }
3299 /* default frontend: Qt GUI */
3300 if (strFrontend.isEmpty())
3301 strFrontend = "GUI/Qt";
3302
3303 if (strFrontend != "emergencystop")
3304 {
3305 /* check the session state */
3306 SessionState_T state;
3307 rc = aSession->COMGETTER(State)(&state);
3308 if (FAILED(rc))
3309 return rc;
3310
3311 if (state != SessionState_Unlocked)
3312 return setError(VBOX_E_INVALID_OBJECT_STATE,
3313 tr("The given session is busy"));
3314
3315 /* get the IInternalSessionControl interface */
3316 ComPtr<IInternalSessionControl> control(aSession);
3317 ComAssertMsgRet(!control.isNull(),
3318 ("No IInternalSessionControl interface"),
3319 E_INVALIDARG);
3320
3321 /* get the teleporter enable state for the progress object init. */
3322 BOOL fTeleporterEnabled;
3323 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3324 if (FAILED(rc))
3325 return rc;
3326
3327 /* create a progress object */
3328 ComObjPtr<ProgressProxy> progress;
3329 progress.createObject();
3330 rc = progress->init(mParent,
3331 static_cast<IMachine*>(this),
3332 Bstr(tr("Starting VM")).raw(),
3333 TRUE /* aCancelable */,
3334 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3335 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3336 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3337 2 /* uFirstOperationWeight */,
3338 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3339
3340 if (SUCCEEDED(rc))
3341 {
3342#ifdef VBOX_WITH_CLOUD_NET
3343 i_connectToCloudNetwork(progress);
3344#endif /* VBOX_WITH_CLOUD_NET */
3345
3346 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3347 if (SUCCEEDED(rc))
3348 {
3349 aProgress = progress;
3350
3351 /* signal the client watcher thread */
3352 mParent->i_updateClientWatcher();
3353
3354 /* fire an event */
3355 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3356 }
3357 }
3358 }
3359 else
3360 {
3361 /* no progress object - either instant success or failure */
3362 aProgress = NULL;
3363
3364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3365
3366 if (mData->mSession.mState != SessionState_Locked)
3367 return setError(VBOX_E_INVALID_OBJECT_STATE,
3368 tr("The machine '%s' is not locked by a session"),
3369 mUserData->s.strName.c_str());
3370
3371 /* must have a VM process associated - do not kill normal API clients
3372 * with an open session */
3373 if (!Global::IsOnline(mData->mMachineState))
3374 return setError(VBOX_E_INVALID_OBJECT_STATE,
3375 tr("The machine '%s' does not have a VM process"),
3376 mUserData->s.strName.c_str());
3377
3378 /* forcibly terminate the VM process */
3379 if (mData->mSession.mPID != NIL_RTPROCESS)
3380 RTProcTerminate(mData->mSession.mPID);
3381
3382 /* signal the client watcher thread, as most likely the client has
3383 * been terminated */
3384 mParent->i_updateClientWatcher();
3385 }
3386
3387 return rc;
3388}
3389
3390HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3391{
3392 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3393 return setError(E_INVALIDARG,
3394 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3395 aPosition, SchemaDefs::MaxBootPosition);
3396
3397 if (aDevice == DeviceType_USB)
3398 return setError(E_NOTIMPL,
3399 tr("Booting from USB device is currently not supported"));
3400
3401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3402
3403 HRESULT rc = i_checkStateDependency(MutableStateDep);
3404 if (FAILED(rc)) return rc;
3405
3406 i_setModified(IsModified_MachineData);
3407 mHWData.backup();
3408 mHWData->mBootOrder[aPosition - 1] = aDevice;
3409
3410 return S_OK;
3411}
3412
3413HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3414{
3415 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3416 return setError(E_INVALIDARG,
3417 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3418 aPosition, SchemaDefs::MaxBootPosition);
3419
3420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3421
3422 *aDevice = mHWData->mBootOrder[aPosition - 1];
3423
3424 return S_OK;
3425}
3426
3427HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3428 LONG aControllerPort,
3429 LONG aDevice,
3430 DeviceType_T aType,
3431 const ComPtr<IMedium> &aMedium)
3432{
3433 IMedium *aM = aMedium;
3434 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3435 aName.c_str(), aControllerPort, aDevice, aType, aM));
3436
3437 // request the host lock first, since might be calling Host methods for getting host drives;
3438 // next, protect the media tree all the while we're in here, as well as our member variables
3439 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3440 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3441
3442 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3443 if (FAILED(rc)) return rc;
3444
3445 /// @todo NEWMEDIA implicit machine registration
3446 if (!mData->mRegistered)
3447 return setError(VBOX_E_INVALID_OBJECT_STATE,
3448 tr("Cannot attach storage devices to an unregistered machine"));
3449
3450 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3451
3452 /* Check for an existing controller. */
3453 ComObjPtr<StorageController> ctl;
3454 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3455 if (FAILED(rc)) return rc;
3456
3457 StorageControllerType_T ctrlType;
3458 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3459 if (FAILED(rc))
3460 return setError(E_FAIL,
3461 tr("Could not get type of controller '%s'"),
3462 aName.c_str());
3463
3464 bool fSilent = false;
3465 Utf8Str strReconfig;
3466
3467 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3468 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3469 if ( mData->mMachineState == MachineState_Paused
3470 && strReconfig == "1")
3471 fSilent = true;
3472
3473 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3474 bool fHotplug = false;
3475 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3476 fHotplug = true;
3477
3478 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3479 return setError(VBOX_E_INVALID_VM_STATE,
3480 tr("Controller '%s' does not support hotplugging"),
3481 aName.c_str());
3482
3483 // check that the port and device are not out of range
3484 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3485 if (FAILED(rc)) return rc;
3486
3487 /* check if the device slot is already busy */
3488 MediumAttachment *pAttachTemp;
3489 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3490 aName,
3491 aControllerPort,
3492 aDevice)))
3493 {
3494 Medium *pMedium = pAttachTemp->i_getMedium();
3495 if (pMedium)
3496 {
3497 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3498 return setError(VBOX_E_OBJECT_IN_USE,
3499 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3500 pMedium->i_getLocationFull().c_str(),
3501 aControllerPort,
3502 aDevice,
3503 aName.c_str());
3504 }
3505 else
3506 return setError(VBOX_E_OBJECT_IN_USE,
3507 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3508 aControllerPort, aDevice, aName.c_str());
3509 }
3510
3511 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3512 if (aMedium && medium.isNull())
3513 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3514
3515 AutoCaller mediumCaller(medium);
3516 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3517
3518 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3519
3520 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3521 && !medium.isNull()
3522 )
3523 return setError(VBOX_E_OBJECT_IN_USE,
3524 tr("Medium '%s' is already attached to this virtual machine"),
3525 medium->i_getLocationFull().c_str());
3526
3527 if (!medium.isNull())
3528 {
3529 MediumType_T mtype = medium->i_getType();
3530 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3531 // For DVDs it's not written to the config file, so needs no global config
3532 // version bump. For floppies it's a new attribute "type", which is ignored
3533 // by older VirtualBox version, so needs no global config version bump either.
3534 // For hard disks this type is not accepted.
3535 if (mtype == MediumType_MultiAttach)
3536 {
3537 // This type is new with VirtualBox 4.0 and therefore requires settings
3538 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3539 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3540 // two reasons: The medium type is a property of the media registry tree, which
3541 // can reside in the global config file (for pre-4.0 media); we would therefore
3542 // possibly need to bump the global config version. We don't want to do that though
3543 // because that might make downgrading to pre-4.0 impossible.
3544 // As a result, we can only use these two new types if the medium is NOT in the
3545 // global registry:
3546 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3547 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3548 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3549 )
3550 return setError(VBOX_E_INVALID_OBJECT_STATE,
3551 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3552 "to machines that were created with VirtualBox 4.0 or later"),
3553 medium->i_getLocationFull().c_str());
3554 }
3555 }
3556
3557 bool fIndirect = false;
3558 if (!medium.isNull())
3559 fIndirect = medium->i_isReadOnly();
3560 bool associate = true;
3561
3562 do
3563 {
3564 if ( aType == DeviceType_HardDisk
3565 && mMediumAttachments.isBackedUp())
3566 {
3567 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3568
3569 /* check if the medium was attached to the VM before we started
3570 * changing attachments in which case the attachment just needs to
3571 * be restored */
3572 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3573 {
3574 AssertReturn(!fIndirect, E_FAIL);
3575
3576 /* see if it's the same bus/channel/device */
3577 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3578 {
3579 /* the simplest case: restore the whole attachment
3580 * and return, nothing else to do */
3581 mMediumAttachments->push_back(pAttachTemp);
3582
3583 /* Reattach the medium to the VM. */
3584 if (fHotplug || fSilent)
3585 {
3586 mediumLock.release();
3587 treeLock.release();
3588 alock.release();
3589
3590 MediumLockList *pMediumLockList(new MediumLockList());
3591
3592 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3593 medium /* pToLockWrite */,
3594 false /* fMediumLockWriteAll */,
3595 NULL,
3596 *pMediumLockList);
3597 alock.acquire();
3598 if (FAILED(rc))
3599 delete pMediumLockList;
3600 else
3601 {
3602 mData->mSession.mLockedMedia.Unlock();
3603 alock.release();
3604 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3605 mData->mSession.mLockedMedia.Lock();
3606 alock.acquire();
3607 }
3608 alock.release();
3609
3610 if (SUCCEEDED(rc))
3611 {
3612 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3613 /* Remove lock list in case of error. */
3614 if (FAILED(rc))
3615 {
3616 mData->mSession.mLockedMedia.Unlock();
3617 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3618 mData->mSession.mLockedMedia.Lock();
3619 }
3620 }
3621 }
3622
3623 return S_OK;
3624 }
3625
3626 /* bus/channel/device differ; we need a new attachment object,
3627 * but don't try to associate it again */
3628 associate = false;
3629 break;
3630 }
3631 }
3632
3633 /* go further only if the attachment is to be indirect */
3634 if (!fIndirect)
3635 break;
3636
3637 /* perform the so called smart attachment logic for indirect
3638 * attachments. Note that smart attachment is only applicable to base
3639 * hard disks. */
3640
3641 if (medium->i_getParent().isNull())
3642 {
3643 /* first, investigate the backup copy of the current hard disk
3644 * attachments to make it possible to re-attach existing diffs to
3645 * another device slot w/o losing their contents */
3646 if (mMediumAttachments.isBackedUp())
3647 {
3648 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3649
3650 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3651 uint32_t foundLevel = 0;
3652
3653 for (MediumAttachmentList::const_iterator
3654 it = oldAtts.begin();
3655 it != oldAtts.end();
3656 ++it)
3657 {
3658 uint32_t level = 0;
3659 MediumAttachment *pAttach = *it;
3660 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3661 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3662 if (pMedium.isNull())
3663 continue;
3664
3665 if (pMedium->i_getBase(&level) == medium)
3666 {
3667 /* skip the hard disk if its currently attached (we
3668 * cannot attach the same hard disk twice) */
3669 if (i_findAttachment(*mMediumAttachments.data(),
3670 pMedium))
3671 continue;
3672
3673 /* matched device, channel and bus (i.e. attached to the
3674 * same place) will win and immediately stop the search;
3675 * otherwise the attachment that has the youngest
3676 * descendant of medium will be used
3677 */
3678 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3679 {
3680 /* the simplest case: restore the whole attachment
3681 * and return, nothing else to do */
3682 mMediumAttachments->push_back(*it);
3683
3684 /* Reattach the medium to the VM. */
3685 if (fHotplug || fSilent)
3686 {
3687 mediumLock.release();
3688 treeLock.release();
3689 alock.release();
3690
3691 MediumLockList *pMediumLockList(new MediumLockList());
3692
3693 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3694 medium /* pToLockWrite */,
3695 false /* fMediumLockWriteAll */,
3696 NULL,
3697 *pMediumLockList);
3698 alock.acquire();
3699 if (FAILED(rc))
3700 delete pMediumLockList;
3701 else
3702 {
3703 mData->mSession.mLockedMedia.Unlock();
3704 alock.release();
3705 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3706 mData->mSession.mLockedMedia.Lock();
3707 alock.acquire();
3708 }
3709 alock.release();
3710
3711 if (SUCCEEDED(rc))
3712 {
3713 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3714 /* Remove lock list in case of error. */
3715 if (FAILED(rc))
3716 {
3717 mData->mSession.mLockedMedia.Unlock();
3718 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3719 mData->mSession.mLockedMedia.Lock();
3720 }
3721 }
3722 }
3723
3724 return S_OK;
3725 }
3726 else if ( foundIt == oldAtts.end()
3727 || level > foundLevel /* prefer younger */
3728 )
3729 {
3730 foundIt = it;
3731 foundLevel = level;
3732 }
3733 }
3734 }
3735
3736 if (foundIt != oldAtts.end())
3737 {
3738 /* use the previously attached hard disk */
3739 medium = (*foundIt)->i_getMedium();
3740 mediumCaller.attach(medium);
3741 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3742 mediumLock.attach(medium);
3743 /* not implicit, doesn't require association with this VM */
3744 fIndirect = false;
3745 associate = false;
3746 /* go right to the MediumAttachment creation */
3747 break;
3748 }
3749 }
3750
3751 /* must give up the medium lock and medium tree lock as below we
3752 * go over snapshots, which needs a lock with higher lock order. */
3753 mediumLock.release();
3754 treeLock.release();
3755
3756 /* then, search through snapshots for the best diff in the given
3757 * hard disk's chain to base the new diff on */
3758
3759 ComObjPtr<Medium> base;
3760 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3761 while (snap)
3762 {
3763 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3764
3765 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3766
3767 MediumAttachment *pAttachFound = NULL;
3768 uint32_t foundLevel = 0;
3769
3770 for (MediumAttachmentList::const_iterator
3771 it = snapAtts.begin();
3772 it != snapAtts.end();
3773 ++it)
3774 {
3775 MediumAttachment *pAttach = *it;
3776 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3777 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3778 if (pMedium.isNull())
3779 continue;
3780
3781 uint32_t level = 0;
3782 if (pMedium->i_getBase(&level) == medium)
3783 {
3784 /* matched device, channel and bus (i.e. attached to the
3785 * same place) will win and immediately stop the search;
3786 * otherwise the attachment that has the youngest
3787 * descendant of medium will be used
3788 */
3789 if ( pAttach->i_getDevice() == aDevice
3790 && pAttach->i_getPort() == aControllerPort
3791 && pAttach->i_getControllerName() == aName
3792 )
3793 {
3794 pAttachFound = pAttach;
3795 break;
3796 }
3797 else if ( !pAttachFound
3798 || level > foundLevel /* prefer younger */
3799 )
3800 {
3801 pAttachFound = pAttach;
3802 foundLevel = level;
3803 }
3804 }
3805 }
3806
3807 if (pAttachFound)
3808 {
3809 base = pAttachFound->i_getMedium();
3810 break;
3811 }
3812
3813 snap = snap->i_getParent();
3814 }
3815
3816 /* re-lock medium tree and the medium, as we need it below */
3817 treeLock.acquire();
3818 mediumLock.acquire();
3819
3820 /* found a suitable diff, use it as a base */
3821 if (!base.isNull())
3822 {
3823 medium = base;
3824 mediumCaller.attach(medium);
3825 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3826 mediumLock.attach(medium);
3827 }
3828 }
3829
3830 Utf8Str strFullSnapshotFolder;
3831 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3832
3833 ComObjPtr<Medium> diff;
3834 diff.createObject();
3835 // store this diff in the same registry as the parent
3836 Guid uuidRegistryParent;
3837 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3838 {
3839 // parent image has no registry: this can happen if we're attaching a new immutable
3840 // image that has not yet been attached (medium then points to the base and we're
3841 // creating the diff image for the immutable, and the parent is not yet registered);
3842 // put the parent in the machine registry then
3843 mediumLock.release();
3844 treeLock.release();
3845 alock.release();
3846 i_addMediumToRegistry(medium);
3847 alock.acquire();
3848 treeLock.acquire();
3849 mediumLock.acquire();
3850 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3851 }
3852 rc = diff->init(mParent,
3853 medium->i_getPreferredDiffFormat(),
3854 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3855 uuidRegistryParent,
3856 DeviceType_HardDisk);
3857 if (FAILED(rc)) return rc;
3858
3859 /* Apply the normal locking logic to the entire chain. */
3860 MediumLockList *pMediumLockList(new MediumLockList());
3861 mediumLock.release();
3862 treeLock.release();
3863 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3864 diff /* pToLockWrite */,
3865 false /* fMediumLockWriteAll */,
3866 medium,
3867 *pMediumLockList);
3868 treeLock.acquire();
3869 mediumLock.acquire();
3870 if (SUCCEEDED(rc))
3871 {
3872 mediumLock.release();
3873 treeLock.release();
3874 rc = pMediumLockList->Lock();
3875 treeLock.acquire();
3876 mediumLock.acquire();
3877 if (FAILED(rc))
3878 setError(rc,
3879 tr("Could not lock medium when creating diff '%s'"),
3880 diff->i_getLocationFull().c_str());
3881 else
3882 {
3883 /* will release the lock before the potentially lengthy
3884 * operation, so protect with the special state */
3885 MachineState_T oldState = mData->mMachineState;
3886 i_setMachineState(MachineState_SettingUp);
3887
3888 mediumLock.release();
3889 treeLock.release();
3890 alock.release();
3891
3892 rc = medium->i_createDiffStorage(diff,
3893 medium->i_getPreferredDiffVariant(),
3894 pMediumLockList,
3895 NULL /* aProgress */,
3896 true /* aWait */,
3897 false /* aNotify */);
3898
3899 alock.acquire();
3900 treeLock.acquire();
3901 mediumLock.acquire();
3902
3903 i_setMachineState(oldState);
3904 }
3905 }
3906
3907 /* Unlock the media and free the associated memory. */
3908 delete pMediumLockList;
3909
3910 if (FAILED(rc)) return rc;
3911
3912 /* use the created diff for the actual attachment */
3913 medium = diff;
3914 mediumCaller.attach(medium);
3915 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3916 mediumLock.attach(medium);
3917 }
3918 while (0);
3919
3920 ComObjPtr<MediumAttachment> attachment;
3921 attachment.createObject();
3922 rc = attachment->init(this,
3923 medium,
3924 aName,
3925 aControllerPort,
3926 aDevice,
3927 aType,
3928 fIndirect,
3929 false /* fPassthrough */,
3930 false /* fTempEject */,
3931 false /* fNonRotational */,
3932 false /* fDiscard */,
3933 fHotplug /* fHotPluggable */,
3934 Utf8Str::Empty);
3935 if (FAILED(rc)) return rc;
3936
3937 if (associate && !medium.isNull())
3938 {
3939 // as the last step, associate the medium to the VM
3940 rc = medium->i_addBackReference(mData->mUuid);
3941 // here we can fail because of Deleting, or being in process of creating a Diff
3942 if (FAILED(rc)) return rc;
3943
3944 mediumLock.release();
3945 treeLock.release();
3946 alock.release();
3947 i_addMediumToRegistry(medium);
3948 alock.acquire();
3949 treeLock.acquire();
3950 mediumLock.acquire();
3951 }
3952
3953 /* success: finally remember the attachment */
3954 i_setModified(IsModified_Storage);
3955 mMediumAttachments.backup();
3956 mMediumAttachments->push_back(attachment);
3957
3958 mediumLock.release();
3959 treeLock.release();
3960 alock.release();
3961
3962 if (fHotplug || fSilent)
3963 {
3964 if (!medium.isNull())
3965 {
3966 MediumLockList *pMediumLockList(new MediumLockList());
3967
3968 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3969 medium /* pToLockWrite */,
3970 false /* fMediumLockWriteAll */,
3971 NULL,
3972 *pMediumLockList);
3973 alock.acquire();
3974 if (FAILED(rc))
3975 delete pMediumLockList;
3976 else
3977 {
3978 mData->mSession.mLockedMedia.Unlock();
3979 alock.release();
3980 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3981 mData->mSession.mLockedMedia.Lock();
3982 alock.acquire();
3983 }
3984 alock.release();
3985 }
3986
3987 if (SUCCEEDED(rc))
3988 {
3989 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3990 /* Remove lock list in case of error. */
3991 if (FAILED(rc))
3992 {
3993 mData->mSession.mLockedMedia.Unlock();
3994 mData->mSession.mLockedMedia.Remove(attachment);
3995 mData->mSession.mLockedMedia.Lock();
3996 }
3997 }
3998 }
3999
4000 /* Save modified registries, but skip this machine as it's the caller's
4001 * job to save its settings like all other settings changes. */
4002 mParent->i_unmarkRegistryModified(i_getId());
4003 mParent->i_saveModifiedRegistries();
4004
4005 if (SUCCEEDED(rc))
4006 {
4007 if (fIndirect && medium != aM)
4008 mParent->i_onMediumConfigChanged(medium);
4009 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4010 }
4011
4012 return rc;
4013}
4014
4015HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4016 LONG aDevice)
4017{
4018 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4019 aName.c_str(), aControllerPort, aDevice));
4020
4021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4022
4023 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4024 if (FAILED(rc)) return rc;
4025
4026 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4027
4028 /* Check for an existing controller. */
4029 ComObjPtr<StorageController> ctl;
4030 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4031 if (FAILED(rc)) return rc;
4032
4033 StorageControllerType_T ctrlType;
4034 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4035 if (FAILED(rc))
4036 return setError(E_FAIL,
4037 tr("Could not get type of controller '%s'"),
4038 aName.c_str());
4039
4040 bool fSilent = false;
4041 Utf8Str strReconfig;
4042
4043 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4044 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4045 if ( mData->mMachineState == MachineState_Paused
4046 && strReconfig == "1")
4047 fSilent = true;
4048
4049 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4050 bool fHotplug = false;
4051 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4052 fHotplug = true;
4053
4054 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4055 return setError(VBOX_E_INVALID_VM_STATE,
4056 tr("Controller '%s' does not support hotplugging"),
4057 aName.c_str());
4058
4059 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4060 aName,
4061 aControllerPort,
4062 aDevice);
4063 if (!pAttach)
4064 return setError(VBOX_E_OBJECT_NOT_FOUND,
4065 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4066 aDevice, aControllerPort, aName.c_str());
4067
4068 if (fHotplug && !pAttach->i_getHotPluggable())
4069 return setError(VBOX_E_NOT_SUPPORTED,
4070 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4071 aDevice, aControllerPort, aName.c_str());
4072
4073 /*
4074 * The VM has to detach the device before we delete any implicit diffs.
4075 * If this fails we can roll back without loosing data.
4076 */
4077 if (fHotplug || fSilent)
4078 {
4079 alock.release();
4080 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4081 alock.acquire();
4082 }
4083 if (FAILED(rc)) return rc;
4084
4085 /* If we are here everything went well and we can delete the implicit now. */
4086 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4087
4088 alock.release();
4089
4090 /* Save modified registries, but skip this machine as it's the caller's
4091 * job to save its settings like all other settings changes. */
4092 mParent->i_unmarkRegistryModified(i_getId());
4093 mParent->i_saveModifiedRegistries();
4094
4095 if (SUCCEEDED(rc))
4096 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4097
4098 return rc;
4099}
4100
4101HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4102 LONG aDevice, BOOL aPassthrough)
4103{
4104 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4105 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4106
4107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4108
4109 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4110 if (FAILED(rc)) return rc;
4111
4112 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4113
4114 /* Check for an existing controller. */
4115 ComObjPtr<StorageController> ctl;
4116 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4117 if (FAILED(rc)) return rc;
4118
4119 StorageControllerType_T ctrlType;
4120 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4121 if (FAILED(rc))
4122 return setError(E_FAIL,
4123 tr("Could not get type of controller '%s'"),
4124 aName.c_str());
4125
4126 bool fSilent = false;
4127 Utf8Str strReconfig;
4128
4129 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4130 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4131 if ( mData->mMachineState == MachineState_Paused
4132 && strReconfig == "1")
4133 fSilent = true;
4134
4135 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4136 bool fHotplug = false;
4137 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4138 fHotplug = true;
4139
4140 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4141 return setError(VBOX_E_INVALID_VM_STATE,
4142 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4143 aName.c_str());
4144
4145 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4146 aName,
4147 aControllerPort,
4148 aDevice);
4149 if (!pAttach)
4150 return setError(VBOX_E_OBJECT_NOT_FOUND,
4151 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4152 aDevice, aControllerPort, aName.c_str());
4153
4154
4155 i_setModified(IsModified_Storage);
4156 mMediumAttachments.backup();
4157
4158 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4159
4160 if (pAttach->i_getType() != DeviceType_DVD)
4161 return setError(E_INVALIDARG,
4162 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4163 aDevice, aControllerPort, aName.c_str());
4164
4165 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4166
4167 pAttach->i_updatePassthrough(!!aPassthrough);
4168
4169 attLock.release();
4170 alock.release();
4171 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4172 if (SUCCEEDED(rc) && fValueChanged)
4173 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4174
4175 return rc;
4176}
4177
4178HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4179 LONG aDevice, BOOL aTemporaryEject)
4180{
4181
4182 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4183 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4184
4185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4186
4187 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4188 if (FAILED(rc)) return rc;
4189
4190 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4191 aName,
4192 aControllerPort,
4193 aDevice);
4194 if (!pAttach)
4195 return setError(VBOX_E_OBJECT_NOT_FOUND,
4196 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4197 aDevice, aControllerPort, aName.c_str());
4198
4199
4200 i_setModified(IsModified_Storage);
4201 mMediumAttachments.backup();
4202
4203 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4204
4205 if (pAttach->i_getType() != DeviceType_DVD)
4206 return setError(E_INVALIDARG,
4207 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4208 aDevice, aControllerPort, aName.c_str());
4209 pAttach->i_updateTempEject(!!aTemporaryEject);
4210
4211 return S_OK;
4212}
4213
4214HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4215 LONG aDevice, BOOL aNonRotational)
4216{
4217
4218 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4219 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4220
4221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4222
4223 HRESULT rc = i_checkStateDependency(MutableStateDep);
4224 if (FAILED(rc)) return rc;
4225
4226 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4227
4228 if (Global::IsOnlineOrTransient(mData->mMachineState))
4229 return setError(VBOX_E_INVALID_VM_STATE,
4230 tr("Invalid machine state: %s"),
4231 Global::stringifyMachineState(mData->mMachineState));
4232
4233 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4234 aName,
4235 aControllerPort,
4236 aDevice);
4237 if (!pAttach)
4238 return setError(VBOX_E_OBJECT_NOT_FOUND,
4239 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4240 aDevice, aControllerPort, aName.c_str());
4241
4242
4243 i_setModified(IsModified_Storage);
4244 mMediumAttachments.backup();
4245
4246 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4247
4248 if (pAttach->i_getType() != DeviceType_HardDisk)
4249 return setError(E_INVALIDARG,
4250 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"),
4251 aDevice, aControllerPort, aName.c_str());
4252 pAttach->i_updateNonRotational(!!aNonRotational);
4253
4254 return S_OK;
4255}
4256
4257HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4258 LONG aDevice, BOOL aDiscard)
4259{
4260
4261 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4262 aName.c_str(), aControllerPort, aDevice, aDiscard));
4263
4264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4265
4266 HRESULT rc = i_checkStateDependency(MutableStateDep);
4267 if (FAILED(rc)) return rc;
4268
4269 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4270
4271 if (Global::IsOnlineOrTransient(mData->mMachineState))
4272 return setError(VBOX_E_INVALID_VM_STATE,
4273 tr("Invalid machine state: %s"),
4274 Global::stringifyMachineState(mData->mMachineState));
4275
4276 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4277 aName,
4278 aControllerPort,
4279 aDevice);
4280 if (!pAttach)
4281 return setError(VBOX_E_OBJECT_NOT_FOUND,
4282 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4283 aDevice, aControllerPort, aName.c_str());
4284
4285
4286 i_setModified(IsModified_Storage);
4287 mMediumAttachments.backup();
4288
4289 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4290
4291 if (pAttach->i_getType() != DeviceType_HardDisk)
4292 return setError(E_INVALIDARG,
4293 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"),
4294 aDevice, aControllerPort, aName.c_str());
4295 pAttach->i_updateDiscard(!!aDiscard);
4296
4297 return S_OK;
4298}
4299
4300HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4301 LONG aDevice, BOOL aHotPluggable)
4302{
4303 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4304 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4305
4306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4307
4308 HRESULT rc = i_checkStateDependency(MutableStateDep);
4309 if (FAILED(rc)) return rc;
4310
4311 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4312
4313 if (Global::IsOnlineOrTransient(mData->mMachineState))
4314 return setError(VBOX_E_INVALID_VM_STATE,
4315 tr("Invalid machine state: %s"),
4316 Global::stringifyMachineState(mData->mMachineState));
4317
4318 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4319 aName,
4320 aControllerPort,
4321 aDevice);
4322 if (!pAttach)
4323 return setError(VBOX_E_OBJECT_NOT_FOUND,
4324 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4325 aDevice, aControllerPort, aName.c_str());
4326
4327 /* Check for an existing controller. */
4328 ComObjPtr<StorageController> ctl;
4329 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4330 if (FAILED(rc)) return rc;
4331
4332 StorageControllerType_T ctrlType;
4333 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4334 if (FAILED(rc))
4335 return setError(E_FAIL,
4336 tr("Could not get type of controller '%s'"),
4337 aName.c_str());
4338
4339 if (!i_isControllerHotplugCapable(ctrlType))
4340 return setError(VBOX_E_NOT_SUPPORTED,
4341 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4342 aName.c_str());
4343
4344 i_setModified(IsModified_Storage);
4345 mMediumAttachments.backup();
4346
4347 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4348
4349 if (pAttach->i_getType() == DeviceType_Floppy)
4350 return setError(E_INVALIDARG,
4351 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"),
4352 aDevice, aControllerPort, aName.c_str());
4353 pAttach->i_updateHotPluggable(!!aHotPluggable);
4354
4355 return S_OK;
4356}
4357
4358HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4359 LONG aDevice)
4360{
4361 int rc = S_OK;
4362 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4363 aName.c_str(), aControllerPort, aDevice));
4364
4365 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4366
4367 return rc;
4368}
4369
4370HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4371 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4372{
4373 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4374 aName.c_str(), aControllerPort, aDevice));
4375
4376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4377
4378 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4379 if (FAILED(rc)) return rc;
4380
4381 if (Global::IsOnlineOrTransient(mData->mMachineState))
4382 return setError(VBOX_E_INVALID_VM_STATE,
4383 tr("Invalid machine state: %s"),
4384 Global::stringifyMachineState(mData->mMachineState));
4385
4386 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4387 aName,
4388 aControllerPort,
4389 aDevice);
4390 if (!pAttach)
4391 return setError(VBOX_E_OBJECT_NOT_FOUND,
4392 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4393 aDevice, aControllerPort, aName.c_str());
4394
4395
4396 i_setModified(IsModified_Storage);
4397 mMediumAttachments.backup();
4398
4399 IBandwidthGroup *iB = aBandwidthGroup;
4400 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4401 if (aBandwidthGroup && group.isNull())
4402 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4403
4404 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4405
4406 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4407 if (strBandwidthGroupOld.isNotEmpty())
4408 {
4409 /* Get the bandwidth group object and release it - this must not fail. */
4410 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4411 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4412 Assert(SUCCEEDED(rc));
4413
4414 pBandwidthGroupOld->i_release();
4415 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4416 }
4417
4418 if (!group.isNull())
4419 {
4420 group->i_reference();
4421 pAttach->i_updateBandwidthGroup(group->i_getName());
4422 }
4423
4424 return S_OK;
4425}
4426
4427HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4428 LONG aControllerPort,
4429 LONG aDevice,
4430 DeviceType_T aType)
4431{
4432 HRESULT rc = S_OK;
4433
4434 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4435 aName.c_str(), aControllerPort, aDevice, aType));
4436
4437 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4438
4439 return rc;
4440}
4441
4442
4443HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4444 LONG aControllerPort,
4445 LONG aDevice,
4446 BOOL aForce)
4447{
4448 int rc = S_OK;
4449 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4450 aName.c_str(), aControllerPort, aForce));
4451
4452 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4453
4454 return rc;
4455}
4456
4457HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4458 LONG aControllerPort,
4459 LONG aDevice,
4460 const ComPtr<IMedium> &aMedium,
4461 BOOL aForce)
4462{
4463 int rc = S_OK;
4464 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4465 aName.c_str(), aControllerPort, aDevice, aForce));
4466
4467 // request the host lock first, since might be calling Host methods for getting host drives;
4468 // next, protect the media tree all the while we're in here, as well as our member variables
4469 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4470 this->lockHandle(),
4471 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4472
4473 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4474 aName,
4475 aControllerPort,
4476 aDevice);
4477 if (pAttach.isNull())
4478 return setError(VBOX_E_OBJECT_NOT_FOUND,
4479 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4480 aDevice, aControllerPort, aName.c_str());
4481
4482 /* Remember previously mounted medium. The medium before taking the
4483 * backup is not necessarily the same thing. */
4484 ComObjPtr<Medium> oldmedium;
4485 oldmedium = pAttach->i_getMedium();
4486
4487 IMedium *iM = aMedium;
4488 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4489 if (aMedium && pMedium.isNull())
4490 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4491
4492 AutoCaller mediumCaller(pMedium);
4493 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4494
4495 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4496 if (pMedium)
4497 {
4498 DeviceType_T mediumType = pAttach->i_getType();
4499 switch (mediumType)
4500 {
4501 case DeviceType_DVD:
4502 case DeviceType_Floppy:
4503 break;
4504
4505 default:
4506 return setError(VBOX_E_INVALID_OBJECT_STATE,
4507 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4508 aControllerPort,
4509 aDevice,
4510 aName.c_str());
4511 }
4512 }
4513
4514 i_setModified(IsModified_Storage);
4515 mMediumAttachments.backup();
4516
4517 {
4518 // The backup operation makes the pAttach reference point to the
4519 // old settings. Re-get the correct reference.
4520 pAttach = i_findAttachment(*mMediumAttachments.data(),
4521 aName,
4522 aControllerPort,
4523 aDevice);
4524 if (!oldmedium.isNull())
4525 oldmedium->i_removeBackReference(mData->mUuid);
4526 if (!pMedium.isNull())
4527 {
4528 pMedium->i_addBackReference(mData->mUuid);
4529
4530 mediumLock.release();
4531 multiLock.release();
4532 i_addMediumToRegistry(pMedium);
4533 multiLock.acquire();
4534 mediumLock.acquire();
4535 }
4536
4537 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4538 pAttach->i_updateMedium(pMedium);
4539 }
4540
4541 i_setModified(IsModified_Storage);
4542
4543 mediumLock.release();
4544 multiLock.release();
4545 rc = i_onMediumChange(pAttach, aForce);
4546 multiLock.acquire();
4547 mediumLock.acquire();
4548
4549 /* On error roll back this change only. */
4550 if (FAILED(rc))
4551 {
4552 if (!pMedium.isNull())
4553 pMedium->i_removeBackReference(mData->mUuid);
4554 pAttach = i_findAttachment(*mMediumAttachments.data(),
4555 aName,
4556 aControllerPort,
4557 aDevice);
4558 /* If the attachment is gone in the meantime, bail out. */
4559 if (pAttach.isNull())
4560 return rc;
4561 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4562 if (!oldmedium.isNull())
4563 oldmedium->i_addBackReference(mData->mUuid);
4564 pAttach->i_updateMedium(oldmedium);
4565 }
4566
4567 mediumLock.release();
4568 multiLock.release();
4569
4570 /* Save modified registries, but skip this machine as it's the caller's
4571 * job to save its settings like all other settings changes. */
4572 mParent->i_unmarkRegistryModified(i_getId());
4573 mParent->i_saveModifiedRegistries();
4574
4575 return rc;
4576}
4577HRESULT Machine::getMedium(const com::Utf8Str &aName,
4578 LONG aControllerPort,
4579 LONG aDevice,
4580 ComPtr<IMedium> &aMedium)
4581{
4582 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4583 aName.c_str(), aControllerPort, aDevice));
4584
4585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4586
4587 aMedium = NULL;
4588
4589 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4590 aName,
4591 aControllerPort,
4592 aDevice);
4593 if (pAttach.isNull())
4594 return setError(VBOX_E_OBJECT_NOT_FOUND,
4595 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4596 aDevice, aControllerPort, aName.c_str());
4597
4598 aMedium = pAttach->i_getMedium();
4599
4600 return S_OK;
4601}
4602
4603HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4604{
4605
4606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4609
4610 return S_OK;
4611}
4612
4613HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4614{
4615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4618
4619 return S_OK;
4620}
4621
4622HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4623{
4624 /* Do not assert if slot is out of range, just return the advertised
4625 status. testdriver/vbox.py triggers this in logVmInfo. */
4626 if (aSlot >= mNetworkAdapters.size())
4627 return setError(E_INVALIDARG,
4628 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4629 aSlot, mNetworkAdapters.size());
4630
4631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4634
4635 return S_OK;
4636}
4637
4638HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4639{
4640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4641
4642 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4643 size_t i = 0;
4644 for (settings::StringsMap::const_iterator
4645 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4646 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4647 ++it, ++i)
4648 aKeys[i] = it->first;
4649
4650 return S_OK;
4651}
4652
4653 /**
4654 * @note Locks this object for reading.
4655 */
4656HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4657 com::Utf8Str &aValue)
4658{
4659 /* start with nothing found */
4660 aValue = "";
4661
4662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4663
4664 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4665 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4666 // found:
4667 aValue = it->second; // source is a Utf8Str
4668
4669 /* return the result to caller (may be empty) */
4670 return S_OK;
4671}
4672
4673 /**
4674 * @note Locks mParent for writing + this object for writing.
4675 */
4676HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4677{
4678 /* Because control characters in aKey have caused problems in the settings
4679 * they are rejected unless the key should be deleted. */
4680 if (!aValue.isEmpty())
4681 {
4682 for (size_t i = 0; i < aKey.length(); ++i)
4683 {
4684 char ch = aKey[i];
4685 if (RTLocCIsCntrl(ch))
4686 return E_INVALIDARG;
4687 }
4688 }
4689
4690 Utf8Str strOldValue; // empty
4691
4692 // locking note: we only hold the read lock briefly to look up the old value,
4693 // then release it and call the onExtraCanChange callbacks. There is a small
4694 // chance of a race insofar as the callback might be called twice if two callers
4695 // change the same key at the same time, but that's a much better solution
4696 // than the deadlock we had here before. The actual changing of the extradata
4697 // is then performed under the write lock and race-free.
4698
4699 // look up the old value first; if nothing has changed then we need not do anything
4700 {
4701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4702
4703 // For snapshots don't even think about allowing changes, extradata
4704 // is global for a machine, so there is nothing snapshot specific.
4705 if (i_isSnapshotMachine())
4706 return setError(VBOX_E_INVALID_VM_STATE,
4707 tr("Cannot set extradata for a snapshot"));
4708
4709 // check if the right IMachine instance is used
4710 if (mData->mRegistered && !i_isSessionMachine())
4711 return setError(VBOX_E_INVALID_VM_STATE,
4712 tr("Cannot set extradata for an immutable machine"));
4713
4714 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4715 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4716 strOldValue = it->second;
4717 }
4718
4719 bool fChanged;
4720 if ((fChanged = (strOldValue != aValue)))
4721 {
4722 // ask for permission from all listeners outside the locks;
4723 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4724 // lock to copy the list of callbacks to invoke
4725 Bstr error;
4726 Bstr bstrValue(aValue);
4727
4728 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4729 {
4730 const char *sep = error.isEmpty() ? "" : ": ";
4731 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4732 return setError(E_ACCESSDENIED,
4733 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4734 aKey.c_str(),
4735 aValue.c_str(),
4736 sep,
4737 error.raw());
4738 }
4739
4740 // data is changing and change not vetoed: then write it out under the lock
4741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 if (aValue.isEmpty())
4744 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4745 else
4746 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4747 // creates a new key if needed
4748
4749 bool fNeedsGlobalSaveSettings = false;
4750 // This saving of settings is tricky: there is no "old state" for the
4751 // extradata items at all (unlike all other settings), so the old/new
4752 // settings comparison would give a wrong result!
4753 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4754
4755 if (fNeedsGlobalSaveSettings)
4756 {
4757 // save the global settings; for that we should hold only the VirtualBox lock
4758 alock.release();
4759 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4760 mParent->i_saveSettings();
4761 }
4762 }
4763
4764 // fire notification outside the lock
4765 if (fChanged)
4766 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4767
4768 return S_OK;
4769}
4770
4771HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4772{
4773 aProgress = NULL;
4774 NOREF(aSettingsFilePath);
4775 ReturnComNotImplemented();
4776}
4777
4778HRESULT Machine::saveSettings()
4779{
4780 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4781
4782 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4783 if (FAILED(rc)) return rc;
4784
4785 /* the settings file path may never be null */
4786 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4787
4788 /* save all VM data excluding snapshots */
4789 bool fNeedsGlobalSaveSettings = false;
4790 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4791 mlock.release();
4792
4793 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4794 {
4795 // save the global settings; for that we should hold only the VirtualBox lock
4796 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4797 rc = mParent->i_saveSettings();
4798 }
4799
4800 return rc;
4801}
4802
4803
4804HRESULT Machine::discardSettings()
4805{
4806 /*
4807 * We need to take the machine list lock here as well as the machine one
4808 * or we'll get into trouble should any media stuff require rolling back.
4809 *
4810 * Details:
4811 *
4812 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4813 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4814 * 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]
4815 * 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
4816 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4817 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4818 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4819 * 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
4820 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4821 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4822 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4823 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4824 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4825 * 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]
4826 * 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] (*)
4827 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4828 * 0:005> k
4829 * # Child-SP RetAddr Call Site
4830 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4831 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4832 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4833 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4834 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4835 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4836 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4837 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4838 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4839 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4840 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4841 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4842 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4843 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4844 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4845 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4846 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4847 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4848 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4849 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4850 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4851 *
4852 */
4853 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4855
4856 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4857 if (FAILED(rc)) return rc;
4858
4859 /*
4860 * during this rollback, the session will be notified if data has
4861 * been actually changed
4862 */
4863 i_rollback(true /* aNotify */);
4864
4865 return S_OK;
4866}
4867
4868/** @note Locks objects! */
4869HRESULT Machine::unregister(AutoCaller &autoCaller,
4870 CleanupMode_T aCleanupMode,
4871 std::vector<ComPtr<IMedium> > &aMedia)
4872{
4873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4874
4875 Guid id(i_getId());
4876
4877 if (mData->mSession.mState != SessionState_Unlocked)
4878 return setError(VBOX_E_INVALID_OBJECT_STATE,
4879 tr("Cannot unregister the machine '%s' while it is locked"),
4880 mUserData->s.strName.c_str());
4881
4882 // wait for state dependents to drop to zero
4883 i_ensureNoStateDependencies();
4884
4885 if (!mData->mAccessible)
4886 {
4887 // inaccessible machines can only be unregistered; uninitialize ourselves
4888 // here because currently there may be no unregistered that are inaccessible
4889 // (this state combination is not supported). Note releasing the caller and
4890 // leaving the lock before calling uninit()
4891 alock.release();
4892 autoCaller.release();
4893
4894 uninit();
4895
4896 mParent->i_unregisterMachine(this, id);
4897 // calls VirtualBox::i_saveSettings()
4898
4899 return S_OK;
4900 }
4901
4902 HRESULT rc = S_OK;
4903 mData->llFilesToDelete.clear();
4904
4905 if (!mSSData->strStateFilePath.isEmpty())
4906 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4907
4908 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4909 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4910 mData->llFilesToDelete.push_back(strNVRAMFile);
4911
4912 // This list collects the medium objects from all medium attachments
4913 // which we will detach from the machine and its snapshots, in a specific
4914 // order which allows for closing all media without getting "media in use"
4915 // errors, simply by going through the list from the front to the back:
4916 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4917 // and must be closed before the parent media from the snapshots, or closing the parents
4918 // will fail because they still have children);
4919 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4920 // the root ("first") snapshot of the machine.
4921 MediaList llMedia;
4922
4923 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4924 && mMediumAttachments->size()
4925 )
4926 {
4927 // we have media attachments: detach them all and add the Medium objects to our list
4928 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4929 }
4930
4931 if (mData->mFirstSnapshot)
4932 {
4933 // add the media from the medium attachments of the snapshots to llMedia
4934 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4935 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4936 // into the children first
4937
4938 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4939 MachineState_T oldState = mData->mMachineState;
4940 mData->mMachineState = MachineState_DeletingSnapshot;
4941
4942 // make a copy of the first snapshot reference so the refcount does not
4943 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4944 // (would hang due to the AutoCaller voodoo)
4945 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4946
4947 // GO!
4948 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4949
4950 mData->mMachineState = oldState;
4951 }
4952
4953 if (FAILED(rc))
4954 {
4955 i_rollbackMedia();
4956 return rc;
4957 }
4958
4959 // commit all the media changes made above
4960 i_commitMedia();
4961
4962 mData->mRegistered = false;
4963
4964 // machine lock no longer needed
4965 alock.release();
4966
4967 /* Make sure that the settings of the current VM are not saved, because
4968 * they are rather crippled at this point to meet the cleanup expectations
4969 * and there's no point destroying the VM config on disk just because. */
4970 mParent->i_unmarkRegistryModified(id);
4971
4972 // return media to caller
4973 aMedia.resize(llMedia.size());
4974 size_t i = 0;
4975 for (MediaList::const_iterator
4976 it = llMedia.begin();
4977 it != llMedia.end();
4978 ++it, ++i)
4979 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4980
4981 mParent->i_unregisterMachine(this, id);
4982 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4983
4984 return S_OK;
4985}
4986
4987/**
4988 * Task record for deleting a machine config.
4989 */
4990class Machine::DeleteConfigTask
4991 : public Machine::Task
4992{
4993public:
4994 DeleteConfigTask(Machine *m,
4995 Progress *p,
4996 const Utf8Str &t,
4997 const RTCList<ComPtr<IMedium> > &llMediums,
4998 const StringsList &llFilesToDelete)
4999 : Task(m, p, t),
5000 m_llMediums(llMediums),
5001 m_llFilesToDelete(llFilesToDelete)
5002 {}
5003
5004private:
5005 void handler()
5006 {
5007 try
5008 {
5009 m_pMachine->i_deleteConfigHandler(*this);
5010 }
5011 catch (...)
5012 {
5013 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5014 }
5015 }
5016
5017 RTCList<ComPtr<IMedium> > m_llMediums;
5018 StringsList m_llFilesToDelete;
5019
5020 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5021};
5022
5023/**
5024 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5025 * SessionMachine::taskHandler().
5026 *
5027 * @note Locks this object for writing.
5028 *
5029 * @param task
5030 * @return
5031 */
5032void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5033{
5034 LogFlowThisFuncEnter();
5035
5036 AutoCaller autoCaller(this);
5037 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5038 if (FAILED(autoCaller.rc()))
5039 {
5040 /* we might have been uninitialized because the session was accidentally
5041 * closed by the client, so don't assert */
5042 HRESULT rc = setError(E_FAIL,
5043 tr("The session has been accidentally closed"));
5044 task.m_pProgress->i_notifyComplete(rc);
5045 LogFlowThisFuncLeave();
5046 return;
5047 }
5048
5049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5050
5051 HRESULT rc = S_OK;
5052
5053 try
5054 {
5055 ULONG uLogHistoryCount = 3;
5056 ComPtr<ISystemProperties> systemProperties;
5057 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5058 if (FAILED(rc)) throw rc;
5059
5060 if (!systemProperties.isNull())
5061 {
5062 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5063 if (FAILED(rc)) throw rc;
5064 }
5065
5066 MachineState_T oldState = mData->mMachineState;
5067 i_setMachineState(MachineState_SettingUp);
5068 alock.release();
5069 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5070 {
5071 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5072 {
5073 AutoCaller mac(pMedium);
5074 if (FAILED(mac.rc())) throw mac.rc();
5075 Utf8Str strLocation = pMedium->i_getLocationFull();
5076 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5077 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5078 if (FAILED(rc)) throw rc;
5079 }
5080 if (pMedium->i_isMediumFormatFile())
5081 {
5082 ComPtr<IProgress> pProgress2;
5083 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5084 if (FAILED(rc)) throw rc;
5085 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5086 if (FAILED(rc)) throw rc;
5087 }
5088
5089 /* Close the medium, deliberately without checking the return
5090 * code, and without leaving any trace in the error info, as
5091 * a failure here is a very minor issue, which shouldn't happen
5092 * as above we even managed to delete the medium. */
5093 {
5094 ErrorInfoKeeper eik;
5095 pMedium->Close();
5096 }
5097 }
5098 i_setMachineState(oldState);
5099 alock.acquire();
5100
5101 // delete the files pushed on the task list by Machine::Delete()
5102 // (this includes saved states of the machine and snapshots and
5103 // medium storage files from the IMedium list passed in, and the
5104 // machine XML file)
5105 for (StringsList::const_iterator
5106 it = task.m_llFilesToDelete.begin();
5107 it != task.m_llFilesToDelete.end();
5108 ++it)
5109 {
5110 const Utf8Str &strFile = *it;
5111 LogFunc(("Deleting file %s\n", strFile.c_str()));
5112 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5113 if (FAILED(rc)) throw rc;
5114
5115 int vrc = RTFileDelete(strFile.c_str());
5116 if (RT_FAILURE(vrc))
5117 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5118 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5119 }
5120
5121 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5122 if (FAILED(rc)) throw rc;
5123
5124 /* delete the settings only when the file actually exists */
5125 if (mData->pMachineConfigFile->fileExists())
5126 {
5127 /* Delete any backup or uncommitted XML files. Ignore failures.
5128 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5129 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5130 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5131 RTFileDelete(otherXml.c_str());
5132 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5133 RTFileDelete(otherXml.c_str());
5134
5135 /* delete the Logs folder, nothing important should be left
5136 * there (we don't check for errors because the user might have
5137 * some private files there that we don't want to delete) */
5138 Utf8Str logFolder;
5139 getLogFolder(logFolder);
5140 Assert(logFolder.length());
5141 if (RTDirExists(logFolder.c_str()))
5142 {
5143 /* Delete all VBox.log[.N] files from the Logs folder
5144 * (this must be in sync with the rotation logic in
5145 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5146 * files that may have been created by the GUI. */
5147 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5148 logFolder.c_str(), RTPATH_DELIMITER);
5149 RTFileDelete(log.c_str());
5150 log = Utf8StrFmt("%s%cVBox.png",
5151 logFolder.c_str(), RTPATH_DELIMITER);
5152 RTFileDelete(log.c_str());
5153 for (int i = uLogHistoryCount; i > 0; i--)
5154 {
5155 log = Utf8StrFmt("%s%cVBox.log.%d",
5156 logFolder.c_str(), RTPATH_DELIMITER, i);
5157 RTFileDelete(log.c_str());
5158 log = Utf8StrFmt("%s%cVBox.png.%d",
5159 logFolder.c_str(), RTPATH_DELIMITER, i);
5160 RTFileDelete(log.c_str());
5161 }
5162#if defined(RT_OS_WINDOWS)
5163 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5164 RTFileDelete(log.c_str());
5165 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5166 RTFileDelete(log.c_str());
5167#endif
5168
5169 RTDirRemove(logFolder.c_str());
5170 }
5171
5172 /* delete the Snapshots folder, nothing important should be left
5173 * there (we don't check for errors because the user might have
5174 * some private files there that we don't want to delete) */
5175 Utf8Str strFullSnapshotFolder;
5176 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5177 Assert(!strFullSnapshotFolder.isEmpty());
5178 if (RTDirExists(strFullSnapshotFolder.c_str()))
5179 RTDirRemove(strFullSnapshotFolder.c_str());
5180
5181 // delete the directory that contains the settings file, but only
5182 // if it matches the VM name
5183 Utf8Str settingsDir;
5184 if (i_isInOwnDir(&settingsDir))
5185 RTDirRemove(settingsDir.c_str());
5186 }
5187
5188 alock.release();
5189
5190 mParent->i_saveModifiedRegistries();
5191 }
5192 catch (HRESULT aRC) { rc = aRC; }
5193
5194 task.m_pProgress->i_notifyComplete(rc);
5195
5196 LogFlowThisFuncLeave();
5197}
5198
5199HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5200{
5201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5202
5203 HRESULT rc = i_checkStateDependency(MutableStateDep);
5204 if (FAILED(rc)) return rc;
5205
5206 if (mData->mRegistered)
5207 return setError(VBOX_E_INVALID_VM_STATE,
5208 tr("Cannot delete settings of a registered machine"));
5209
5210 // collect files to delete
5211 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5212 // machine config file
5213 if (mData->pMachineConfigFile->fileExists())
5214 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5215 // backup of machine config file
5216 Utf8Str strTmp(mData->m_strConfigFileFull);
5217 strTmp.append("-prev");
5218 if (RTFileExists(strTmp.c_str()))
5219 llFilesToDelete.push_back(strTmp);
5220
5221 RTCList<ComPtr<IMedium> > llMediums;
5222 for (size_t i = 0; i < aMedia.size(); ++i)
5223 {
5224 IMedium *pIMedium(aMedia[i]);
5225 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5226 if (pMedium.isNull())
5227 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5228 SafeArray<BSTR> ids;
5229 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5230 if (FAILED(rc)) return rc;
5231 /* At this point the medium should not have any back references
5232 * anymore. If it has it is attached to another VM and *must* not
5233 * deleted. */
5234 if (ids.size() < 1)
5235 llMediums.append(pMedium);
5236 }
5237
5238 ComObjPtr<Progress> pProgress;
5239 pProgress.createObject();
5240 rc = pProgress->init(i_getVirtualBox(),
5241 static_cast<IMachine*>(this) /* aInitiator */,
5242 tr("Deleting files"),
5243 true /* fCancellable */,
5244 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5245 tr("Collecting file inventory"));
5246 if (FAILED(rc))
5247 return rc;
5248
5249 /* create and start the task on a separate thread (note that it will not
5250 * start working until we release alock) */
5251 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5252 rc = pTask->createThread();
5253 pTask = NULL;
5254 if (FAILED(rc))
5255 return rc;
5256
5257 pProgress.queryInterfaceTo(aProgress.asOutParam());
5258
5259 LogFlowFuncLeave();
5260
5261 return S_OK;
5262}
5263
5264HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5265{
5266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5267
5268 ComObjPtr<Snapshot> pSnapshot;
5269 HRESULT rc;
5270
5271 if (aNameOrId.isEmpty())
5272 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5273 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5274 else
5275 {
5276 Guid uuid(aNameOrId);
5277 if (uuid.isValid())
5278 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5279 else
5280 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5281 }
5282 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5283
5284 return rc;
5285}
5286
5287HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5288 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5289{
5290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5291
5292 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5293 if (FAILED(rc)) return rc;
5294
5295 ComObjPtr<SharedFolder> sharedFolder;
5296 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5297 if (SUCCEEDED(rc))
5298 return setError(VBOX_E_OBJECT_IN_USE,
5299 tr("Shared folder named '%s' already exists"),
5300 aName.c_str());
5301
5302 sharedFolder.createObject();
5303 rc = sharedFolder->init(i_getMachine(),
5304 aName,
5305 aHostPath,
5306 !!aWritable,
5307 !!aAutomount,
5308 aAutoMountPoint,
5309 true /* fFailOnError */);
5310 if (FAILED(rc)) return rc;
5311
5312 i_setModified(IsModified_SharedFolders);
5313 mHWData.backup();
5314 mHWData->mSharedFolders.push_back(sharedFolder);
5315
5316 /* inform the direct session if any */
5317 alock.release();
5318 i_onSharedFolderChange();
5319
5320 return S_OK;
5321}
5322
5323HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5324{
5325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5326
5327 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5328 if (FAILED(rc)) return rc;
5329
5330 ComObjPtr<SharedFolder> sharedFolder;
5331 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5332 if (FAILED(rc)) return rc;
5333
5334 i_setModified(IsModified_SharedFolders);
5335 mHWData.backup();
5336 mHWData->mSharedFolders.remove(sharedFolder);
5337
5338 /* inform the direct session if any */
5339 alock.release();
5340 i_onSharedFolderChange();
5341
5342 return S_OK;
5343}
5344
5345HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5346{
5347 /* start with No */
5348 *aCanShow = FALSE;
5349
5350 ComPtr<IInternalSessionControl> directControl;
5351 {
5352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5353
5354 if (mData->mSession.mState != SessionState_Locked)
5355 return setError(VBOX_E_INVALID_VM_STATE,
5356 tr("Machine is not locked for session (session state: %s)"),
5357 Global::stringifySessionState(mData->mSession.mState));
5358
5359 if (mData->mSession.mLockType == LockType_VM)
5360 directControl = mData->mSession.mDirectControl;
5361 }
5362
5363 /* ignore calls made after #OnSessionEnd() is called */
5364 if (!directControl)
5365 return S_OK;
5366
5367 LONG64 dummy;
5368 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5369}
5370
5371HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5372{
5373 ComPtr<IInternalSessionControl> directControl;
5374 {
5375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5376
5377 if (mData->mSession.mState != SessionState_Locked)
5378 return setError(E_FAIL,
5379 tr("Machine is not locked for session (session state: %s)"),
5380 Global::stringifySessionState(mData->mSession.mState));
5381
5382 if (mData->mSession.mLockType == LockType_VM)
5383 directControl = mData->mSession.mDirectControl;
5384 }
5385
5386 /* ignore calls made after #OnSessionEnd() is called */
5387 if (!directControl)
5388 return S_OK;
5389
5390 BOOL dummy;
5391 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5392}
5393
5394#ifdef VBOX_WITH_GUEST_PROPS
5395/**
5396 * Look up a guest property in VBoxSVC's internal structures.
5397 */
5398HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5399 com::Utf8Str &aValue,
5400 LONG64 *aTimestamp,
5401 com::Utf8Str &aFlags) const
5402{
5403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5404
5405 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5406 if (it != mHWData->mGuestProperties.end())
5407 {
5408 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5409 aValue = it->second.strValue;
5410 *aTimestamp = it->second.mTimestamp;
5411 GuestPropWriteFlags(it->second.mFlags, szFlags);
5412 aFlags = Utf8Str(szFlags);
5413 }
5414
5415 return S_OK;
5416}
5417
5418/**
5419 * Query the VM that a guest property belongs to for the property.
5420 * @returns E_ACCESSDENIED if the VM process is not available or not
5421 * currently handling queries and the lookup should then be done in
5422 * VBoxSVC.
5423 */
5424HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5425 com::Utf8Str &aValue,
5426 LONG64 *aTimestamp,
5427 com::Utf8Str &aFlags) const
5428{
5429 HRESULT rc = S_OK;
5430 Bstr bstrValue;
5431 Bstr bstrFlags;
5432
5433 ComPtr<IInternalSessionControl> directControl;
5434 {
5435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5436 if (mData->mSession.mLockType == LockType_VM)
5437 directControl = mData->mSession.mDirectControl;
5438 }
5439
5440 /* ignore calls made after #OnSessionEnd() is called */
5441 if (!directControl)
5442 rc = E_ACCESSDENIED;
5443 else
5444 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5445 0 /* accessMode */,
5446 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5447
5448 aValue = bstrValue;
5449 aFlags = bstrFlags;
5450
5451 return rc;
5452}
5453#endif // VBOX_WITH_GUEST_PROPS
5454
5455HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5456 com::Utf8Str &aValue,
5457 LONG64 *aTimestamp,
5458 com::Utf8Str &aFlags)
5459{
5460#ifndef VBOX_WITH_GUEST_PROPS
5461 ReturnComNotImplemented();
5462#else // VBOX_WITH_GUEST_PROPS
5463
5464 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5465
5466 if (rc == E_ACCESSDENIED)
5467 /* The VM is not running or the service is not (yet) accessible */
5468 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5469 return rc;
5470#endif // VBOX_WITH_GUEST_PROPS
5471}
5472
5473HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5474{
5475 LONG64 dummyTimestamp;
5476 com::Utf8Str dummyFlags;
5477 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5478 return rc;
5479
5480}
5481HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5482{
5483 com::Utf8Str dummyFlags;
5484 com::Utf8Str dummyValue;
5485 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5486 return rc;
5487}
5488
5489#ifdef VBOX_WITH_GUEST_PROPS
5490/**
5491 * Set a guest property in VBoxSVC's internal structures.
5492 */
5493HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5494 const com::Utf8Str &aFlags, bool fDelete)
5495{
5496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5497 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5498 if (FAILED(rc)) return rc;
5499
5500 try
5501 {
5502 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5503 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5504 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5505
5506 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5507 if (it == mHWData->mGuestProperties.end())
5508 {
5509 if (!fDelete)
5510 {
5511 i_setModified(IsModified_MachineData);
5512 mHWData.backupEx();
5513
5514 RTTIMESPEC time;
5515 HWData::GuestProperty prop;
5516 prop.strValue = Bstr(aValue).raw();
5517 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5518 prop.mFlags = fFlags;
5519 mHWData->mGuestProperties[aName] = prop;
5520 }
5521 }
5522 else
5523 {
5524 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5525 {
5526 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5527 }
5528 else
5529 {
5530 i_setModified(IsModified_MachineData);
5531 mHWData.backupEx();
5532
5533 /* The backupEx() operation invalidates our iterator,
5534 * so get a new one. */
5535 it = mHWData->mGuestProperties.find(aName);
5536 Assert(it != mHWData->mGuestProperties.end());
5537
5538 if (!fDelete)
5539 {
5540 RTTIMESPEC time;
5541 it->second.strValue = aValue;
5542 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5543 it->second.mFlags = fFlags;
5544 }
5545 else
5546 mHWData->mGuestProperties.erase(it);
5547 }
5548 }
5549
5550 if (SUCCEEDED(rc))
5551 {
5552 alock.release();
5553
5554 mParent->i_onGuestPropertyChange(mData->mUuid,
5555 Bstr(aName).raw(),
5556 Bstr(aValue).raw(),
5557 Bstr(aFlags).raw());
5558 }
5559 }
5560 catch (std::bad_alloc &)
5561 {
5562 rc = E_OUTOFMEMORY;
5563 }
5564
5565 return rc;
5566}
5567
5568/**
5569 * Set a property on the VM that that property belongs to.
5570 * @returns E_ACCESSDENIED if the VM process is not available or not
5571 * currently handling queries and the setting should then be done in
5572 * VBoxSVC.
5573 */
5574HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5575 const com::Utf8Str &aFlags, bool fDelete)
5576{
5577 HRESULT rc;
5578
5579 try
5580 {
5581 ComPtr<IInternalSessionControl> directControl;
5582 {
5583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5584 if (mData->mSession.mLockType == LockType_VM)
5585 directControl = mData->mSession.mDirectControl;
5586 }
5587
5588 Bstr dummy1; /* will not be changed (setter) */
5589 Bstr dummy2; /* will not be changed (setter) */
5590 LONG64 dummy64;
5591 if (!directControl)
5592 rc = E_ACCESSDENIED;
5593 else
5594 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5595 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5596 fDelete ? 2 : 1 /* accessMode */,
5597 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5598 }
5599 catch (std::bad_alloc &)
5600 {
5601 rc = E_OUTOFMEMORY;
5602 }
5603
5604 return rc;
5605}
5606#endif // VBOX_WITH_GUEST_PROPS
5607
5608HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5609 const com::Utf8Str &aFlags)
5610{
5611#ifndef VBOX_WITH_GUEST_PROPS
5612 ReturnComNotImplemented();
5613#else // VBOX_WITH_GUEST_PROPS
5614 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5615 if (rc == E_ACCESSDENIED)
5616 /* The VM is not running or the service is not (yet) accessible */
5617 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5618 return rc;
5619#endif // VBOX_WITH_GUEST_PROPS
5620}
5621
5622HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5623{
5624 return setGuestProperty(aProperty, aValue, "");
5625}
5626
5627HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5628{
5629#ifndef VBOX_WITH_GUEST_PROPS
5630 ReturnComNotImplemented();
5631#else // VBOX_WITH_GUEST_PROPS
5632 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5633 if (rc == E_ACCESSDENIED)
5634 /* The VM is not running or the service is not (yet) accessible */
5635 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5636 return rc;
5637#endif // VBOX_WITH_GUEST_PROPS
5638}
5639
5640#ifdef VBOX_WITH_GUEST_PROPS
5641/**
5642 * Enumerate the guest properties in VBoxSVC's internal structures.
5643 */
5644HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5645 std::vector<com::Utf8Str> &aNames,
5646 std::vector<com::Utf8Str> &aValues,
5647 std::vector<LONG64> &aTimestamps,
5648 std::vector<com::Utf8Str> &aFlags)
5649{
5650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5651 Utf8Str strPatterns(aPatterns);
5652
5653 /*
5654 * Look for matching patterns and build up a list.
5655 */
5656 HWData::GuestPropertyMap propMap;
5657 for (HWData::GuestPropertyMap::const_iterator
5658 it = mHWData->mGuestProperties.begin();
5659 it != mHWData->mGuestProperties.end();
5660 ++it)
5661 {
5662 if ( strPatterns.isEmpty()
5663 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5664 RTSTR_MAX,
5665 it->first.c_str(),
5666 RTSTR_MAX,
5667 NULL)
5668 )
5669 propMap.insert(*it);
5670 }
5671
5672 alock.release();
5673
5674 /*
5675 * And build up the arrays for returning the property information.
5676 */
5677 size_t cEntries = propMap.size();
5678
5679 aNames.resize(cEntries);
5680 aValues.resize(cEntries);
5681 aTimestamps.resize(cEntries);
5682 aFlags.resize(cEntries);
5683
5684 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5685 size_t i = 0;
5686 for (HWData::GuestPropertyMap::const_iterator
5687 it = propMap.begin();
5688 it != propMap.end();
5689 ++it, ++i)
5690 {
5691 aNames[i] = it->first;
5692 aValues[i] = it->second.strValue;
5693 aTimestamps[i] = it->second.mTimestamp;
5694 GuestPropWriteFlags(it->second.mFlags, szFlags);
5695 aFlags[i] = Utf8Str(szFlags);
5696 }
5697
5698 return S_OK;
5699}
5700
5701/**
5702 * Enumerate the properties managed by a VM.
5703 * @returns E_ACCESSDENIED if the VM process is not available or not
5704 * currently handling queries and the setting should then be done in
5705 * VBoxSVC.
5706 */
5707HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5708 std::vector<com::Utf8Str> &aNames,
5709 std::vector<com::Utf8Str> &aValues,
5710 std::vector<LONG64> &aTimestamps,
5711 std::vector<com::Utf8Str> &aFlags)
5712{
5713 HRESULT rc;
5714 ComPtr<IInternalSessionControl> directControl;
5715 {
5716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5717 if (mData->mSession.mLockType == LockType_VM)
5718 directControl = mData->mSession.mDirectControl;
5719 }
5720
5721 com::SafeArray<BSTR> bNames;
5722 com::SafeArray<BSTR> bValues;
5723 com::SafeArray<LONG64> bTimestamps;
5724 com::SafeArray<BSTR> bFlags;
5725
5726 if (!directControl)
5727 rc = E_ACCESSDENIED;
5728 else
5729 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5730 ComSafeArrayAsOutParam(bNames),
5731 ComSafeArrayAsOutParam(bValues),
5732 ComSafeArrayAsOutParam(bTimestamps),
5733 ComSafeArrayAsOutParam(bFlags));
5734 size_t i;
5735 aNames.resize(bNames.size());
5736 for (i = 0; i < bNames.size(); ++i)
5737 aNames[i] = Utf8Str(bNames[i]);
5738 aValues.resize(bValues.size());
5739 for (i = 0; i < bValues.size(); ++i)
5740 aValues[i] = Utf8Str(bValues[i]);
5741 aTimestamps.resize(bTimestamps.size());
5742 for (i = 0; i < bTimestamps.size(); ++i)
5743 aTimestamps[i] = bTimestamps[i];
5744 aFlags.resize(bFlags.size());
5745 for (i = 0; i < bFlags.size(); ++i)
5746 aFlags[i] = Utf8Str(bFlags[i]);
5747
5748 return rc;
5749}
5750#endif // VBOX_WITH_GUEST_PROPS
5751HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5752 std::vector<com::Utf8Str> &aNames,
5753 std::vector<com::Utf8Str> &aValues,
5754 std::vector<LONG64> &aTimestamps,
5755 std::vector<com::Utf8Str> &aFlags)
5756{
5757#ifndef VBOX_WITH_GUEST_PROPS
5758 ReturnComNotImplemented();
5759#else // VBOX_WITH_GUEST_PROPS
5760
5761 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5762
5763 if (rc == E_ACCESSDENIED)
5764 /* The VM is not running or the service is not (yet) accessible */
5765 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5766 return rc;
5767#endif // VBOX_WITH_GUEST_PROPS
5768}
5769
5770HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5771 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5772{
5773 MediumAttachmentList atts;
5774
5775 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5776 if (FAILED(rc)) return rc;
5777
5778 aMediumAttachments.resize(atts.size());
5779 size_t i = 0;
5780 for (MediumAttachmentList::const_iterator
5781 it = atts.begin();
5782 it != atts.end();
5783 ++it, ++i)
5784 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5785
5786 return S_OK;
5787}
5788
5789HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5790 LONG aControllerPort,
5791 LONG aDevice,
5792 ComPtr<IMediumAttachment> &aAttachment)
5793{
5794 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5795 aName.c_str(), aControllerPort, aDevice));
5796
5797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5798
5799 aAttachment = NULL;
5800
5801 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5802 aName,
5803 aControllerPort,
5804 aDevice);
5805 if (pAttach.isNull())
5806 return setError(VBOX_E_OBJECT_NOT_FOUND,
5807 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5808 aDevice, aControllerPort, aName.c_str());
5809
5810 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5811
5812 return S_OK;
5813}
5814
5815
5816HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5817 StorageBus_T aConnectionType,
5818 ComPtr<IStorageController> &aController)
5819{
5820 if ( (aConnectionType <= StorageBus_Null)
5821 || (aConnectionType > StorageBus_VirtioSCSI))
5822 return setError(E_INVALIDARG,
5823 tr("Invalid connection type: %d"),
5824 aConnectionType);
5825
5826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5827
5828 HRESULT rc = i_checkStateDependency(MutableStateDep);
5829 if (FAILED(rc)) return rc;
5830
5831 /* try to find one with the name first. */
5832 ComObjPtr<StorageController> ctrl;
5833
5834 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5835 if (SUCCEEDED(rc))
5836 return setError(VBOX_E_OBJECT_IN_USE,
5837 tr("Storage controller named '%s' already exists"),
5838 aName.c_str());
5839
5840 ctrl.createObject();
5841
5842 /* get a new instance number for the storage controller */
5843 ULONG ulInstance = 0;
5844 bool fBootable = true;
5845 for (StorageControllerList::const_iterator
5846 it = mStorageControllers->begin();
5847 it != mStorageControllers->end();
5848 ++it)
5849 {
5850 if ((*it)->i_getStorageBus() == aConnectionType)
5851 {
5852 ULONG ulCurInst = (*it)->i_getInstance();
5853
5854 if (ulCurInst >= ulInstance)
5855 ulInstance = ulCurInst + 1;
5856
5857 /* Only one controller of each type can be marked as bootable. */
5858 if ((*it)->i_getBootable())
5859 fBootable = false;
5860 }
5861 }
5862
5863 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5864 if (FAILED(rc)) return rc;
5865
5866 i_setModified(IsModified_Storage);
5867 mStorageControllers.backup();
5868 mStorageControllers->push_back(ctrl);
5869
5870 ctrl.queryInterfaceTo(aController.asOutParam());
5871
5872 /* inform the direct session if any */
5873 alock.release();
5874 i_onStorageControllerChange(i_getId(), aName);
5875
5876 return S_OK;
5877}
5878
5879HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5880 ComPtr<IStorageController> &aStorageController)
5881{
5882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5883
5884 ComObjPtr<StorageController> ctrl;
5885
5886 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5887 if (SUCCEEDED(rc))
5888 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5889
5890 return rc;
5891}
5892
5893HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5894 ULONG aInstance,
5895 ComPtr<IStorageController> &aStorageController)
5896{
5897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5898
5899 for (StorageControllerList::const_iterator
5900 it = mStorageControllers->begin();
5901 it != mStorageControllers->end();
5902 ++it)
5903 {
5904 if ( (*it)->i_getStorageBus() == aConnectionType
5905 && (*it)->i_getInstance() == aInstance)
5906 {
5907 (*it).queryInterfaceTo(aStorageController.asOutParam());
5908 return S_OK;
5909 }
5910 }
5911
5912 return setError(VBOX_E_OBJECT_NOT_FOUND,
5913 tr("Could not find a storage controller with instance number '%lu'"),
5914 aInstance);
5915}
5916
5917HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5918{
5919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5920
5921 HRESULT rc = i_checkStateDependency(MutableStateDep);
5922 if (FAILED(rc)) return rc;
5923
5924 ComObjPtr<StorageController> ctrl;
5925
5926 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5927 if (SUCCEEDED(rc))
5928 {
5929 /* Ensure that only one controller of each type is marked as bootable. */
5930 if (aBootable == TRUE)
5931 {
5932 for (StorageControllerList::const_iterator
5933 it = mStorageControllers->begin();
5934 it != mStorageControllers->end();
5935 ++it)
5936 {
5937 ComObjPtr<StorageController> aCtrl = (*it);
5938
5939 if ( (aCtrl->i_getName() != aName)
5940 && aCtrl->i_getBootable() == TRUE
5941 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5942 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5943 {
5944 aCtrl->i_setBootable(FALSE);
5945 break;
5946 }
5947 }
5948 }
5949
5950 if (SUCCEEDED(rc))
5951 {
5952 ctrl->i_setBootable(aBootable);
5953 i_setModified(IsModified_Storage);
5954 }
5955 }
5956
5957 if (SUCCEEDED(rc))
5958 {
5959 /* inform the direct session if any */
5960 alock.release();
5961 i_onStorageControllerChange(i_getId(), aName);
5962 }
5963
5964 return rc;
5965}
5966
5967HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5968{
5969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5970
5971 HRESULT rc = i_checkStateDependency(MutableStateDep);
5972 if (FAILED(rc)) return rc;
5973
5974 ComObjPtr<StorageController> ctrl;
5975 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5976 if (FAILED(rc)) return rc;
5977
5978 MediumAttachmentList llDetachedAttachments;
5979 {
5980 /* find all attached devices to the appropriate storage controller and detach them all */
5981 // make a temporary list because detachDevice invalidates iterators into
5982 // mMediumAttachments
5983 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5984
5985 for (MediumAttachmentList::const_iterator
5986 it = llAttachments2.begin();
5987 it != llAttachments2.end();
5988 ++it)
5989 {
5990 MediumAttachment *pAttachTemp = *it;
5991
5992 AutoCaller localAutoCaller(pAttachTemp);
5993 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5994
5995 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5996
5997 if (pAttachTemp->i_getControllerName() == aName)
5998 {
5999 llDetachedAttachments.push_back(pAttachTemp);
6000 rc = i_detachDevice(pAttachTemp, alock, NULL);
6001 if (FAILED(rc)) return rc;
6002 }
6003 }
6004 }
6005
6006 /* send event about detached devices before removing parent controller */
6007 for (MediumAttachmentList::const_iterator
6008 it = llDetachedAttachments.begin();
6009 it != llDetachedAttachments.end();
6010 ++it)
6011 {
6012 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6013 }
6014
6015 /* We can remove it now. */
6016 i_setModified(IsModified_Storage);
6017 mStorageControllers.backup();
6018
6019 ctrl->i_unshare();
6020
6021 mStorageControllers->remove(ctrl);
6022
6023 /* inform the direct session if any */
6024 alock.release();
6025 i_onStorageControllerChange(i_getId(), aName);
6026
6027 return S_OK;
6028}
6029
6030HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6031 ComPtr<IUSBController> &aController)
6032{
6033 if ( (aType <= USBControllerType_Null)
6034 || (aType >= USBControllerType_Last))
6035 return setError(E_INVALIDARG,
6036 tr("Invalid USB controller type: %d"),
6037 aType);
6038
6039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6040
6041 HRESULT rc = i_checkStateDependency(MutableStateDep);
6042 if (FAILED(rc)) return rc;
6043
6044 /* try to find one with the same type first. */
6045 ComObjPtr<USBController> ctrl;
6046
6047 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6048 if (SUCCEEDED(rc))
6049 return setError(VBOX_E_OBJECT_IN_USE,
6050 tr("USB controller named '%s' already exists"),
6051 aName.c_str());
6052
6053 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6054 ULONG maxInstances;
6055 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6056 if (FAILED(rc))
6057 return rc;
6058
6059 ULONG cInstances = i_getUSBControllerCountByType(aType);
6060 if (cInstances >= maxInstances)
6061 return setError(E_INVALIDARG,
6062 tr("Too many USB controllers of this type"));
6063
6064 ctrl.createObject();
6065
6066 rc = ctrl->init(this, aName, aType);
6067 if (FAILED(rc)) return rc;
6068
6069 i_setModified(IsModified_USB);
6070 mUSBControllers.backup();
6071 mUSBControllers->push_back(ctrl);
6072
6073 ctrl.queryInterfaceTo(aController.asOutParam());
6074
6075 /* inform the direct session if any */
6076 alock.release();
6077 i_onUSBControllerChange();
6078
6079 return S_OK;
6080}
6081
6082HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6083{
6084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6085
6086 ComObjPtr<USBController> ctrl;
6087
6088 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6089 if (SUCCEEDED(rc))
6090 ctrl.queryInterfaceTo(aController.asOutParam());
6091
6092 return rc;
6093}
6094
6095HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6096 ULONG *aControllers)
6097{
6098 if ( (aType <= USBControllerType_Null)
6099 || (aType >= USBControllerType_Last))
6100 return setError(E_INVALIDARG,
6101 tr("Invalid USB controller type: %d"),
6102 aType);
6103
6104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6105
6106 ComObjPtr<USBController> ctrl;
6107
6108 *aControllers = i_getUSBControllerCountByType(aType);
6109
6110 return S_OK;
6111}
6112
6113HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6114{
6115
6116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6117
6118 HRESULT rc = i_checkStateDependency(MutableStateDep);
6119 if (FAILED(rc)) return rc;
6120
6121 ComObjPtr<USBController> ctrl;
6122 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6123 if (FAILED(rc)) return rc;
6124
6125 i_setModified(IsModified_USB);
6126 mUSBControllers.backup();
6127
6128 ctrl->i_unshare();
6129
6130 mUSBControllers->remove(ctrl);
6131
6132 /* inform the direct session if any */
6133 alock.release();
6134 i_onUSBControllerChange();
6135
6136 return S_OK;
6137}
6138
6139HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6140 ULONG *aOriginX,
6141 ULONG *aOriginY,
6142 ULONG *aWidth,
6143 ULONG *aHeight,
6144 BOOL *aEnabled)
6145{
6146 uint32_t u32OriginX= 0;
6147 uint32_t u32OriginY= 0;
6148 uint32_t u32Width = 0;
6149 uint32_t u32Height = 0;
6150 uint16_t u16Flags = 0;
6151
6152 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6153 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6154 if (RT_FAILURE(vrc))
6155 {
6156#ifdef RT_OS_WINDOWS
6157 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6158 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6159 * So just assign fEnable to TRUE again.
6160 * The right fix would be to change GUI API wrappers to make sure that parameters
6161 * are changed only if API succeeds.
6162 */
6163 *aEnabled = TRUE;
6164#endif
6165 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6166 tr("Saved guest size is not available (%Rrc)"),
6167 vrc);
6168 }
6169
6170 *aOriginX = u32OriginX;
6171 *aOriginY = u32OriginY;
6172 *aWidth = u32Width;
6173 *aHeight = u32Height;
6174 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6175
6176 return S_OK;
6177}
6178
6179HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6180 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6181{
6182 if (aScreenId != 0)
6183 return E_NOTIMPL;
6184
6185 if ( aBitmapFormat != BitmapFormat_BGR0
6186 && aBitmapFormat != BitmapFormat_BGRA
6187 && aBitmapFormat != BitmapFormat_RGBA
6188 && aBitmapFormat != BitmapFormat_PNG)
6189 return setError(E_NOTIMPL,
6190 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6191
6192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6193
6194 uint8_t *pu8Data = NULL;
6195 uint32_t cbData = 0;
6196 uint32_t u32Width = 0;
6197 uint32_t u32Height = 0;
6198
6199 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6200
6201 if (RT_FAILURE(vrc))
6202 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6203 tr("Saved thumbnail data is not available (%Rrc)"),
6204 vrc);
6205
6206 HRESULT hr = S_OK;
6207
6208 *aWidth = u32Width;
6209 *aHeight = u32Height;
6210
6211 if (cbData > 0)
6212 {
6213 /* Convert pixels to the format expected by the API caller. */
6214 if (aBitmapFormat == BitmapFormat_BGR0)
6215 {
6216 /* [0] B, [1] G, [2] R, [3] 0. */
6217 aData.resize(cbData);
6218 memcpy(&aData.front(), pu8Data, cbData);
6219 }
6220 else if (aBitmapFormat == BitmapFormat_BGRA)
6221 {
6222 /* [0] B, [1] G, [2] R, [3] A. */
6223 aData.resize(cbData);
6224 for (uint32_t i = 0; i < cbData; i += 4)
6225 {
6226 aData[i] = pu8Data[i];
6227 aData[i + 1] = pu8Data[i + 1];
6228 aData[i + 2] = pu8Data[i + 2];
6229 aData[i + 3] = 0xff;
6230 }
6231 }
6232 else if (aBitmapFormat == BitmapFormat_RGBA)
6233 {
6234 /* [0] R, [1] G, [2] B, [3] A. */
6235 aData.resize(cbData);
6236 for (uint32_t i = 0; i < cbData; i += 4)
6237 {
6238 aData[i] = pu8Data[i + 2];
6239 aData[i + 1] = pu8Data[i + 1];
6240 aData[i + 2] = pu8Data[i];
6241 aData[i + 3] = 0xff;
6242 }
6243 }
6244 else if (aBitmapFormat == BitmapFormat_PNG)
6245 {
6246 uint8_t *pu8PNG = NULL;
6247 uint32_t cbPNG = 0;
6248 uint32_t cxPNG = 0;
6249 uint32_t cyPNG = 0;
6250
6251 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6252
6253 if (RT_SUCCESS(vrc))
6254 {
6255 aData.resize(cbPNG);
6256 if (cbPNG)
6257 memcpy(&aData.front(), pu8PNG, cbPNG);
6258 }
6259 else
6260 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6261 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6262 vrc);
6263
6264 RTMemFree(pu8PNG);
6265 }
6266 }
6267
6268 freeSavedDisplayScreenshot(pu8Data);
6269
6270 return hr;
6271}
6272
6273HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6274 ULONG *aWidth,
6275 ULONG *aHeight,
6276 std::vector<BitmapFormat_T> &aBitmapFormats)
6277{
6278 if (aScreenId != 0)
6279 return E_NOTIMPL;
6280
6281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6282
6283 uint8_t *pu8Data = NULL;
6284 uint32_t cbData = 0;
6285 uint32_t u32Width = 0;
6286 uint32_t u32Height = 0;
6287
6288 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6289
6290 if (RT_FAILURE(vrc))
6291 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6292 tr("Saved screenshot data is not available (%Rrc)"),
6293 vrc);
6294
6295 *aWidth = u32Width;
6296 *aHeight = u32Height;
6297 aBitmapFormats.resize(1);
6298 aBitmapFormats[0] = BitmapFormat_PNG;
6299
6300 freeSavedDisplayScreenshot(pu8Data);
6301
6302 return S_OK;
6303}
6304
6305HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6306 BitmapFormat_T aBitmapFormat,
6307 ULONG *aWidth,
6308 ULONG *aHeight,
6309 std::vector<BYTE> &aData)
6310{
6311 if (aScreenId != 0)
6312 return E_NOTIMPL;
6313
6314 if (aBitmapFormat != BitmapFormat_PNG)
6315 return E_NOTIMPL;
6316
6317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6318
6319 uint8_t *pu8Data = NULL;
6320 uint32_t cbData = 0;
6321 uint32_t u32Width = 0;
6322 uint32_t u32Height = 0;
6323
6324 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6325
6326 if (RT_FAILURE(vrc))
6327 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6328 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6329 vrc);
6330
6331 *aWidth = u32Width;
6332 *aHeight = u32Height;
6333
6334 aData.resize(cbData);
6335 if (cbData)
6336 memcpy(&aData.front(), pu8Data, cbData);
6337
6338 freeSavedDisplayScreenshot(pu8Data);
6339
6340 return S_OK;
6341}
6342
6343HRESULT Machine::hotPlugCPU(ULONG aCpu)
6344{
6345 HRESULT rc = S_OK;
6346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6347
6348 if (!mHWData->mCPUHotPlugEnabled)
6349 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6350
6351 if (aCpu >= mHWData->mCPUCount)
6352 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6353
6354 if (mHWData->mCPUAttached[aCpu])
6355 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6356
6357 alock.release();
6358 rc = i_onCPUChange(aCpu, false);
6359 alock.acquire();
6360 if (FAILED(rc)) return rc;
6361
6362 i_setModified(IsModified_MachineData);
6363 mHWData.backup();
6364 mHWData->mCPUAttached[aCpu] = true;
6365
6366 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6367 if (Global::IsOnline(mData->mMachineState))
6368 i_saveSettings(NULL);
6369
6370 return S_OK;
6371}
6372
6373HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6374{
6375 HRESULT rc = S_OK;
6376
6377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 if (!mHWData->mCPUHotPlugEnabled)
6380 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6381
6382 if (aCpu >= SchemaDefs::MaxCPUCount)
6383 return setError(E_INVALIDARG,
6384 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6385 SchemaDefs::MaxCPUCount);
6386
6387 if (!mHWData->mCPUAttached[aCpu])
6388 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6389
6390 /* CPU 0 can't be detached */
6391 if (aCpu == 0)
6392 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6393
6394 alock.release();
6395 rc = i_onCPUChange(aCpu, true);
6396 alock.acquire();
6397 if (FAILED(rc)) return rc;
6398
6399 i_setModified(IsModified_MachineData);
6400 mHWData.backup();
6401 mHWData->mCPUAttached[aCpu] = false;
6402
6403 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6404 if (Global::IsOnline(mData->mMachineState))
6405 i_saveSettings(NULL);
6406
6407 return S_OK;
6408}
6409
6410HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6411{
6412 *aAttached = false;
6413
6414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 /* If hotplug is enabled the CPU is always enabled. */
6417 if (!mHWData->mCPUHotPlugEnabled)
6418 {
6419 if (aCpu < mHWData->mCPUCount)
6420 *aAttached = true;
6421 }
6422 else
6423 {
6424 if (aCpu < SchemaDefs::MaxCPUCount)
6425 *aAttached = mHWData->mCPUAttached[aCpu];
6426 }
6427
6428 return S_OK;
6429}
6430
6431HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6432{
6433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6434
6435 Utf8Str log = i_getLogFilename(aIdx);
6436 if (!RTFileExists(log.c_str()))
6437 log.setNull();
6438 aFilename = log;
6439
6440 return S_OK;
6441}
6442
6443HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6444{
6445 if (aSize < 0)
6446 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6447
6448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6449
6450 HRESULT rc = S_OK;
6451 Utf8Str log = i_getLogFilename(aIdx);
6452
6453 /* do not unnecessarily hold the lock while doing something which does
6454 * not need the lock and potentially takes a long time. */
6455 alock.release();
6456
6457 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6458 * keeps the SOAP reply size under 1M for the webservice (we're using
6459 * base64 encoded strings for binary data for years now, avoiding the
6460 * expansion of each byte array element to approx. 25 bytes of XML. */
6461 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6462 aData.resize(cbData);
6463
6464 RTFILE LogFile;
6465 int vrc = RTFileOpen(&LogFile, log.c_str(),
6466 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6467 if (RT_SUCCESS(vrc))
6468 {
6469 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6470 if (RT_SUCCESS(vrc))
6471 aData.resize(cbData);
6472 else
6473 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6474 tr("Could not read log file '%s' (%Rrc)"),
6475 log.c_str(), vrc);
6476 RTFileClose(LogFile);
6477 }
6478 else
6479 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6480 tr("Could not open log file '%s' (%Rrc)"),
6481 log.c_str(), vrc);
6482
6483 if (FAILED(rc))
6484 aData.resize(0);
6485
6486 return rc;
6487}
6488
6489
6490/**
6491 * Currently this method doesn't attach device to the running VM,
6492 * just makes sure it's plugged on next VM start.
6493 */
6494HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6495{
6496 // lock scope
6497 {
6498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6499
6500 HRESULT rc = i_checkStateDependency(MutableStateDep);
6501 if (FAILED(rc)) return rc;
6502
6503 ChipsetType_T aChipset = ChipsetType_PIIX3;
6504 COMGETTER(ChipsetType)(&aChipset);
6505
6506 if (aChipset != ChipsetType_ICH9)
6507 {
6508 return setError(E_INVALIDARG,
6509 tr("Host PCI attachment only supported with ICH9 chipset"));
6510 }
6511
6512 // check if device with this host PCI address already attached
6513 for (HWData::PCIDeviceAssignmentList::const_iterator
6514 it = mHWData->mPCIDeviceAssignments.begin();
6515 it != mHWData->mPCIDeviceAssignments.end();
6516 ++it)
6517 {
6518 LONG iHostAddress = -1;
6519 ComPtr<PCIDeviceAttachment> pAttach;
6520 pAttach = *it;
6521 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6522 if (iHostAddress == aHostAddress)
6523 return setError(E_INVALIDARG,
6524 tr("Device with host PCI address already attached to this VM"));
6525 }
6526
6527 ComObjPtr<PCIDeviceAttachment> pda;
6528 char name[32];
6529
6530 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6531 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6532 pda.createObject();
6533 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6534 i_setModified(IsModified_MachineData);
6535 mHWData.backup();
6536 mHWData->mPCIDeviceAssignments.push_back(pda);
6537 }
6538
6539 return S_OK;
6540}
6541
6542/**
6543 * Currently this method doesn't detach device from the running VM,
6544 * just makes sure it's not plugged on next VM start.
6545 */
6546HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6547{
6548 ComObjPtr<PCIDeviceAttachment> pAttach;
6549 bool fRemoved = false;
6550 HRESULT rc;
6551
6552 // lock scope
6553 {
6554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 rc = i_checkStateDependency(MutableStateDep);
6557 if (FAILED(rc)) return rc;
6558
6559 for (HWData::PCIDeviceAssignmentList::const_iterator
6560 it = mHWData->mPCIDeviceAssignments.begin();
6561 it != mHWData->mPCIDeviceAssignments.end();
6562 ++it)
6563 {
6564 LONG iHostAddress = -1;
6565 pAttach = *it;
6566 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6567 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6568 {
6569 i_setModified(IsModified_MachineData);
6570 mHWData.backup();
6571 mHWData->mPCIDeviceAssignments.remove(pAttach);
6572 fRemoved = true;
6573 break;
6574 }
6575 }
6576 }
6577
6578
6579 /* Fire event outside of the lock */
6580 if (fRemoved)
6581 {
6582 Assert(!pAttach.isNull());
6583 ComPtr<IEventSource> es;
6584 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6585 Assert(SUCCEEDED(rc));
6586 Bstr mid;
6587 rc = this->COMGETTER(Id)(mid.asOutParam());
6588 Assert(SUCCEEDED(rc));
6589 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6590 }
6591
6592 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6593 tr("No host PCI device %08x attached"),
6594 aHostAddress
6595 );
6596}
6597
6598HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6599{
6600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6601
6602 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6603 size_t i = 0;
6604 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6605 it = mHWData->mPCIDeviceAssignments.begin();
6606 it != mHWData->mPCIDeviceAssignments.end();
6607 ++it, ++i)
6608 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6609
6610 return S_OK;
6611}
6612
6613HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6614{
6615 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6616
6617 return S_OK;
6618}
6619
6620HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6621{
6622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6623
6624 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6625
6626 return S_OK;
6627}
6628
6629HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6630{
6631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6632 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6633 if (SUCCEEDED(hrc))
6634 {
6635 hrc = mHWData.backupEx();
6636 if (SUCCEEDED(hrc))
6637 {
6638 i_setModified(IsModified_MachineData);
6639 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6640 }
6641 }
6642 return hrc;
6643}
6644
6645HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6646{
6647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6648 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6649 return S_OK;
6650}
6651
6652HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6653{
6654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6655 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6656 if (SUCCEEDED(hrc))
6657 {
6658 hrc = mHWData.backupEx();
6659 if (SUCCEEDED(hrc))
6660 {
6661 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6662 if (SUCCEEDED(hrc))
6663 i_setModified(IsModified_MachineData);
6664 }
6665 }
6666 return hrc;
6667}
6668
6669HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6670{
6671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6672
6673 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6674
6675 return S_OK;
6676}
6677
6678HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6679{
6680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6681 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6682 if (SUCCEEDED(hrc))
6683 {
6684 hrc = mHWData.backupEx();
6685 if (SUCCEEDED(hrc))
6686 {
6687 i_setModified(IsModified_MachineData);
6688 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6689 }
6690 }
6691 return hrc;
6692}
6693
6694HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6695{
6696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6697
6698 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6699
6700 return S_OK;
6701}
6702
6703HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6704{
6705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6706
6707 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6708 if ( SUCCEEDED(hrc)
6709 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6710 {
6711 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6712 int vrc;
6713
6714 if (aAutostartEnabled)
6715 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6716 else
6717 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6718
6719 if (RT_SUCCESS(vrc))
6720 {
6721 hrc = mHWData.backupEx();
6722 if (SUCCEEDED(hrc))
6723 {
6724 i_setModified(IsModified_MachineData);
6725 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6726 }
6727 }
6728 else if (vrc == VERR_NOT_SUPPORTED)
6729 hrc = setError(VBOX_E_NOT_SUPPORTED,
6730 tr("The VM autostart feature is not supported on this platform"));
6731 else if (vrc == VERR_PATH_NOT_FOUND)
6732 hrc = setError(E_FAIL,
6733 tr("The path to the autostart database is not set"));
6734 else
6735 hrc = setError(E_UNEXPECTED,
6736 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6737 aAutostartEnabled ? "Adding" : "Removing",
6738 mUserData->s.strName.c_str(), vrc);
6739 }
6740 return hrc;
6741}
6742
6743HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6744{
6745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6746
6747 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6748
6749 return S_OK;
6750}
6751
6752HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6753{
6754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6755 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6756 if (SUCCEEDED(hrc))
6757 {
6758 hrc = mHWData.backupEx();
6759 if (SUCCEEDED(hrc))
6760 {
6761 i_setModified(IsModified_MachineData);
6762 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6763 }
6764 }
6765 return hrc;
6766}
6767
6768HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6769{
6770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6771
6772 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6773
6774 return S_OK;
6775}
6776
6777HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6778{
6779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6780 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6781 if ( SUCCEEDED(hrc)
6782 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6783 {
6784 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6785 int vrc;
6786
6787 if (aAutostopType != AutostopType_Disabled)
6788 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6789 else
6790 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6791
6792 if (RT_SUCCESS(vrc))
6793 {
6794 hrc = mHWData.backupEx();
6795 if (SUCCEEDED(hrc))
6796 {
6797 i_setModified(IsModified_MachineData);
6798 mHWData->mAutostart.enmAutostopType = aAutostopType;
6799 }
6800 }
6801 else if (vrc == VERR_NOT_SUPPORTED)
6802 hrc = setError(VBOX_E_NOT_SUPPORTED,
6803 tr("The VM autostop feature is not supported on this platform"));
6804 else if (vrc == VERR_PATH_NOT_FOUND)
6805 hrc = setError(E_FAIL,
6806 tr("The path to the autostart database is not set"));
6807 else
6808 hrc = setError(E_UNEXPECTED,
6809 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6810 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6811 mUserData->s.strName.c_str(), vrc);
6812 }
6813 return hrc;
6814}
6815
6816HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6817{
6818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6819
6820 aDefaultFrontend = mHWData->mDefaultFrontend;
6821
6822 return S_OK;
6823}
6824
6825HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6826{
6827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6828 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6829 if (SUCCEEDED(hrc))
6830 {
6831 hrc = mHWData.backupEx();
6832 if (SUCCEEDED(hrc))
6833 {
6834 i_setModified(IsModified_MachineData);
6835 mHWData->mDefaultFrontend = aDefaultFrontend;
6836 }
6837 }
6838 return hrc;
6839}
6840
6841HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6842{
6843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6844 size_t cbIcon = mUserData->s.ovIcon.size();
6845 aIcon.resize(cbIcon);
6846 if (cbIcon)
6847 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6848 return S_OK;
6849}
6850
6851HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6852{
6853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6855 if (SUCCEEDED(hrc))
6856 {
6857 i_setModified(IsModified_MachineData);
6858 mUserData.backup();
6859 size_t cbIcon = aIcon.size();
6860 mUserData->s.ovIcon.resize(cbIcon);
6861 if (cbIcon)
6862 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6863 }
6864 return hrc;
6865}
6866
6867HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6868{
6869#ifdef VBOX_WITH_USB
6870 *aUSBProxyAvailable = true;
6871#else
6872 *aUSBProxyAvailable = false;
6873#endif
6874 return S_OK;
6875}
6876
6877HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880
6881 *aVMProcessPriority = mUserData->s.enmVMPriority;
6882
6883 return S_OK;
6884}
6885
6886HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6887{
6888 RT_NOREF(aVMProcessPriority);
6889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6890 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6891 if (SUCCEEDED(hrc))
6892 {
6893 hrc = mUserData.backupEx();
6894 if (SUCCEEDED(hrc))
6895 {
6896 i_setModified(IsModified_MachineData);
6897 mUserData->s.enmVMPriority = aVMProcessPriority;
6898 }
6899 }
6900 alock.release();
6901 if (SUCCEEDED(hrc))
6902 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6903 return hrc;
6904}
6905
6906HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6907 ComPtr<IProgress> &aProgress)
6908{
6909 ComObjPtr<Progress> pP;
6910 Progress *ppP = pP;
6911 IProgress *iP = static_cast<IProgress *>(ppP);
6912 IProgress **pProgress = &iP;
6913
6914 IMachine *pTarget = aTarget;
6915
6916 /* Convert the options. */
6917 RTCList<CloneOptions_T> optList;
6918 if (aOptions.size())
6919 for (size_t i = 0; i < aOptions.size(); ++i)
6920 optList.append(aOptions[i]);
6921
6922 if (optList.contains(CloneOptions_Link))
6923 {
6924 if (!i_isSnapshotMachine())
6925 return setError(E_INVALIDARG,
6926 tr("Linked clone can only be created from a snapshot"));
6927 if (aMode != CloneMode_MachineState)
6928 return setError(E_INVALIDARG,
6929 tr("Linked clone can only be created for a single machine state"));
6930 }
6931 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6932
6933 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6934
6935 HRESULT rc = pWorker->start(pProgress);
6936
6937 pP = static_cast<Progress *>(*pProgress);
6938 pP.queryInterfaceTo(aProgress.asOutParam());
6939
6940 return rc;
6941
6942}
6943
6944HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6945 const com::Utf8Str &aType,
6946 ComPtr<IProgress> &aProgress)
6947{
6948 LogFlowThisFuncEnter();
6949
6950 ComObjPtr<Progress> ptrProgress;
6951 HRESULT hrc = ptrProgress.createObject();
6952 if (SUCCEEDED(hrc))
6953 {
6954 /* Initialize our worker task */
6955 MachineMoveVM *pTask = NULL;
6956 try
6957 {
6958 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6959 }
6960 catch (std::bad_alloc &)
6961 {
6962 return E_OUTOFMEMORY;
6963 }
6964
6965 hrc = pTask->init();//no exceptions are thrown
6966
6967 if (SUCCEEDED(hrc))
6968 {
6969 hrc = pTask->createThread();
6970 pTask = NULL; /* Consumed by createThread(). */
6971 if (SUCCEEDED(hrc))
6972 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6973 else
6974 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6975 }
6976 else
6977 delete pTask;
6978 }
6979
6980 LogFlowThisFuncLeave();
6981 return hrc;
6982
6983}
6984
6985HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6986{
6987 NOREF(aProgress);
6988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6989
6990 // This check should always fail.
6991 HRESULT rc = i_checkStateDependency(MutableStateDep);
6992 if (FAILED(rc)) return rc;
6993
6994 AssertFailedReturn(E_NOTIMPL);
6995}
6996
6997HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6998{
6999 NOREF(aSavedStateFile);
7000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7001
7002 // This check should always fail.
7003 HRESULT rc = i_checkStateDependency(MutableStateDep);
7004 if (FAILED(rc)) return rc;
7005
7006 AssertFailedReturn(E_NOTIMPL);
7007}
7008
7009HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7010{
7011 NOREF(aFRemoveFile);
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013
7014 // This check should always fail.
7015 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7016 if (FAILED(rc)) return rc;
7017
7018 AssertFailedReturn(E_NOTIMPL);
7019}
7020
7021// public methods for internal purposes
7022/////////////////////////////////////////////////////////////////////////////
7023
7024/**
7025 * Adds the given IsModified_* flag to the dirty flags of the machine.
7026 * This must be called either during i_loadSettings or under the machine write lock.
7027 * @param fl Flag
7028 * @param fAllowStateModification If state modifications are allowed.
7029 */
7030void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7031{
7032 mData->flModifications |= fl;
7033 if (fAllowStateModification && i_isStateModificationAllowed())
7034 mData->mCurrentStateModified = true;
7035}
7036
7037/**
7038 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7039 * care of the write locking.
7040 *
7041 * @param fModification The flag to add.
7042 * @param fAllowStateModification If state modifications are allowed.
7043 */
7044void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7045{
7046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7047 i_setModified(fModification, fAllowStateModification);
7048}
7049
7050/**
7051 * Saves the registry entry of this machine to the given configuration node.
7052 *
7053 * @param data Machine registry data.
7054 *
7055 * @note locks this object for reading.
7056 */
7057HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7058{
7059 AutoLimitedCaller autoCaller(this);
7060 AssertComRCReturnRC(autoCaller.rc());
7061
7062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7063
7064 data.uuid = mData->mUuid;
7065 data.strSettingsFile = mData->m_strConfigFile;
7066
7067 return S_OK;
7068}
7069
7070/**
7071 * Calculates the absolute path of the given path taking the directory of the
7072 * machine settings file as the current directory.
7073 *
7074 * @param strPath Path to calculate the absolute path for.
7075 * @param aResult Where to put the result (used only on success, can be the
7076 * same Utf8Str instance as passed in @a aPath).
7077 * @return IPRT result.
7078 *
7079 * @note Locks this object for reading.
7080 */
7081int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7082{
7083 AutoCaller autoCaller(this);
7084 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7085
7086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7087
7088 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7089
7090 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7091
7092 strSettingsDir.stripFilename();
7093 char szFolder[RTPATH_MAX];
7094 size_t cbFolder = sizeof(szFolder);
7095 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7096 if (RT_SUCCESS(vrc))
7097 aResult = szFolder;
7098
7099 return vrc;
7100}
7101
7102/**
7103 * Copies strSource to strTarget, making it relative to the machine folder
7104 * if it is a subdirectory thereof, or simply copying it otherwise.
7105 *
7106 * @param strSource Path to evaluate and copy.
7107 * @param strTarget Buffer to receive target path.
7108 *
7109 * @note Locks this object for reading.
7110 */
7111void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7112 Utf8Str &strTarget)
7113{
7114 AutoCaller autoCaller(this);
7115 AssertComRCReturn(autoCaller.rc(), (void)0);
7116
7117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7118
7119 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7120 // use strTarget as a temporary buffer to hold the machine settings dir
7121 strTarget = mData->m_strConfigFileFull;
7122 strTarget.stripFilename();
7123 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7124 {
7125 // is relative: then append what's left
7126 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7127 // for empty paths (only possible for subdirs) use "." to avoid
7128 // triggering default settings for not present config attributes.
7129 if (strTarget.isEmpty())
7130 strTarget = ".";
7131 }
7132 else
7133 // is not relative: then overwrite
7134 strTarget = strSource;
7135}
7136
7137/**
7138 * Returns the full path to the machine's log folder in the
7139 * \a aLogFolder argument.
7140 */
7141void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7142{
7143 AutoCaller autoCaller(this);
7144 AssertComRCReturnVoid(autoCaller.rc());
7145
7146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7147
7148 char szTmp[RTPATH_MAX];
7149 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7150 if (RT_SUCCESS(vrc))
7151 {
7152 if (szTmp[0] && !mUserData.isNull())
7153 {
7154 char szTmp2[RTPATH_MAX];
7155 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7156 if (RT_SUCCESS(vrc))
7157 aLogFolder = Utf8StrFmt("%s%c%s",
7158 szTmp2,
7159 RTPATH_DELIMITER,
7160 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7161 }
7162 else
7163 vrc = VERR_PATH_IS_RELATIVE;
7164 }
7165
7166 if (RT_FAILURE(vrc))
7167 {
7168 // fallback if VBOX_USER_LOGHOME is not set or invalid
7169 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7170 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7171 aLogFolder.append(RTPATH_DELIMITER);
7172 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7173 }
7174}
7175
7176/**
7177 * Returns the full path to the machine's log file for an given index.
7178 */
7179Utf8Str Machine::i_getLogFilename(ULONG idx)
7180{
7181 Utf8Str logFolder;
7182 getLogFolder(logFolder);
7183 Assert(logFolder.length());
7184
7185 Utf8Str log;
7186 if (idx == 0)
7187 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7188#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7189 else if (idx == 1)
7190 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7191 else
7192 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7193#else
7194 else
7195 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7196#endif
7197 return log;
7198}
7199
7200/**
7201 * Returns the full path to the machine's hardened log file.
7202 */
7203Utf8Str Machine::i_getHardeningLogFilename(void)
7204{
7205 Utf8Str strFilename;
7206 getLogFolder(strFilename);
7207 Assert(strFilename.length());
7208 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7209 return strFilename;
7210}
7211
7212/**
7213 * Returns the default NVRAM filename based on the location of the VM config.
7214 * Note that this is a relative path.
7215 */
7216Utf8Str Machine::i_getDefaultNVRAMFilename()
7217{
7218 AutoCaller autoCaller(this);
7219 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7220
7221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7222
7223 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7224 || i_isSnapshotMachine())
7225 return Utf8Str::Empty;
7226
7227 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7228 strNVRAMFilePath.stripPath();
7229 strNVRAMFilePath.stripSuffix();
7230 strNVRAMFilePath += ".nvram";
7231
7232 return strNVRAMFilePath;
7233}
7234
7235/**
7236 * Returns the NVRAM filename for a new snapshot. This intentionally works
7237 * similarly to the saved state file naming. Note that this is usually
7238 * a relative path, unless the snapshot folder is absolute.
7239 */
7240Utf8Str Machine::i_getSnapshotNVRAMFilename()
7241{
7242 AutoCaller autoCaller(this);
7243 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7244
7245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7246
7247 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7248 return Utf8Str::Empty;
7249
7250 RTTIMESPEC ts;
7251 RTTimeNow(&ts);
7252 RTTIME time;
7253 RTTimeExplode(&time, &ts);
7254
7255 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7256 strNVRAMFilePath += RTPATH_DELIMITER;
7257 strNVRAMFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7258 time.i32Year, time.u8Month, time.u8MonthDay,
7259 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7260
7261 return strNVRAMFilePath;
7262}
7263
7264/**
7265 * Composes a unique saved state filename based on the current system time. The filename is
7266 * granular to the second so this will work so long as no more than one snapshot is taken on
7267 * a machine per second.
7268 *
7269 * Before version 4.1, we used this formula for saved state files:
7270 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7271 * which no longer works because saved state files can now be shared between the saved state of the
7272 * "saved" machine and an online snapshot, and the following would cause problems:
7273 * 1) save machine
7274 * 2) create online snapshot from that machine state --> reusing saved state file
7275 * 3) save machine again --> filename would be reused, breaking the online snapshot
7276 *
7277 * So instead we now use a timestamp.
7278 *
7279 * @param strStateFilePath
7280 */
7281
7282void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7283{
7284 AutoCaller autoCaller(this);
7285 AssertComRCReturnVoid(autoCaller.rc());
7286
7287 {
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7290 }
7291
7292 RTTIMESPEC ts;
7293 RTTimeNow(&ts);
7294 RTTIME time;
7295 RTTimeExplode(&time, &ts);
7296
7297 strStateFilePath += RTPATH_DELIMITER;
7298 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7299 time.i32Year, time.u8Month, time.u8MonthDay,
7300 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7301}
7302
7303/**
7304 * Returns whether at least one USB controller is present for the VM.
7305 */
7306bool Machine::i_isUSBControllerPresent()
7307{
7308 AutoCaller autoCaller(this);
7309 AssertComRCReturn(autoCaller.rc(), false);
7310
7311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7312
7313 return (mUSBControllers->size() > 0);
7314}
7315
7316#ifdef VBOX_WITH_CLOUD_NET
7317HRESULT Machine::i_connectToCloudNetwork(ProgressProxy *aProgress)
7318{
7319 LogFlowThisFuncEnter();
7320 AssertReturn(aProgress, E_FAIL);
7321
7322 HRESULT hrc = E_FAIL;
7323 Bstr name;
7324
7325 LogFlowThisFunc(("Checking if cloud network needs to be connected\n"));
7326 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7327 {
7328 BOOL enabled;
7329 hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
7330 if ( FAILED(hrc)
7331 || !enabled)
7332 continue;
7333
7334 NetworkAttachmentType_T type;
7335 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
7336 if ( SUCCEEDED(hrc)
7337 && type == NetworkAttachmentType_Cloud)
7338 {
7339 if (name.isNotEmpty())
7340 {
7341 LogRel(("VM '%s' uses multiple cloud network attachments. '%ls' will be ignored.\n",
7342 mUserData->s.strName.c_str(), name.raw()));
7343 continue;
7344 }
7345 hrc = mNetworkAdapters[slot]->COMGETTER(CloudNetwork)(name.asOutParam());
7346 if (SUCCEEDED(hrc))
7347 {
7348 LogRel(("VM '%s' uses cloud network '%ls'\n",
7349 mUserData->s.strName.c_str(), name.raw()));
7350 }
7351 }
7352 }
7353 if (name.isNotEmpty())
7354 {
7355 LogFlowThisFunc(("Connecting to cloud network '%ls'...\n", name.raw()));
7356 ComObjPtr<CloudNetwork> network;
7357 hrc = mParent->i_findCloudNetworkByName(name, &network);
7358 if (FAILED(hrc))
7359 {
7360 LogRel(("Could not find cloud network '%ls'.\n", name.raw()));
7361 return hrc;
7362 }
7363 GatewayInfo gateways;
7364 hrc = startGateways(mParent, network, gateways);
7365 if (SUCCEEDED(hrc))
7366 {
7367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7368 mData->mGatewayInfo = gateways;
7369 }
7370 }
7371 else
7372 LogFlowThisFunc(("VM '%s' has no cloud network attachments.\n", mUserData->s.strName.c_str()));
7373
7374 LogFlowThisFuncLeave();
7375 return hrc;
7376}
7377
7378HRESULT Machine::i_disconnectFromCloudNetwork()
7379{
7380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7381 GatewayInfo gateways(mData->mGatewayInfo);
7382 mData->mGatewayInfo.setNull();
7383 alock.release();
7384
7385 HRESULT hrc = stopGateways(mParent, gateways);
7386 return hrc;
7387}
7388#endif /* VBOX_WITH_CLOUD_NET */
7389
7390
7391/**
7392 * @note Locks this object for writing, calls the client process
7393 * (inside the lock).
7394 */
7395HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7396 const Utf8Str &strFrontend,
7397 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7398 ProgressProxy *aProgress)
7399{
7400 LogFlowThisFuncEnter();
7401
7402 AssertReturn(aControl, E_FAIL);
7403 AssertReturn(aProgress, E_FAIL);
7404 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7405
7406 AutoCaller autoCaller(this);
7407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7408
7409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7410
7411 if (!mData->mRegistered)
7412 return setError(E_UNEXPECTED,
7413 tr("The machine '%s' is not registered"),
7414 mUserData->s.strName.c_str());
7415
7416 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7417
7418 /* The process started when launching a VM with separate UI/VM processes is always
7419 * the UI process, i.e. needs special handling as it won't claim the session. */
7420 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7421
7422 if (fSeparate)
7423 {
7424 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7425 return setError(VBOX_E_INVALID_OBJECT_STATE,
7426 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7427 mUserData->s.strName.c_str());
7428 }
7429 else
7430 {
7431 if ( mData->mSession.mState == SessionState_Locked
7432 || mData->mSession.mState == SessionState_Spawning
7433 || mData->mSession.mState == SessionState_Unlocking)
7434 return setError(VBOX_E_INVALID_OBJECT_STATE,
7435 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7436 mUserData->s.strName.c_str());
7437
7438 /* may not be busy */
7439 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7440 }
7441
7442 /* Hardening logging */
7443#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7444 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7445 {
7446 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7447 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7448 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7449 {
7450 Utf8Str strStartupLogDir = strHardeningLogFile;
7451 strStartupLogDir.stripFilename();
7452 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7453 file without stripping the file. */
7454 }
7455 strSupHardeningLogArg.append(strHardeningLogFile);
7456
7457 /* Remove legacy log filename to avoid confusion. */
7458 Utf8Str strOldStartupLogFile;
7459 getLogFolder(strOldStartupLogFile);
7460 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7461 RTFileDelete(strOldStartupLogFile.c_str());
7462 }
7463#else
7464 Utf8Str strSupHardeningLogArg;
7465#endif
7466
7467 Utf8Str strAppOverride;
7468#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7469 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7470#endif
7471
7472 bool fUseVBoxSDS = false;
7473 Utf8Str strCanonicalName;
7474 if (false)
7475 { }
7476#ifdef VBOX_WITH_QTGUI
7477 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7478 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7479 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7480 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7481 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7482 {
7483 strCanonicalName = "GUI/Qt";
7484 fUseVBoxSDS = true;
7485 }
7486#endif
7487#ifdef VBOX_WITH_VBOXSDL
7488 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7489 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7490 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7491 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7492 {
7493 strCanonicalName = "GUI/SDL";
7494 fUseVBoxSDS = true;
7495 }
7496#endif
7497#ifdef VBOX_WITH_HEADLESS
7498 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7499 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7500 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7501 {
7502 strCanonicalName = "headless";
7503 }
7504#endif
7505 else
7506 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7507
7508 Utf8Str idStr = mData->mUuid.toString();
7509 Utf8Str const &strMachineName = mUserData->s.strName;
7510 RTPROCESS pid = NIL_RTPROCESS;
7511
7512#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7513 RT_NOREF(fUseVBoxSDS);
7514#else
7515 DWORD idCallerSession = ~(DWORD)0;
7516 if (fUseVBoxSDS)
7517 {
7518 /*
7519 * The VBoxSDS should be used for process launching the VM with
7520 * GUI only if the caller and the VBoxSDS are in different Windows
7521 * sessions and the caller in the interactive one.
7522 */
7523 fUseVBoxSDS = false;
7524
7525 /* Get windows session of the current process. The process token used
7526 due to several reasons:
7527 1. The token is absent for the current thread except someone set it
7528 for us.
7529 2. Needs to get the id of the session where the process is started.
7530 We only need to do this once, though. */
7531 static DWORD s_idCurrentSession = ~(DWORD)0;
7532 DWORD idCurrentSession = s_idCurrentSession;
7533 if (idCurrentSession == ~(DWORD)0)
7534 {
7535 HANDLE hCurrentProcessToken = NULL;
7536 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7537 {
7538 DWORD cbIgn = 0;
7539 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7540 s_idCurrentSession = idCurrentSession;
7541 else
7542 {
7543 idCurrentSession = ~(DWORD)0;
7544 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7545 }
7546 CloseHandle(hCurrentProcessToken);
7547 }
7548 else
7549 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7550 }
7551
7552 /* get the caller's session */
7553 HRESULT hrc = CoImpersonateClient();
7554 if (SUCCEEDED(hrc))
7555 {
7556 HANDLE hCallerThreadToken;
7557 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7558 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7559 &hCallerThreadToken))
7560 {
7561 SetLastError(NO_ERROR);
7562 DWORD cbIgn = 0;
7563 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7564 {
7565 /* Only need to use SDS if the session ID differs: */
7566 if (idCurrentSession != idCallerSession)
7567 {
7568 fUseVBoxSDS = false;
7569
7570 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7571 DWORD cbTokenGroups = 0;
7572 PTOKEN_GROUPS pTokenGroups = NULL;
7573 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7574 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7575 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7576 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7577 {
7578 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7579 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7580 PSID pInteractiveSid = NULL;
7581 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7582 {
7583 /* Iterate over the groups looking for the interactive SID: */
7584 fUseVBoxSDS = false;
7585 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7586 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7587 {
7588 fUseVBoxSDS = true;
7589 break;
7590 }
7591 FreeSid(pInteractiveSid);
7592 }
7593 }
7594 else
7595 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7596 RTMemTmpFree(pTokenGroups);
7597 }
7598 }
7599 else
7600 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7601 CloseHandle(hCallerThreadToken);
7602 }
7603 else
7604 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7605 CoRevertToSelf();
7606 }
7607 else
7608 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7609 }
7610 if (fUseVBoxSDS)
7611 {
7612 /* connect to VBoxSDS */
7613 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7614 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7615 if (FAILED(rc))
7616 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7617 strMachineName.c_str());
7618
7619 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7620 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7621 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7622 service to access the files. */
7623 rc = CoSetProxyBlanket(pVBoxSDS,
7624 RPC_C_AUTHN_DEFAULT,
7625 RPC_C_AUTHZ_DEFAULT,
7626 COLE_DEFAULT_PRINCIPAL,
7627 RPC_C_AUTHN_LEVEL_DEFAULT,
7628 RPC_C_IMP_LEVEL_IMPERSONATE,
7629 NULL,
7630 EOAC_DEFAULT);
7631 if (FAILED(rc))
7632 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7633
7634 size_t const cEnvVars = aEnvironmentChanges.size();
7635 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7636 for (size_t i = 0; i < cEnvVars; i++)
7637 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7638
7639 ULONG uPid = 0;
7640 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7641 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7642 idCallerSession, &uPid);
7643 if (FAILED(rc))
7644 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7645 pid = (RTPROCESS)uPid;
7646 }
7647 else
7648#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7649 {
7650 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7651 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7652 if (RT_FAILURE(vrc))
7653 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7654 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7655 }
7656
7657 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7658 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7659
7660 if (!fSeparate)
7661 {
7662 /*
7663 * Note that we don't release the lock here before calling the client,
7664 * because it doesn't need to call us back if called with a NULL argument.
7665 * Releasing the lock here is dangerous because we didn't prepare the
7666 * launch data yet, but the client we've just started may happen to be
7667 * too fast and call LockMachine() that will fail (because of PID, etc.),
7668 * so that the Machine will never get out of the Spawning session state.
7669 */
7670
7671 /* inform the session that it will be a remote one */
7672 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7673#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7674 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7675#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7676 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7677#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7678 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7679
7680 if (FAILED(rc))
7681 {
7682 /* restore the session state */
7683 mData->mSession.mState = SessionState_Unlocked;
7684 alock.release();
7685 mParent->i_addProcessToReap(pid);
7686 /* The failure may occur w/o any error info (from RPC), so provide one */
7687 return setError(VBOX_E_VM_ERROR,
7688 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7689 }
7690
7691 /* attach launch data to the machine */
7692 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7693 mData->mSession.mRemoteControls.push_back(aControl);
7694 mData->mSession.mProgress = aProgress;
7695 mData->mSession.mPID = pid;
7696 mData->mSession.mState = SessionState_Spawning;
7697 Assert(strCanonicalName.isNotEmpty());
7698 mData->mSession.mName = strCanonicalName;
7699 }
7700 else
7701 {
7702 /* For separate UI process we declare the launch as completed instantly, as the
7703 * actual headless VM start may or may not come. No point in remembering anything
7704 * yet, as what matters for us is when the headless VM gets started. */
7705 aProgress->i_notifyComplete(S_OK);
7706 }
7707
7708 alock.release();
7709 mParent->i_addProcessToReap(pid);
7710
7711 LogFlowThisFuncLeave();
7712 return S_OK;
7713}
7714
7715/**
7716 * Returns @c true if the given session machine instance has an open direct
7717 * session (and optionally also for direct sessions which are closing) and
7718 * returns the session control machine instance if so.
7719 *
7720 * Note that when the method returns @c false, the arguments remain unchanged.
7721 *
7722 * @param aMachine Session machine object.
7723 * @param aControl Direct session control object (optional).
7724 * @param aRequireVM If true then only allow VM sessions.
7725 * @param aAllowClosing If true then additionally a session which is currently
7726 * being closed will also be allowed.
7727 *
7728 * @note locks this object for reading.
7729 */
7730bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7731 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7732 bool aRequireVM /*= false*/,
7733 bool aAllowClosing /*= false*/)
7734{
7735 AutoLimitedCaller autoCaller(this);
7736 AssertComRCReturn(autoCaller.rc(), false);
7737
7738 /* just return false for inaccessible machines */
7739 if (getObjectState().getState() != ObjectState::Ready)
7740 return false;
7741
7742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7743
7744 if ( ( mData->mSession.mState == SessionState_Locked
7745 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7746 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7747 )
7748 {
7749 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7750
7751 aMachine = mData->mSession.mMachine;
7752
7753 if (aControl != NULL)
7754 *aControl = mData->mSession.mDirectControl;
7755
7756 return true;
7757 }
7758
7759 return false;
7760}
7761
7762/**
7763 * Returns @c true if the given machine has an spawning direct session.
7764 *
7765 * @note locks this object for reading.
7766 */
7767bool Machine::i_isSessionSpawning()
7768{
7769 AutoLimitedCaller autoCaller(this);
7770 AssertComRCReturn(autoCaller.rc(), false);
7771
7772 /* just return false for inaccessible machines */
7773 if (getObjectState().getState() != ObjectState::Ready)
7774 return false;
7775
7776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7777
7778 if (mData->mSession.mState == SessionState_Spawning)
7779 return true;
7780
7781 return false;
7782}
7783
7784/**
7785 * Called from the client watcher thread to check for unexpected client process
7786 * death during Session_Spawning state (e.g. before it successfully opened a
7787 * direct session).
7788 *
7789 * On Win32 and on OS/2, this method is called only when we've got the
7790 * direct client's process termination notification, so it always returns @c
7791 * true.
7792 *
7793 * On other platforms, this method returns @c true if the client process is
7794 * terminated and @c false if it's still alive.
7795 *
7796 * @note Locks this object for writing.
7797 */
7798bool Machine::i_checkForSpawnFailure()
7799{
7800 AutoCaller autoCaller(this);
7801 if (!autoCaller.isOk())
7802 {
7803 /* nothing to do */
7804 LogFlowThisFunc(("Already uninitialized!\n"));
7805 return true;
7806 }
7807
7808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7809
7810 if (mData->mSession.mState != SessionState_Spawning)
7811 {
7812 /* nothing to do */
7813 LogFlowThisFunc(("Not spawning any more!\n"));
7814 return true;
7815 }
7816
7817 HRESULT rc = S_OK;
7818
7819 /* PID not yet initialized, skip check. */
7820 if (mData->mSession.mPID == NIL_RTPROCESS)
7821 return false;
7822
7823 RTPROCSTATUS status;
7824 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7825
7826 if (vrc != VERR_PROCESS_RUNNING)
7827 {
7828 Utf8Str strExtraInfo;
7829
7830#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7831 /* If the startup logfile exists and is of non-zero length, tell the
7832 user to look there for more details to encourage them to attach it
7833 when reporting startup issues. */
7834 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7835 uint64_t cbStartupLogFile = 0;
7836 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7837 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7838 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7839#endif
7840
7841 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7842 rc = setError(E_FAIL,
7843 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7844 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7845 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7846 rc = setError(E_FAIL,
7847 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7848 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7849 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7850 rc = setError(E_FAIL,
7851 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7852 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7853 else
7854 rc = setErrorBoth(E_FAIL, vrc,
7855 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7856 i_getName().c_str(), vrc, strExtraInfo.c_str());
7857 }
7858
7859 if (FAILED(rc))
7860 {
7861 /* Close the remote session, remove the remote control from the list
7862 * and reset session state to Closed (@note keep the code in sync with
7863 * the relevant part in LockMachine()). */
7864
7865 Assert(mData->mSession.mRemoteControls.size() == 1);
7866 if (mData->mSession.mRemoteControls.size() == 1)
7867 {
7868 ErrorInfoKeeper eik;
7869 mData->mSession.mRemoteControls.front()->Uninitialize();
7870 }
7871
7872 mData->mSession.mRemoteControls.clear();
7873 mData->mSession.mState = SessionState_Unlocked;
7874
7875 /* finalize the progress after setting the state */
7876 if (!mData->mSession.mProgress.isNull())
7877 {
7878 mData->mSession.mProgress->notifyComplete(rc);
7879 mData->mSession.mProgress.setNull();
7880 }
7881
7882 mData->mSession.mPID = NIL_RTPROCESS;
7883
7884 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7885 return true;
7886 }
7887
7888 return false;
7889}
7890
7891/**
7892 * Checks whether the machine can be registered. If so, commits and saves
7893 * all settings.
7894 *
7895 * @note Must be called from mParent's write lock. Locks this object and
7896 * children for writing.
7897 */
7898HRESULT Machine::i_prepareRegister()
7899{
7900 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7901
7902 AutoLimitedCaller autoCaller(this);
7903 AssertComRCReturnRC(autoCaller.rc());
7904
7905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7906
7907 /* wait for state dependents to drop to zero */
7908 i_ensureNoStateDependencies();
7909
7910 if (!mData->mAccessible)
7911 return setError(VBOX_E_INVALID_OBJECT_STATE,
7912 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7913 mUserData->s.strName.c_str(),
7914 mData->mUuid.toString().c_str());
7915
7916 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7917
7918 if (mData->mRegistered)
7919 return setError(VBOX_E_INVALID_OBJECT_STATE,
7920 tr("The machine '%s' with UUID {%s} is already registered"),
7921 mUserData->s.strName.c_str(),
7922 mData->mUuid.toString().c_str());
7923
7924 HRESULT rc = S_OK;
7925
7926 // Ensure the settings are saved. If we are going to be registered and
7927 // no config file exists yet, create it by calling i_saveSettings() too.
7928 if ( (mData->flModifications)
7929 || (!mData->pMachineConfigFile->fileExists())
7930 )
7931 {
7932 rc = i_saveSettings(NULL);
7933 // no need to check whether VirtualBox.xml needs saving too since
7934 // we can't have a machine XML file rename pending
7935 if (FAILED(rc)) return rc;
7936 }
7937
7938 /* more config checking goes here */
7939
7940 if (SUCCEEDED(rc))
7941 {
7942 /* we may have had implicit modifications we want to fix on success */
7943 i_commit();
7944
7945 mData->mRegistered = true;
7946 }
7947 else
7948 {
7949 /* we may have had implicit modifications we want to cancel on failure*/
7950 i_rollback(false /* aNotify */);
7951 }
7952
7953 return rc;
7954}
7955
7956/**
7957 * Increases the number of objects dependent on the machine state or on the
7958 * registered state. Guarantees that these two states will not change at least
7959 * until #i_releaseStateDependency() is called.
7960 *
7961 * Depending on the @a aDepType value, additional state checks may be made.
7962 * These checks will set extended error info on failure. See
7963 * #i_checkStateDependency() for more info.
7964 *
7965 * If this method returns a failure, the dependency is not added and the caller
7966 * is not allowed to rely on any particular machine state or registration state
7967 * value and may return the failed result code to the upper level.
7968 *
7969 * @param aDepType Dependency type to add.
7970 * @param aState Current machine state (NULL if not interested).
7971 * @param aRegistered Current registered state (NULL if not interested).
7972 *
7973 * @note Locks this object for writing.
7974 */
7975HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7976 MachineState_T *aState /* = NULL */,
7977 BOOL *aRegistered /* = NULL */)
7978{
7979 AutoCaller autoCaller(this);
7980 AssertComRCReturnRC(autoCaller.rc());
7981
7982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7983
7984 HRESULT rc = i_checkStateDependency(aDepType);
7985 if (FAILED(rc)) return rc;
7986
7987 {
7988 if (mData->mMachineStateChangePending != 0)
7989 {
7990 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7991 * drop to zero so don't add more. It may make sense to wait a bit
7992 * and retry before reporting an error (since the pending state
7993 * transition should be really quick) but let's just assert for
7994 * now to see if it ever happens on practice. */
7995
7996 AssertFailed();
7997
7998 return setError(E_ACCESSDENIED,
7999 tr("Machine state change is in progress. Please retry the operation later."));
8000 }
8001
8002 ++mData->mMachineStateDeps;
8003 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8004 }
8005
8006 if (aState)
8007 *aState = mData->mMachineState;
8008 if (aRegistered)
8009 *aRegistered = mData->mRegistered;
8010
8011 return S_OK;
8012}
8013
8014/**
8015 * Decreases the number of objects dependent on the machine state.
8016 * Must always complete the #i_addStateDependency() call after the state
8017 * dependency is no more necessary.
8018 */
8019void Machine::i_releaseStateDependency()
8020{
8021 AutoCaller autoCaller(this);
8022 AssertComRCReturnVoid(autoCaller.rc());
8023
8024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8025
8026 /* releaseStateDependency() w/o addStateDependency()? */
8027 AssertReturnVoid(mData->mMachineStateDeps != 0);
8028 -- mData->mMachineStateDeps;
8029
8030 if (mData->mMachineStateDeps == 0)
8031 {
8032 /* inform i_ensureNoStateDependencies() that there are no more deps */
8033 if (mData->mMachineStateChangePending != 0)
8034 {
8035 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8036 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8037 }
8038 }
8039}
8040
8041Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8042{
8043 /* start with nothing found */
8044 Utf8Str strResult("");
8045
8046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8047
8048 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8049 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8050 // found:
8051 strResult = it->second; // source is a Utf8Str
8052
8053 return strResult;
8054}
8055
8056// protected methods
8057/////////////////////////////////////////////////////////////////////////////
8058
8059/**
8060 * Performs machine state checks based on the @a aDepType value. If a check
8061 * fails, this method will set extended error info, otherwise it will return
8062 * S_OK. It is supposed, that on failure, the caller will immediately return
8063 * the return value of this method to the upper level.
8064 *
8065 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8066 *
8067 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8068 * current state of this machine object allows to change settings of the
8069 * machine (i.e. the machine is not registered, or registered but not running
8070 * and not saved). It is useful to call this method from Machine setters
8071 * before performing any change.
8072 *
8073 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8074 * as for MutableStateDep except that if the machine is saved, S_OK is also
8075 * returned. This is useful in setters which allow changing machine
8076 * properties when it is in the saved state.
8077 *
8078 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8079 * if the current state of this machine object allows to change runtime
8080 * changeable settings of the machine (i.e. the machine is not registered, or
8081 * registered but either running or not running and not saved). It is useful
8082 * to call this method from Machine setters before performing any changes to
8083 * runtime changeable settings.
8084 *
8085 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8086 * the same as for MutableOrRunningStateDep except that if the machine is
8087 * saved, S_OK is also returned. This is useful in setters which allow
8088 * changing runtime and saved state changeable machine properties.
8089 *
8090 * @param aDepType Dependency type to check.
8091 *
8092 * @note Non Machine based classes should use #i_addStateDependency() and
8093 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8094 * template.
8095 *
8096 * @note This method must be called from under this object's read or write
8097 * lock.
8098 */
8099HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8100{
8101 switch (aDepType)
8102 {
8103 case AnyStateDep:
8104 {
8105 break;
8106 }
8107 case MutableStateDep:
8108 {
8109 if ( mData->mRegistered
8110 && ( !i_isSessionMachine()
8111 || ( mData->mMachineState != MachineState_Aborted
8112 && mData->mMachineState != MachineState_Teleported
8113 && mData->mMachineState != MachineState_PoweredOff
8114 )
8115 )
8116 )
8117 return setError(VBOX_E_INVALID_VM_STATE,
8118 tr("The machine is not mutable (state is %s)"),
8119 Global::stringifyMachineState(mData->mMachineState));
8120 break;
8121 }
8122 case MutableOrSavedStateDep:
8123 {
8124 if ( mData->mRegistered
8125 && ( !i_isSessionMachine()
8126 || ( mData->mMachineState != MachineState_Aborted
8127 && mData->mMachineState != MachineState_Teleported
8128 && mData->mMachineState != MachineState_Saved
8129 && mData->mMachineState != MachineState_PoweredOff
8130 )
8131 )
8132 )
8133 return setError(VBOX_E_INVALID_VM_STATE,
8134 tr("The machine is not mutable or saved (state is %s)"),
8135 Global::stringifyMachineState(mData->mMachineState));
8136 break;
8137 }
8138 case MutableOrRunningStateDep:
8139 {
8140 if ( mData->mRegistered
8141 && ( !i_isSessionMachine()
8142 || ( mData->mMachineState != MachineState_Aborted
8143 && mData->mMachineState != MachineState_Teleported
8144 && mData->mMachineState != MachineState_PoweredOff
8145 && !Global::IsOnline(mData->mMachineState)
8146 )
8147 )
8148 )
8149 return setError(VBOX_E_INVALID_VM_STATE,
8150 tr("The machine is not mutable or running (state is %s)"),
8151 Global::stringifyMachineState(mData->mMachineState));
8152 break;
8153 }
8154 case MutableOrSavedOrRunningStateDep:
8155 {
8156 if ( mData->mRegistered
8157 && ( !i_isSessionMachine()
8158 || ( mData->mMachineState != MachineState_Aborted
8159 && mData->mMachineState != MachineState_Teleported
8160 && mData->mMachineState != MachineState_Saved
8161 && mData->mMachineState != MachineState_PoweredOff
8162 && !Global::IsOnline(mData->mMachineState)
8163 )
8164 )
8165 )
8166 return setError(VBOX_E_INVALID_VM_STATE,
8167 tr("The machine is not mutable, saved or running (state is %s)"),
8168 Global::stringifyMachineState(mData->mMachineState));
8169 break;
8170 }
8171 }
8172
8173 return S_OK;
8174}
8175
8176/**
8177 * Helper to initialize all associated child objects and allocate data
8178 * structures.
8179 *
8180 * This method must be called as a part of the object's initialization procedure
8181 * (usually done in the #init() method).
8182 *
8183 * @note Must be called only from #init() or from #i_registeredInit().
8184 */
8185HRESULT Machine::initDataAndChildObjects()
8186{
8187 AutoCaller autoCaller(this);
8188 AssertComRCReturnRC(autoCaller.rc());
8189 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8190 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8191
8192 AssertReturn(!mData->mAccessible, E_FAIL);
8193
8194 /* allocate data structures */
8195 mSSData.allocate();
8196 mUserData.allocate();
8197 mHWData.allocate();
8198 mMediumAttachments.allocate();
8199 mStorageControllers.allocate();
8200 mUSBControllers.allocate();
8201
8202 /* initialize mOSTypeId */
8203 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8204
8205/** @todo r=bird: init() methods never fails, right? Why don't we make them
8206 * return void then! */
8207
8208 /* create associated BIOS settings object */
8209 unconst(mBIOSSettings).createObject();
8210 mBIOSSettings->init(this);
8211
8212 /* create associated record settings object */
8213 unconst(mRecordingSettings).createObject();
8214 mRecordingSettings->init(this);
8215
8216 /* create the graphics adapter object (always present) */
8217 unconst(mGraphicsAdapter).createObject();
8218 mGraphicsAdapter->init(this);
8219
8220 /* create an associated VRDE object (default is disabled) */
8221 unconst(mVRDEServer).createObject();
8222 mVRDEServer->init(this);
8223
8224 /* create associated serial port objects */
8225 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8226 {
8227 unconst(mSerialPorts[slot]).createObject();
8228 mSerialPorts[slot]->init(this, slot);
8229 }
8230
8231 /* create associated parallel port objects */
8232 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8233 {
8234 unconst(mParallelPorts[slot]).createObject();
8235 mParallelPorts[slot]->init(this, slot);
8236 }
8237
8238 /* create the audio adapter object (always present, default is disabled) */
8239 unconst(mAudioAdapter).createObject();
8240 mAudioAdapter->init(this);
8241
8242 /* create the USB device filters object (always present) */
8243 unconst(mUSBDeviceFilters).createObject();
8244 mUSBDeviceFilters->init(this);
8245
8246 /* create associated network adapter objects */
8247 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8248 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8249 {
8250 unconst(mNetworkAdapters[slot]).createObject();
8251 mNetworkAdapters[slot]->init(this, slot);
8252 }
8253
8254 /* create the bandwidth control */
8255 unconst(mBandwidthControl).createObject();
8256 mBandwidthControl->init(this);
8257
8258 return S_OK;
8259}
8260
8261/**
8262 * Helper to uninitialize all associated child objects and to free all data
8263 * structures.
8264 *
8265 * This method must be called as a part of the object's uninitialization
8266 * procedure (usually done in the #uninit() method).
8267 *
8268 * @note Must be called only from #uninit() or from #i_registeredInit().
8269 */
8270void Machine::uninitDataAndChildObjects()
8271{
8272 AutoCaller autoCaller(this);
8273 AssertComRCReturnVoid(autoCaller.rc());
8274 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8275 || getObjectState().getState() == ObjectState::Limited);
8276
8277 /* tell all our other child objects we've been uninitialized */
8278 if (mBandwidthControl)
8279 {
8280 mBandwidthControl->uninit();
8281 unconst(mBandwidthControl).setNull();
8282 }
8283
8284 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8285 {
8286 if (mNetworkAdapters[slot])
8287 {
8288 mNetworkAdapters[slot]->uninit();
8289 unconst(mNetworkAdapters[slot]).setNull();
8290 }
8291 }
8292
8293 if (mUSBDeviceFilters)
8294 {
8295 mUSBDeviceFilters->uninit();
8296 unconst(mUSBDeviceFilters).setNull();
8297 }
8298
8299 if (mAudioAdapter)
8300 {
8301 mAudioAdapter->uninit();
8302 unconst(mAudioAdapter).setNull();
8303 }
8304
8305 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8306 {
8307 if (mParallelPorts[slot])
8308 {
8309 mParallelPorts[slot]->uninit();
8310 unconst(mParallelPorts[slot]).setNull();
8311 }
8312 }
8313
8314 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8315 {
8316 if (mSerialPorts[slot])
8317 {
8318 mSerialPorts[slot]->uninit();
8319 unconst(mSerialPorts[slot]).setNull();
8320 }
8321 }
8322
8323 if (mVRDEServer)
8324 {
8325 mVRDEServer->uninit();
8326 unconst(mVRDEServer).setNull();
8327 }
8328
8329 if (mGraphicsAdapter)
8330 {
8331 mGraphicsAdapter->uninit();
8332 unconst(mGraphicsAdapter).setNull();
8333 }
8334
8335 if (mBIOSSettings)
8336 {
8337 mBIOSSettings->uninit();
8338 unconst(mBIOSSettings).setNull();
8339 }
8340
8341 if (mRecordingSettings)
8342 {
8343 mRecordingSettings->uninit();
8344 unconst(mRecordingSettings).setNull();
8345 }
8346
8347 /* Deassociate media (only when a real Machine or a SnapshotMachine
8348 * instance is uninitialized; SessionMachine instances refer to real
8349 * Machine media). This is necessary for a clean re-initialization of
8350 * the VM after successfully re-checking the accessibility state. Note
8351 * that in case of normal Machine or SnapshotMachine uninitialization (as
8352 * a result of unregistering or deleting the snapshot), outdated media
8353 * attachments will already be uninitialized and deleted, so this
8354 * code will not affect them. */
8355 if ( !mMediumAttachments.isNull()
8356 && !i_isSessionMachine()
8357 )
8358 {
8359 for (MediumAttachmentList::const_iterator
8360 it = mMediumAttachments->begin();
8361 it != mMediumAttachments->end();
8362 ++it)
8363 {
8364 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8365 if (pMedium.isNull())
8366 continue;
8367 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8368 AssertComRC(rc);
8369 }
8370 }
8371
8372 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8373 {
8374 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8375 if (mData->mFirstSnapshot)
8376 {
8377 // snapshots tree is protected by machine write lock; strictly
8378 // this isn't necessary here since we're deleting the entire
8379 // machine, but otherwise we assert in Snapshot::uninit()
8380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8381 mData->mFirstSnapshot->uninit();
8382 mData->mFirstSnapshot.setNull();
8383 }
8384
8385 mData->mCurrentSnapshot.setNull();
8386 }
8387
8388 /* free data structures (the essential mData structure is not freed here
8389 * since it may be still in use) */
8390 mMediumAttachments.free();
8391 mStorageControllers.free();
8392 mUSBControllers.free();
8393 mHWData.free();
8394 mUserData.free();
8395 mSSData.free();
8396}
8397
8398/**
8399 * Returns a pointer to the Machine object for this machine that acts like a
8400 * parent for complex machine data objects such as shared folders, etc.
8401 *
8402 * For primary Machine objects and for SnapshotMachine objects, returns this
8403 * object's pointer itself. For SessionMachine objects, returns the peer
8404 * (primary) machine pointer.
8405 */
8406Machine *Machine::i_getMachine()
8407{
8408 if (i_isSessionMachine())
8409 return (Machine*)mPeer;
8410 return this;
8411}
8412
8413/**
8414 * Makes sure that there are no machine state dependents. If necessary, waits
8415 * for the number of dependents to drop to zero.
8416 *
8417 * Make sure this method is called from under this object's write lock to
8418 * guarantee that no new dependents may be added when this method returns
8419 * control to the caller.
8420 *
8421 * @note Locks this object for writing. The lock will be released while waiting
8422 * (if necessary).
8423 *
8424 * @warning To be used only in methods that change the machine state!
8425 */
8426void Machine::i_ensureNoStateDependencies()
8427{
8428 AssertReturnVoid(isWriteLockOnCurrentThread());
8429
8430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8431
8432 /* Wait for all state dependents if necessary */
8433 if (mData->mMachineStateDeps != 0)
8434 {
8435 /* lazy semaphore creation */
8436 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8437 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8438
8439 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8440 mData->mMachineStateDeps));
8441
8442 ++mData->mMachineStateChangePending;
8443
8444 /* reset the semaphore before waiting, the last dependent will signal
8445 * it */
8446 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8447
8448 alock.release();
8449
8450 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8451
8452 alock.acquire();
8453
8454 -- mData->mMachineStateChangePending;
8455 }
8456}
8457
8458/**
8459 * Changes the machine state and informs callbacks.
8460 *
8461 * This method is not intended to fail so it either returns S_OK or asserts (and
8462 * returns a failure).
8463 *
8464 * @note Locks this object for writing.
8465 */
8466HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8467{
8468 LogFlowThisFuncEnter();
8469 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8470 Assert(aMachineState != MachineState_Null);
8471
8472 AutoCaller autoCaller(this);
8473 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8474
8475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8476
8477 /* wait for state dependents to drop to zero */
8478 i_ensureNoStateDependencies();
8479
8480 MachineState_T const enmOldState = mData->mMachineState;
8481 if (enmOldState != aMachineState)
8482 {
8483 mData->mMachineState = aMachineState;
8484 RTTimeNow(&mData->mLastStateChange);
8485
8486#ifdef VBOX_WITH_DTRACE_R3_MAIN
8487 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8488#endif
8489 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8490 }
8491
8492 LogFlowThisFuncLeave();
8493 return S_OK;
8494}
8495
8496/**
8497 * Searches for a shared folder with the given logical name
8498 * in the collection of shared folders.
8499 *
8500 * @param aName logical name of the shared folder
8501 * @param aSharedFolder where to return the found object
8502 * @param aSetError whether to set the error info if the folder is
8503 * not found
8504 * @return
8505 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8506 *
8507 * @note
8508 * must be called from under the object's lock!
8509 */
8510HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8511 ComObjPtr<SharedFolder> &aSharedFolder,
8512 bool aSetError /* = false */)
8513{
8514 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8515 for (HWData::SharedFolderList::const_iterator
8516 it = mHWData->mSharedFolders.begin();
8517 it != mHWData->mSharedFolders.end();
8518 ++it)
8519 {
8520 SharedFolder *pSF = *it;
8521 AutoCaller autoCaller(pSF);
8522 if (pSF->i_getName() == aName)
8523 {
8524 aSharedFolder = pSF;
8525 rc = S_OK;
8526 break;
8527 }
8528 }
8529
8530 if (aSetError && FAILED(rc))
8531 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8532
8533 return rc;
8534}
8535
8536/**
8537 * Initializes all machine instance data from the given settings structures
8538 * from XML. The exception is the machine UUID which needs special handling
8539 * depending on the caller's use case, so the caller needs to set that herself.
8540 *
8541 * This gets called in several contexts during machine initialization:
8542 *
8543 * -- When machine XML exists on disk already and needs to be loaded into memory,
8544 * for example, from #i_registeredInit() to load all registered machines on
8545 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8546 * attached to the machine should be part of some media registry already.
8547 *
8548 * -- During OVF import, when a machine config has been constructed from an
8549 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8550 * ensure that the media listed as attachments in the config (which have
8551 * been imported from the OVF) receive the correct registry ID.
8552 *
8553 * -- During VM cloning.
8554 *
8555 * @param config Machine settings from XML.
8556 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8557 * for each attached medium in the config.
8558 * @return
8559 */
8560HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8561 const Guid *puuidRegistry)
8562{
8563 // copy name, description, OS type, teleporter, UTC etc.
8564 mUserData->s = config.machineUserData;
8565
8566 // look up the object by Id to check it is valid
8567 ComObjPtr<GuestOSType> pGuestOSType;
8568 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8569 if (!pGuestOSType.isNull())
8570 mUserData->s.strOsType = pGuestOSType->i_id();
8571
8572 // stateFile (optional)
8573 if (config.strStateFile.isEmpty())
8574 mSSData->strStateFilePath.setNull();
8575 else
8576 {
8577 Utf8Str stateFilePathFull(config.strStateFile);
8578 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8579 if (RT_FAILURE(vrc))
8580 return setErrorBoth(E_FAIL, vrc,
8581 tr("Invalid saved state file path '%s' (%Rrc)"),
8582 config.strStateFile.c_str(),
8583 vrc);
8584 mSSData->strStateFilePath = stateFilePathFull;
8585 }
8586
8587 // snapshot folder needs special processing so set it again
8588 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8589 if (FAILED(rc)) return rc;
8590
8591 /* Copy the extra data items (config may or may not be the same as
8592 * mData->pMachineConfigFile) if necessary. When loading the XML files
8593 * from disk they are the same, but not for OVF import. */
8594 if (mData->pMachineConfigFile != &config)
8595 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8596
8597 /* currentStateModified (optional, default is true) */
8598 mData->mCurrentStateModified = config.fCurrentStateModified;
8599
8600 mData->mLastStateChange = config.timeLastStateChange;
8601
8602 /*
8603 * note: all mUserData members must be assigned prior this point because
8604 * we need to commit changes in order to let mUserData be shared by all
8605 * snapshot machine instances.
8606 */
8607 mUserData.commitCopy();
8608
8609 // machine registry, if present (must be loaded before snapshots)
8610 if (config.canHaveOwnMediaRegistry())
8611 {
8612 // determine machine folder
8613 Utf8Str strMachineFolder = i_getSettingsFileFull();
8614 strMachineFolder.stripFilename();
8615 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8616 config.mediaRegistry,
8617 strMachineFolder);
8618 if (FAILED(rc)) return rc;
8619 }
8620
8621 /* Snapshot node (optional) */
8622 size_t cRootSnapshots;
8623 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8624 {
8625 // there must be only one root snapshot
8626 Assert(cRootSnapshots == 1);
8627
8628 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8629
8630 rc = i_loadSnapshot(snap,
8631 config.uuidCurrentSnapshot,
8632 NULL); // no parent == first snapshot
8633 if (FAILED(rc)) return rc;
8634 }
8635
8636 // hardware data
8637 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8638 if (FAILED(rc)) return rc;
8639
8640 /*
8641 * NOTE: the assignment below must be the last thing to do,
8642 * otherwise it will be not possible to change the settings
8643 * somewhere in the code above because all setters will be
8644 * blocked by i_checkStateDependency(MutableStateDep).
8645 */
8646
8647 /* set the machine state to Aborted or Saved when appropriate */
8648 if (config.fAborted)
8649 {
8650 mSSData->strStateFilePath.setNull();
8651
8652 /* no need to use i_setMachineState() during init() */
8653 mData->mMachineState = MachineState_Aborted;
8654 }
8655 else if (!mSSData->strStateFilePath.isEmpty())
8656 {
8657 /* no need to use i_setMachineState() during init() */
8658 mData->mMachineState = MachineState_Saved;
8659 }
8660
8661 // after loading settings, we are no longer different from the XML on disk
8662 mData->flModifications = 0;
8663
8664 return S_OK;
8665}
8666
8667/**
8668 * Recursively loads all snapshots starting from the given.
8669 *
8670 * @param data snapshot settings.
8671 * @param aCurSnapshotId Current snapshot ID from the settings file.
8672 * @param aParentSnapshot Parent snapshot.
8673 */
8674HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8675 const Guid &aCurSnapshotId,
8676 Snapshot *aParentSnapshot)
8677{
8678 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8679 AssertReturn(!i_isSessionMachine(), E_FAIL);
8680
8681 HRESULT rc = S_OK;
8682
8683 Utf8Str strStateFile;
8684 if (!data.strStateFile.isEmpty())
8685 {
8686 /* optional */
8687 strStateFile = data.strStateFile;
8688 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8689 if (RT_FAILURE(vrc))
8690 return setErrorBoth(E_FAIL, vrc,
8691 tr("Invalid saved state file path '%s' (%Rrc)"),
8692 strStateFile.c_str(),
8693 vrc);
8694 }
8695
8696 /* create a snapshot machine object */
8697 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8698 pSnapshotMachine.createObject();
8699 rc = pSnapshotMachine->initFromSettings(this,
8700 data.hardware,
8701 &data.debugging,
8702 &data.autostart,
8703 data.uuid.ref(),
8704 strStateFile);
8705 if (FAILED(rc)) return rc;
8706
8707 /* create a snapshot object */
8708 ComObjPtr<Snapshot> pSnapshot;
8709 pSnapshot.createObject();
8710 /* initialize the snapshot */
8711 rc = pSnapshot->init(mParent, // VirtualBox object
8712 data.uuid,
8713 data.strName,
8714 data.strDescription,
8715 data.timestamp,
8716 pSnapshotMachine,
8717 aParentSnapshot);
8718 if (FAILED(rc)) return rc;
8719
8720 /* memorize the first snapshot if necessary */
8721 if (!mData->mFirstSnapshot)
8722 mData->mFirstSnapshot = pSnapshot;
8723
8724 /* memorize the current snapshot when appropriate */
8725 if ( !mData->mCurrentSnapshot
8726 && pSnapshot->i_getId() == aCurSnapshotId
8727 )
8728 mData->mCurrentSnapshot = pSnapshot;
8729
8730 // now create the children
8731 for (settings::SnapshotsList::const_iterator
8732 it = data.llChildSnapshots.begin();
8733 it != data.llChildSnapshots.end();
8734 ++it)
8735 {
8736 const settings::Snapshot &childData = *it;
8737 // recurse
8738 rc = i_loadSnapshot(childData,
8739 aCurSnapshotId,
8740 pSnapshot); // parent = the one we created above
8741 if (FAILED(rc)) return rc;
8742 }
8743
8744 return rc;
8745}
8746
8747/**
8748 * Loads settings into mHWData.
8749 *
8750 * @param puuidRegistry Registry ID.
8751 * @param puuidSnapshot Snapshot ID
8752 * @param data Reference to the hardware settings.
8753 * @param pDbg Pointer to the debugging settings.
8754 * @param pAutostart Pointer to the autostart settings.
8755 */
8756HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8757 const Guid *puuidSnapshot,
8758 const settings::Hardware &data,
8759 const settings::Debugging *pDbg,
8760 const settings::Autostart *pAutostart)
8761{
8762 AssertReturn(!i_isSessionMachine(), E_FAIL);
8763
8764 HRESULT rc = S_OK;
8765
8766 try
8767 {
8768 ComObjPtr<GuestOSType> pGuestOSType;
8769 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8770
8771 /* The hardware version attribute (optional). */
8772 mHWData->mHWVersion = data.strVersion;
8773 mHWData->mHardwareUUID = data.uuid;
8774
8775 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8776 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8777 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8778 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8779 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8780 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8781 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8782 mHWData->mPAEEnabled = data.fPAE;
8783 mHWData->mLongMode = data.enmLongMode;
8784 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8785 mHWData->mAPIC = data.fAPIC;
8786 mHWData->mX2APIC = data.fX2APIC;
8787 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8788 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8789 mHWData->mSpecCtrl = data.fSpecCtrl;
8790 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8791 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8792 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8793 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8794 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8795 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8796 mHWData->mCPUCount = data.cCPUs;
8797 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8798 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8799 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8800 mHWData->mCpuProfile = data.strCpuProfile;
8801
8802 // cpu
8803 if (mHWData->mCPUHotPlugEnabled)
8804 {
8805 for (settings::CpuList::const_iterator
8806 it = data.llCpus.begin();
8807 it != data.llCpus.end();
8808 ++it)
8809 {
8810 const settings::Cpu &cpu = *it;
8811
8812 mHWData->mCPUAttached[cpu.ulId] = true;
8813 }
8814 }
8815
8816 // cpuid leafs
8817 for (settings::CpuIdLeafsList::const_iterator
8818 it = data.llCpuIdLeafs.begin();
8819 it != data.llCpuIdLeafs.end();
8820 ++it)
8821 {
8822 const settings::CpuIdLeaf &rLeaf= *it;
8823 if ( rLeaf.idx < UINT32_C(0x20)
8824 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8825 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8826 mHWData->mCpuIdLeafList.push_back(rLeaf);
8827 /* else: just ignore */
8828 }
8829
8830 mHWData->mMemorySize = data.ulMemorySizeMB;
8831 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8832
8833 // boot order
8834 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8835 {
8836 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8837 if (it == data.mapBootOrder.end())
8838 mHWData->mBootOrder[i] = DeviceType_Null;
8839 else
8840 mHWData->mBootOrder[i] = it->second;
8841 }
8842
8843 mHWData->mFirmwareType = data.firmwareType;
8844 mHWData->mPointingHIDType = data.pointingHIDType;
8845 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8846 mHWData->mChipsetType = data.chipsetType;
8847 mHWData->mParavirtProvider = data.paravirtProvider;
8848 mHWData->mParavirtDebug = data.strParavirtDebug;
8849 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8850 mHWData->mHPETEnabled = data.fHPETEnabled;
8851
8852 /* GraphicsAdapter */
8853 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8854 if (FAILED(rc)) return rc;
8855
8856 /* VRDEServer */
8857 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8858 if (FAILED(rc)) return rc;
8859
8860 /* BIOS */
8861 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8862 if (FAILED(rc)) return rc;
8863
8864 /* Recording settings */
8865 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8866 if (FAILED(rc)) return rc;
8867
8868 // Bandwidth control (must come before network adapters)
8869 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8870 if (FAILED(rc)) return rc;
8871
8872 /* USB controllers */
8873 for (settings::USBControllerList::const_iterator
8874 it = data.usbSettings.llUSBControllers.begin();
8875 it != data.usbSettings.llUSBControllers.end();
8876 ++it)
8877 {
8878 const settings::USBController &settingsCtrl = *it;
8879 ComObjPtr<USBController> newCtrl;
8880
8881 newCtrl.createObject();
8882 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8883 mUSBControllers->push_back(newCtrl);
8884 }
8885
8886 /* USB device filters */
8887 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8888 if (FAILED(rc)) return rc;
8889
8890 // network adapters (establish array size first and apply defaults, to
8891 // ensure reading the same settings as we saved, since the list skips
8892 // adapters having defaults)
8893 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8894 size_t oldCount = mNetworkAdapters.size();
8895 if (newCount > oldCount)
8896 {
8897 mNetworkAdapters.resize(newCount);
8898 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8899 {
8900 unconst(mNetworkAdapters[slot]).createObject();
8901 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8902 }
8903 }
8904 else if (newCount < oldCount)
8905 mNetworkAdapters.resize(newCount);
8906 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8907 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8908 for (settings::NetworkAdaptersList::const_iterator
8909 it = data.llNetworkAdapters.begin();
8910 it != data.llNetworkAdapters.end();
8911 ++it)
8912 {
8913 const settings::NetworkAdapter &nic = *it;
8914
8915 /* slot uniqueness is guaranteed by XML Schema */
8916 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8917 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8918 if (FAILED(rc)) return rc;
8919 }
8920
8921 // serial ports (establish defaults first, to ensure reading the same
8922 // settings as we saved, since the list skips ports having defaults)
8923 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8924 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8925 for (settings::SerialPortsList::const_iterator
8926 it = data.llSerialPorts.begin();
8927 it != data.llSerialPorts.end();
8928 ++it)
8929 {
8930 const settings::SerialPort &s = *it;
8931
8932 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8933 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8934 if (FAILED(rc)) return rc;
8935 }
8936
8937 // parallel ports (establish defaults first, to ensure reading the same
8938 // settings as we saved, since the list skips ports having defaults)
8939 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8940 mParallelPorts[i]->i_applyDefaults();
8941 for (settings::ParallelPortsList::const_iterator
8942 it = data.llParallelPorts.begin();
8943 it != data.llParallelPorts.end();
8944 ++it)
8945 {
8946 const settings::ParallelPort &p = *it;
8947
8948 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8949 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8950 if (FAILED(rc)) return rc;
8951 }
8952
8953 /* AudioAdapter */
8954 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8955 if (FAILED(rc)) return rc;
8956
8957 /* storage controllers */
8958 rc = i_loadStorageControllers(data.storage,
8959 puuidRegistry,
8960 puuidSnapshot);
8961 if (FAILED(rc)) return rc;
8962
8963 /* Shared folders */
8964 for (settings::SharedFoldersList::const_iterator
8965 it = data.llSharedFolders.begin();
8966 it != data.llSharedFolders.end();
8967 ++it)
8968 {
8969 const settings::SharedFolder &sf = *it;
8970
8971 ComObjPtr<SharedFolder> sharedFolder;
8972 /* Check for double entries. Not allowed! */
8973 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8974 if (SUCCEEDED(rc))
8975 return setError(VBOX_E_OBJECT_IN_USE,
8976 tr("Shared folder named '%s' already exists"),
8977 sf.strName.c_str());
8978
8979 /* Create the new shared folder. Don't break on error. This will be
8980 * reported when the machine starts. */
8981 sharedFolder.createObject();
8982 rc = sharedFolder->init(i_getMachine(),
8983 sf.strName,
8984 sf.strHostPath,
8985 RT_BOOL(sf.fWritable),
8986 RT_BOOL(sf.fAutoMount),
8987 sf.strAutoMountPoint,
8988 false /* fFailOnError */);
8989 if (FAILED(rc)) return rc;
8990 mHWData->mSharedFolders.push_back(sharedFolder);
8991 }
8992
8993 // Clipboard
8994 mHWData->mClipboardMode = data.clipboardMode;
8995 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8996
8997 // drag'n'drop
8998 mHWData->mDnDMode = data.dndMode;
8999
9000 // guest settings
9001 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9002
9003 // IO settings
9004 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9005 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9006
9007 // Host PCI devices
9008 for (settings::HostPCIDeviceAttachmentList::const_iterator
9009 it = data.pciAttachments.begin();
9010 it != data.pciAttachments.end();
9011 ++it)
9012 {
9013 const settings::HostPCIDeviceAttachment &hpda = *it;
9014 ComObjPtr<PCIDeviceAttachment> pda;
9015
9016 pda.createObject();
9017 pda->i_loadSettings(this, hpda);
9018 mHWData->mPCIDeviceAssignments.push_back(pda);
9019 }
9020
9021 /*
9022 * (The following isn't really real hardware, but it lives in HWData
9023 * for reasons of convenience.)
9024 */
9025
9026#ifdef VBOX_WITH_GUEST_PROPS
9027 /* Guest properties (optional) */
9028
9029 /* Only load transient guest properties for configs which have saved
9030 * state, because there shouldn't be any for powered off VMs. The same
9031 * logic applies for snapshots, as offline snapshots shouldn't have
9032 * any such properties. They confuse the code in various places.
9033 * Note: can't rely on the machine state, as it isn't set yet. */
9034 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9035 /* apologies for the hacky unconst() usage, but this needs hacking
9036 * actually inconsistent settings into consistency, otherwise there
9037 * will be some corner cases where the inconsistency survives
9038 * surprisingly long without getting fixed, especially for snapshots
9039 * as there are no config changes. */
9040 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9041 for (settings::GuestPropertiesList::iterator
9042 it = llGuestProperties.begin();
9043 it != llGuestProperties.end();
9044 /*nothing*/)
9045 {
9046 const settings::GuestProperty &prop = *it;
9047 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9048 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9049 if ( fSkipTransientGuestProperties
9050 && ( fFlags & GUEST_PROP_F_TRANSIENT
9051 || fFlags & GUEST_PROP_F_TRANSRESET))
9052 {
9053 it = llGuestProperties.erase(it);
9054 continue;
9055 }
9056 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9057 mHWData->mGuestProperties[prop.strName] = property;
9058 ++it;
9059 }
9060#endif /* VBOX_WITH_GUEST_PROPS defined */
9061
9062 rc = i_loadDebugging(pDbg);
9063 if (FAILED(rc))
9064 return rc;
9065
9066 mHWData->mAutostart = *pAutostart;
9067
9068 /* default frontend */
9069 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9070 }
9071 catch (std::bad_alloc &)
9072 {
9073 return E_OUTOFMEMORY;
9074 }
9075
9076 AssertComRC(rc);
9077 return rc;
9078}
9079
9080/**
9081 * Called from i_loadHardware() to load the debugging settings of the
9082 * machine.
9083 *
9084 * @param pDbg Pointer to the settings.
9085 */
9086HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9087{
9088 mHWData->mDebugging = *pDbg;
9089 /* no more processing currently required, this will probably change. */
9090 return S_OK;
9091}
9092
9093/**
9094 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9095 *
9096 * @param data storage settings.
9097 * @param puuidRegistry media registry ID to set media to or NULL;
9098 * see Machine::i_loadMachineDataFromSettings()
9099 * @param puuidSnapshot snapshot ID
9100 * @return
9101 */
9102HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9103 const Guid *puuidRegistry,
9104 const Guid *puuidSnapshot)
9105{
9106 AssertReturn(!i_isSessionMachine(), E_FAIL);
9107
9108 HRESULT rc = S_OK;
9109
9110 for (settings::StorageControllersList::const_iterator
9111 it = data.llStorageControllers.begin();
9112 it != data.llStorageControllers.end();
9113 ++it)
9114 {
9115 const settings::StorageController &ctlData = *it;
9116
9117 ComObjPtr<StorageController> pCtl;
9118 /* Try to find one with the name first. */
9119 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9120 if (SUCCEEDED(rc))
9121 return setError(VBOX_E_OBJECT_IN_USE,
9122 tr("Storage controller named '%s' already exists"),
9123 ctlData.strName.c_str());
9124
9125 pCtl.createObject();
9126 rc = pCtl->init(this,
9127 ctlData.strName,
9128 ctlData.storageBus,
9129 ctlData.ulInstance,
9130 ctlData.fBootable);
9131 if (FAILED(rc)) return rc;
9132
9133 mStorageControllers->push_back(pCtl);
9134
9135 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9136 if (FAILED(rc)) return rc;
9137
9138 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9139 if (FAILED(rc)) return rc;
9140
9141 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9142 if (FAILED(rc)) return rc;
9143
9144 /* Load the attached devices now. */
9145 rc = i_loadStorageDevices(pCtl,
9146 ctlData,
9147 puuidRegistry,
9148 puuidSnapshot);
9149 if (FAILED(rc)) return rc;
9150 }
9151
9152 return S_OK;
9153}
9154
9155/**
9156 * Called from i_loadStorageControllers for a controller's devices.
9157 *
9158 * @param aStorageController
9159 * @param data
9160 * @param puuidRegistry media registry ID to set media to or NULL; see
9161 * Machine::i_loadMachineDataFromSettings()
9162 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9163 * @return
9164 */
9165HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9166 const settings::StorageController &data,
9167 const Guid *puuidRegistry,
9168 const Guid *puuidSnapshot)
9169{
9170 HRESULT rc = S_OK;
9171
9172 /* paranoia: detect duplicate attachments */
9173 for (settings::AttachedDevicesList::const_iterator
9174 it = data.llAttachedDevices.begin();
9175 it != data.llAttachedDevices.end();
9176 ++it)
9177 {
9178 const settings::AttachedDevice &ad = *it;
9179
9180 for (settings::AttachedDevicesList::const_iterator it2 = it;
9181 it2 != data.llAttachedDevices.end();
9182 ++it2)
9183 {
9184 if (it == it2)
9185 continue;
9186
9187 const settings::AttachedDevice &ad2 = *it2;
9188
9189 if ( ad.lPort == ad2.lPort
9190 && ad.lDevice == ad2.lDevice)
9191 {
9192 return setError(E_FAIL,
9193 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9194 aStorageController->i_getName().c_str(),
9195 ad.lPort,
9196 ad.lDevice,
9197 mUserData->s.strName.c_str());
9198 }
9199 }
9200 }
9201
9202 for (settings::AttachedDevicesList::const_iterator
9203 it = data.llAttachedDevices.begin();
9204 it != data.llAttachedDevices.end();
9205 ++it)
9206 {
9207 const settings::AttachedDevice &dev = *it;
9208 ComObjPtr<Medium> medium;
9209
9210 switch (dev.deviceType)
9211 {
9212 case DeviceType_Floppy:
9213 case DeviceType_DVD:
9214 if (dev.strHostDriveSrc.isNotEmpty())
9215 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9216 false /* fRefresh */, medium);
9217 else
9218 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9219 dev.uuid,
9220 false /* fRefresh */,
9221 false /* aSetError */,
9222 medium);
9223 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9224 // This is not an error. The host drive or UUID might have vanished, so just go
9225 // ahead without this removeable medium attachment
9226 rc = S_OK;
9227 break;
9228
9229 case DeviceType_HardDisk:
9230 {
9231 /* find a hard disk by UUID */
9232 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9233 if (FAILED(rc))
9234 {
9235 if (i_isSnapshotMachine())
9236 {
9237 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9238 // so the user knows that the bad disk is in a snapshot somewhere
9239 com::ErrorInfo info;
9240 return setError(E_FAIL,
9241 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9242 puuidSnapshot->raw(),
9243 info.getText().raw());
9244 }
9245 else
9246 return rc;
9247 }
9248
9249 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9250
9251 if (medium->i_getType() == MediumType_Immutable)
9252 {
9253 if (i_isSnapshotMachine())
9254 return setError(E_FAIL,
9255 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9256 "of the virtual machine '%s' ('%s')"),
9257 medium->i_getLocationFull().c_str(),
9258 dev.uuid.raw(),
9259 puuidSnapshot->raw(),
9260 mUserData->s.strName.c_str(),
9261 mData->m_strConfigFileFull.c_str());
9262
9263 return setError(E_FAIL,
9264 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9265 medium->i_getLocationFull().c_str(),
9266 dev.uuid.raw(),
9267 mUserData->s.strName.c_str(),
9268 mData->m_strConfigFileFull.c_str());
9269 }
9270
9271 if (medium->i_getType() == MediumType_MultiAttach)
9272 {
9273 if (i_isSnapshotMachine())
9274 return setError(E_FAIL,
9275 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9276 "of the virtual machine '%s' ('%s')"),
9277 medium->i_getLocationFull().c_str(),
9278 dev.uuid.raw(),
9279 puuidSnapshot->raw(),
9280 mUserData->s.strName.c_str(),
9281 mData->m_strConfigFileFull.c_str());
9282
9283 return setError(E_FAIL,
9284 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9285 medium->i_getLocationFull().c_str(),
9286 dev.uuid.raw(),
9287 mUserData->s.strName.c_str(),
9288 mData->m_strConfigFileFull.c_str());
9289 }
9290
9291 if ( !i_isSnapshotMachine()
9292 && medium->i_getChildren().size() != 0
9293 )
9294 return setError(E_FAIL,
9295 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9296 "because it has %d differencing child hard disks"),
9297 medium->i_getLocationFull().c_str(),
9298 dev.uuid.raw(),
9299 mUserData->s.strName.c_str(),
9300 mData->m_strConfigFileFull.c_str(),
9301 medium->i_getChildren().size());
9302
9303 if (i_findAttachment(*mMediumAttachments.data(),
9304 medium))
9305 return setError(E_FAIL,
9306 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9307 medium->i_getLocationFull().c_str(),
9308 dev.uuid.raw(),
9309 mUserData->s.strName.c_str(),
9310 mData->m_strConfigFileFull.c_str());
9311
9312 break;
9313 }
9314
9315 default:
9316 return setError(E_FAIL,
9317 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9318 medium->i_getLocationFull().c_str(),
9319 mUserData->s.strName.c_str(),
9320 mData->m_strConfigFileFull.c_str());
9321 }
9322
9323 if (FAILED(rc))
9324 break;
9325
9326 /* Bandwidth groups are loaded at this point. */
9327 ComObjPtr<BandwidthGroup> pBwGroup;
9328
9329 if (!dev.strBwGroup.isEmpty())
9330 {
9331 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9332 if (FAILED(rc))
9333 return setError(E_FAIL,
9334 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9335 medium->i_getLocationFull().c_str(),
9336 dev.strBwGroup.c_str(),
9337 mUserData->s.strName.c_str(),
9338 mData->m_strConfigFileFull.c_str());
9339 pBwGroup->i_reference();
9340 }
9341
9342 const Utf8Str controllerName = aStorageController->i_getName();
9343 ComObjPtr<MediumAttachment> pAttachment;
9344 pAttachment.createObject();
9345 rc = pAttachment->init(this,
9346 medium,
9347 controllerName,
9348 dev.lPort,
9349 dev.lDevice,
9350 dev.deviceType,
9351 false,
9352 dev.fPassThrough,
9353 dev.fTempEject,
9354 dev.fNonRotational,
9355 dev.fDiscard,
9356 dev.fHotPluggable,
9357 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9358 if (FAILED(rc)) break;
9359
9360 /* associate the medium with this machine and snapshot */
9361 if (!medium.isNull())
9362 {
9363 AutoCaller medCaller(medium);
9364 if (FAILED(medCaller.rc())) return medCaller.rc();
9365 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9366
9367 if (i_isSnapshotMachine())
9368 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9369 else
9370 rc = medium->i_addBackReference(mData->mUuid);
9371 /* If the medium->addBackReference fails it sets an appropriate
9372 * error message, so no need to do any guesswork here. */
9373
9374 if (puuidRegistry)
9375 // caller wants registry ID to be set on all attached media (OVF import case)
9376 medium->i_addRegistry(*puuidRegistry);
9377 }
9378
9379 if (FAILED(rc))
9380 break;
9381
9382 /* back up mMediumAttachments to let registeredInit() properly rollback
9383 * on failure (= limited accessibility) */
9384 i_setModified(IsModified_Storage);
9385 mMediumAttachments.backup();
9386 mMediumAttachments->push_back(pAttachment);
9387 }
9388
9389 return rc;
9390}
9391
9392/**
9393 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9394 *
9395 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9396 * @param aSnapshot where to return the found snapshot
9397 * @param aSetError true to set extended error info on failure
9398 */
9399HRESULT Machine::i_findSnapshotById(const Guid &aId,
9400 ComObjPtr<Snapshot> &aSnapshot,
9401 bool aSetError /* = false */)
9402{
9403 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9404
9405 if (!mData->mFirstSnapshot)
9406 {
9407 if (aSetError)
9408 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9409 return E_FAIL;
9410 }
9411
9412 if (aId.isZero())
9413 aSnapshot = mData->mFirstSnapshot;
9414 else
9415 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9416
9417 if (!aSnapshot)
9418 {
9419 if (aSetError)
9420 return setError(E_FAIL,
9421 tr("Could not find a snapshot with UUID {%s}"),
9422 aId.toString().c_str());
9423 return E_FAIL;
9424 }
9425
9426 return S_OK;
9427}
9428
9429/**
9430 * Returns the snapshot with the given name or fails of no such snapshot.
9431 *
9432 * @param strName snapshot name to find
9433 * @param aSnapshot where to return the found snapshot
9434 * @param aSetError true to set extended error info on failure
9435 */
9436HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9437 ComObjPtr<Snapshot> &aSnapshot,
9438 bool aSetError /* = false */)
9439{
9440 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9441
9442 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9443
9444 if (!mData->mFirstSnapshot)
9445 {
9446 if (aSetError)
9447 return setError(VBOX_E_OBJECT_NOT_FOUND,
9448 tr("This machine does not have any snapshots"));
9449 return VBOX_E_OBJECT_NOT_FOUND;
9450 }
9451
9452 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9453
9454 if (!aSnapshot)
9455 {
9456 if (aSetError)
9457 return setError(VBOX_E_OBJECT_NOT_FOUND,
9458 tr("Could not find a snapshot named '%s'"), strName.c_str());
9459 return VBOX_E_OBJECT_NOT_FOUND;
9460 }
9461
9462 return S_OK;
9463}
9464
9465/**
9466 * Returns a storage controller object with the given name.
9467 *
9468 * @param aName storage controller name to find
9469 * @param aStorageController where to return the found storage controller
9470 * @param aSetError true to set extended error info on failure
9471 */
9472HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9473 ComObjPtr<StorageController> &aStorageController,
9474 bool aSetError /* = false */)
9475{
9476 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9477
9478 for (StorageControllerList::const_iterator
9479 it = mStorageControllers->begin();
9480 it != mStorageControllers->end();
9481 ++it)
9482 {
9483 if ((*it)->i_getName() == aName)
9484 {
9485 aStorageController = (*it);
9486 return S_OK;
9487 }
9488 }
9489
9490 if (aSetError)
9491 return setError(VBOX_E_OBJECT_NOT_FOUND,
9492 tr("Could not find a storage controller named '%s'"),
9493 aName.c_str());
9494 return VBOX_E_OBJECT_NOT_FOUND;
9495}
9496
9497/**
9498 * Returns a USB controller object with the given name.
9499 *
9500 * @param aName USB controller name to find
9501 * @param aUSBController where to return the found USB controller
9502 * @param aSetError true to set extended error info on failure
9503 */
9504HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9505 ComObjPtr<USBController> &aUSBController,
9506 bool aSetError /* = false */)
9507{
9508 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9509
9510 for (USBControllerList::const_iterator
9511 it = mUSBControllers->begin();
9512 it != mUSBControllers->end();
9513 ++it)
9514 {
9515 if ((*it)->i_getName() == aName)
9516 {
9517 aUSBController = (*it);
9518 return S_OK;
9519 }
9520 }
9521
9522 if (aSetError)
9523 return setError(VBOX_E_OBJECT_NOT_FOUND,
9524 tr("Could not find a storage controller named '%s'"),
9525 aName.c_str());
9526 return VBOX_E_OBJECT_NOT_FOUND;
9527}
9528
9529/**
9530 * Returns the number of USB controller instance of the given type.
9531 *
9532 * @param enmType USB controller type.
9533 */
9534ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9535{
9536 ULONG cCtrls = 0;
9537
9538 for (USBControllerList::const_iterator
9539 it = mUSBControllers->begin();
9540 it != mUSBControllers->end();
9541 ++it)
9542 {
9543 if ((*it)->i_getControllerType() == enmType)
9544 cCtrls++;
9545 }
9546
9547 return cCtrls;
9548}
9549
9550HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9551 MediumAttachmentList &atts)
9552{
9553 AutoCaller autoCaller(this);
9554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9555
9556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9557
9558 for (MediumAttachmentList::const_iterator
9559 it = mMediumAttachments->begin();
9560 it != mMediumAttachments->end();
9561 ++it)
9562 {
9563 const ComObjPtr<MediumAttachment> &pAtt = *it;
9564 // should never happen, but deal with NULL pointers in the list.
9565 AssertContinue(!pAtt.isNull());
9566
9567 // getControllerName() needs caller+read lock
9568 AutoCaller autoAttCaller(pAtt);
9569 if (FAILED(autoAttCaller.rc()))
9570 {
9571 atts.clear();
9572 return autoAttCaller.rc();
9573 }
9574 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9575
9576 if (pAtt->i_getControllerName() == aName)
9577 atts.push_back(pAtt);
9578 }
9579
9580 return S_OK;
9581}
9582
9583
9584/**
9585 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9586 * file if the machine name was changed and about creating a new settings file
9587 * if this is a new machine.
9588 *
9589 * @note Must be never called directly but only from #saveSettings().
9590 */
9591HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9592{
9593 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9594
9595 HRESULT rc = S_OK;
9596
9597 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9598
9599 /// @todo need to handle primary group change, too
9600
9601 /* attempt to rename the settings file if machine name is changed */
9602 if ( mUserData->s.fNameSync
9603 && mUserData.isBackedUp()
9604 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9605 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9606 )
9607 {
9608 bool dirRenamed = false;
9609 bool fileRenamed = false;
9610
9611 Utf8Str configFile, newConfigFile;
9612 Utf8Str configFilePrev, newConfigFilePrev;
9613 Utf8Str NVRAMFile, newNVRAMFile;
9614 Utf8Str configDir, newConfigDir;
9615
9616 do
9617 {
9618 int vrc = VINF_SUCCESS;
9619
9620 Utf8Str name = mUserData.backedUpData()->s.strName;
9621 Utf8Str newName = mUserData->s.strName;
9622 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9623 if (group == "/")
9624 group.setNull();
9625 Utf8Str newGroup = mUserData->s.llGroups.front();
9626 if (newGroup == "/")
9627 newGroup.setNull();
9628
9629 configFile = mData->m_strConfigFileFull;
9630
9631 /* first, rename the directory if it matches the group and machine name */
9632 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9633 group.c_str(), RTPATH_DELIMITER, name.c_str());
9634 /** @todo hack, make somehow use of ComposeMachineFilename */
9635 if (mUserData->s.fDirectoryIncludesUUID)
9636 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9637 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9638 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9639 /** @todo hack, make somehow use of ComposeMachineFilename */
9640 if (mUserData->s.fDirectoryIncludesUUID)
9641 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9642 configDir = configFile;
9643 configDir.stripFilename();
9644 newConfigDir = configDir;
9645 if ( configDir.length() >= groupPlusName.length()
9646 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9647 groupPlusName.c_str()))
9648 {
9649 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9650 Utf8Str newConfigBaseDir(newConfigDir);
9651 newConfigDir.append(newGroupPlusName);
9652 /* consistency: use \ if appropriate on the platform */
9653 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9654 /* new dir and old dir cannot be equal here because of 'if'
9655 * above and because name != newName */
9656 Assert(configDir != newConfigDir);
9657 if (!fSettingsFileIsNew)
9658 {
9659 /* perform real rename only if the machine is not new */
9660 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9661 if ( vrc == VERR_FILE_NOT_FOUND
9662 || vrc == VERR_PATH_NOT_FOUND)
9663 {
9664 /* create the parent directory, then retry renaming */
9665 Utf8Str parent(newConfigDir);
9666 parent.stripFilename();
9667 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9668 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9669 }
9670 if (RT_FAILURE(vrc))
9671 {
9672 rc = setErrorBoth(E_FAIL, vrc,
9673 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9674 configDir.c_str(),
9675 newConfigDir.c_str(),
9676 vrc);
9677 break;
9678 }
9679 /* delete subdirectories which are no longer needed */
9680 Utf8Str dir(configDir);
9681 dir.stripFilename();
9682 while (dir != newConfigBaseDir && dir != ".")
9683 {
9684 vrc = RTDirRemove(dir.c_str());
9685 if (RT_FAILURE(vrc))
9686 break;
9687 dir.stripFilename();
9688 }
9689 dirRenamed = true;
9690 }
9691 }
9692
9693 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9694 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9695
9696 /* then try to rename the settings file itself */
9697 if (newConfigFile != configFile)
9698 {
9699 /* get the path to old settings file in renamed directory */
9700 configFile = Utf8StrFmt("%s%c%s",
9701 newConfigDir.c_str(),
9702 RTPATH_DELIMITER,
9703 RTPathFilename(configFile.c_str()));
9704 if (!fSettingsFileIsNew)
9705 {
9706 /* perform real rename only if the machine is not new */
9707 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9708 if (RT_FAILURE(vrc))
9709 {
9710 rc = setErrorBoth(E_FAIL, vrc,
9711 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9712 configFile.c_str(),
9713 newConfigFile.c_str(),
9714 vrc);
9715 break;
9716 }
9717 fileRenamed = true;
9718 configFilePrev = configFile;
9719 configFilePrev += "-prev";
9720 newConfigFilePrev = newConfigFile;
9721 newConfigFilePrev += "-prev";
9722 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9723 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9724 if (NVRAMFile.isNotEmpty())
9725 {
9726 // in the NVRAM file path, replace the old directory with the new directory
9727 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9728 {
9729 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9730 NVRAMFile = newConfigDir + strNVRAMFile;
9731 }
9732 newNVRAMFile = newConfigFile;
9733 newNVRAMFile.stripSuffix();
9734 newNVRAMFile += ".nvram";
9735 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9736 }
9737 }
9738 }
9739
9740 // update m_strConfigFileFull amd mConfigFile
9741 mData->m_strConfigFileFull = newConfigFile;
9742 // compute the relative path too
9743 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9744
9745 // store the old and new so that VirtualBox::i_saveSettings() can update
9746 // the media registry
9747 if ( mData->mRegistered
9748 && (configDir != newConfigDir || configFile != newConfigFile))
9749 {
9750 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9751
9752 if (pfNeedsGlobalSaveSettings)
9753 *pfNeedsGlobalSaveSettings = true;
9754 }
9755
9756 // in the saved state file path, replace the old directory with the new directory
9757 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9758 {
9759 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9760 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9761 }
9762 if (newNVRAMFile.isNotEmpty())
9763 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9764
9765 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9766 if (mData->mFirstSnapshot)
9767 {
9768 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9769 newConfigDir.c_str());
9770 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9771 newConfigDir.c_str());
9772 }
9773 }
9774 while (0);
9775
9776 if (FAILED(rc))
9777 {
9778 /* silently try to rename everything back */
9779 if (fileRenamed)
9780 {
9781 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9782 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9783 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9784 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9785 }
9786 if (dirRenamed)
9787 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9788 }
9789
9790 if (FAILED(rc)) return rc;
9791 }
9792
9793 if (fSettingsFileIsNew)
9794 {
9795 /* create a virgin config file */
9796 int vrc = VINF_SUCCESS;
9797
9798 /* ensure the settings directory exists */
9799 Utf8Str path(mData->m_strConfigFileFull);
9800 path.stripFilename();
9801 if (!RTDirExists(path.c_str()))
9802 {
9803 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9804 if (RT_FAILURE(vrc))
9805 {
9806 return setErrorBoth(E_FAIL, vrc,
9807 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9808 path.c_str(),
9809 vrc);
9810 }
9811 }
9812
9813 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9814 path = Utf8Str(mData->m_strConfigFileFull);
9815 RTFILE f = NIL_RTFILE;
9816 vrc = RTFileOpen(&f, path.c_str(),
9817 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9818 if (RT_FAILURE(vrc))
9819 return setErrorBoth(E_FAIL, vrc,
9820 tr("Could not create the settings file '%s' (%Rrc)"),
9821 path.c_str(),
9822 vrc);
9823 RTFileClose(f);
9824 }
9825
9826 return rc;
9827}
9828
9829/**
9830 * Saves and commits machine data, user data and hardware data.
9831 *
9832 * Note that on failure, the data remains uncommitted.
9833 *
9834 * @a aFlags may combine the following flags:
9835 *
9836 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9837 * Used when saving settings after an operation that makes them 100%
9838 * correspond to the settings from the current snapshot.
9839 * - SaveS_Force: settings will be saved without doing a deep compare of the
9840 * settings structures. This is used when this is called because snapshots
9841 * have changed to avoid the overhead of the deep compare.
9842 *
9843 * @note Must be called from under this object's write lock. Locks children for
9844 * writing.
9845 *
9846 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9847 * initialized to false and that will be set to true by this function if
9848 * the caller must invoke VirtualBox::i_saveSettings() because the global
9849 * settings have changed. This will happen if a machine rename has been
9850 * saved and the global machine and media registries will therefore need
9851 * updating.
9852 * @param aFlags Flags.
9853 */
9854HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9855 int aFlags /*= 0*/)
9856{
9857 LogFlowThisFuncEnter();
9858
9859 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9860
9861 /* make sure child objects are unable to modify the settings while we are
9862 * saving them */
9863 i_ensureNoStateDependencies();
9864
9865 AssertReturn(!i_isSnapshotMachine(),
9866 E_FAIL);
9867
9868 if (!mData->mAccessible)
9869 return setError(VBOX_E_INVALID_VM_STATE,
9870 tr("The machine is not accessible, so cannot save settings"));
9871
9872 HRESULT rc = S_OK;
9873 bool fNeedsWrite = false;
9874
9875 /* First, prepare to save settings. It will care about renaming the
9876 * settings directory and file if the machine name was changed and about
9877 * creating a new settings file if this is a new machine. */
9878 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9879 if (FAILED(rc)) return rc;
9880
9881 // keep a pointer to the current settings structures
9882 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9883 settings::MachineConfigFile *pNewConfig = NULL;
9884
9885 try
9886 {
9887 // make a fresh one to have everyone write stuff into
9888 pNewConfig = new settings::MachineConfigFile(NULL);
9889 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9890
9891 // now go and copy all the settings data from COM to the settings structures
9892 // (this calls i_saveSettings() on all the COM objects in the machine)
9893 i_copyMachineDataToSettings(*pNewConfig);
9894
9895 if (aFlags & SaveS_ResetCurStateModified)
9896 {
9897 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9898 mData->mCurrentStateModified = FALSE;
9899 fNeedsWrite = true; // always, no need to compare
9900 }
9901 else if (aFlags & SaveS_Force)
9902 {
9903 fNeedsWrite = true; // always, no need to compare
9904 }
9905 else
9906 {
9907 if (!mData->mCurrentStateModified)
9908 {
9909 // do a deep compare of the settings that we just saved with the settings
9910 // previously stored in the config file; this invokes MachineConfigFile::operator==
9911 // which does a deep compare of all the settings, which is expensive but less expensive
9912 // than writing out XML in vain
9913 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9914
9915 // could still be modified if any settings changed
9916 mData->mCurrentStateModified = fAnySettingsChanged;
9917
9918 fNeedsWrite = fAnySettingsChanged;
9919 }
9920 else
9921 fNeedsWrite = true;
9922 }
9923
9924 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9925
9926 if (fNeedsWrite)
9927 // now spit it all out!
9928 pNewConfig->write(mData->m_strConfigFileFull);
9929
9930 mData->pMachineConfigFile = pNewConfig;
9931 delete pOldConfig;
9932 i_commit();
9933
9934 // after saving settings, we are no longer different from the XML on disk
9935 mData->flModifications = 0;
9936 }
9937 catch (HRESULT err)
9938 {
9939 // we assume that error info is set by the thrower
9940 rc = err;
9941
9942 // restore old config
9943 delete pNewConfig;
9944 mData->pMachineConfigFile = pOldConfig;
9945 }
9946 catch (...)
9947 {
9948 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9949 }
9950
9951 if (fNeedsWrite)
9952 {
9953 /* Fire the data change event, even on failure (since we've already
9954 * committed all data). This is done only for SessionMachines because
9955 * mutable Machine instances are always not registered (i.e. private
9956 * to the client process that creates them) and thus don't need to
9957 * inform callbacks. */
9958 if (i_isSessionMachine())
9959 mParent->i_onMachineDataChange(mData->mUuid);
9960 }
9961
9962 LogFlowThisFunc(("rc=%08X\n", rc));
9963 LogFlowThisFuncLeave();
9964 return rc;
9965}
9966
9967/**
9968 * Implementation for saving the machine settings into the given
9969 * settings::MachineConfigFile instance. This copies machine extradata
9970 * from the previous machine config file in the instance data, if any.
9971 *
9972 * This gets called from two locations:
9973 *
9974 * -- Machine::i_saveSettings(), during the regular XML writing;
9975 *
9976 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9977 * exported to OVF and we write the VirtualBox proprietary XML
9978 * into a <vbox:Machine> tag.
9979 *
9980 * This routine fills all the fields in there, including snapshots, *except*
9981 * for the following:
9982 *
9983 * -- fCurrentStateModified. There is some special logic associated with that.
9984 *
9985 * The caller can then call MachineConfigFile::write() or do something else
9986 * with it.
9987 *
9988 * Caller must hold the machine lock!
9989 *
9990 * This throws XML errors and HRESULT, so the caller must have a catch block!
9991 */
9992void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9993{
9994 // deep copy extradata, being extra careful with self assignment (the STL
9995 // map assignment on Mac OS X clang based Xcode isn't checking)
9996 if (&config != mData->pMachineConfigFile)
9997 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9998
9999 config.uuid = mData->mUuid;
10000
10001 // copy name, description, OS type, teleport, UTC etc.
10002 config.machineUserData = mUserData->s;
10003
10004 if ( mData->mMachineState == MachineState_Saved
10005 || mData->mMachineState == MachineState_Restoring
10006 // when doing certain snapshot operations we may or may not have
10007 // a saved state in the current state, so keep everything as is
10008 || ( ( mData->mMachineState == MachineState_Snapshotting
10009 || mData->mMachineState == MachineState_DeletingSnapshot
10010 || mData->mMachineState == MachineState_RestoringSnapshot)
10011 && (!mSSData->strStateFilePath.isEmpty())
10012 )
10013 )
10014 {
10015 Assert(!mSSData->strStateFilePath.isEmpty());
10016 /* try to make the file name relative to the settings file dir */
10017 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10018 }
10019 else
10020 {
10021 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10022 config.strStateFile.setNull();
10023 }
10024
10025 if (mData->mCurrentSnapshot)
10026 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10027 else
10028 config.uuidCurrentSnapshot.clear();
10029
10030 config.timeLastStateChange = mData->mLastStateChange;
10031 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10032 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10033
10034 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10035 if (FAILED(rc)) throw rc;
10036
10037 // save machine's media registry if this is VirtualBox 4.0 or later
10038 if (config.canHaveOwnMediaRegistry())
10039 {
10040 // determine machine folder
10041 Utf8Str strMachineFolder = i_getSettingsFileFull();
10042 strMachineFolder.stripFilename();
10043 mParent->i_saveMediaRegistry(config.mediaRegistry,
10044 i_getId(), // only media with registry ID == machine UUID
10045 strMachineFolder);
10046 // this throws HRESULT
10047 }
10048
10049 // save snapshots
10050 rc = i_saveAllSnapshots(config);
10051 if (FAILED(rc)) throw rc;
10052}
10053
10054/**
10055 * Saves all snapshots of the machine into the given machine config file. Called
10056 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10057 * @param config
10058 * @return
10059 */
10060HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10061{
10062 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10063
10064 HRESULT rc = S_OK;
10065
10066 try
10067 {
10068 config.llFirstSnapshot.clear();
10069
10070 if (mData->mFirstSnapshot)
10071 {
10072 // the settings use a list for "the first snapshot"
10073 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10074
10075 // get reference to the snapshot on the list and work on that
10076 // element straight in the list to avoid excessive copying later
10077 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10078 if (FAILED(rc)) throw rc;
10079 }
10080
10081// if (mType == IsSessionMachine)
10082// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10083
10084 }
10085 catch (HRESULT err)
10086 {
10087 /* we assume that error info is set by the thrower */
10088 rc = err;
10089 }
10090 catch (...)
10091 {
10092 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10093 }
10094
10095 return rc;
10096}
10097
10098/**
10099 * Saves the VM hardware configuration. It is assumed that the
10100 * given node is empty.
10101 *
10102 * @param data Reference to the settings object for the hardware config.
10103 * @param pDbg Pointer to the settings object for the debugging config
10104 * which happens to live in mHWData.
10105 * @param pAutostart Pointer to the settings object for the autostart config
10106 * which happens to live in mHWData.
10107 */
10108HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10109 settings::Autostart *pAutostart)
10110{
10111 HRESULT rc = S_OK;
10112
10113 try
10114 {
10115 /* The hardware version attribute (optional).
10116 Automatically upgrade from 1 to current default hardware version
10117 when there is no saved state. (ugly!) */
10118 if ( mHWData->mHWVersion == "1"
10119 && mSSData->strStateFilePath.isEmpty()
10120 )
10121 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10122
10123 data.strVersion = mHWData->mHWVersion;
10124 data.uuid = mHWData->mHardwareUUID;
10125
10126 // CPU
10127 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10128 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10129 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10130 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10131 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10132 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10133 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10134 data.fPAE = !!mHWData->mPAEEnabled;
10135 data.enmLongMode = mHWData->mLongMode;
10136 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10137 data.fAPIC = !!mHWData->mAPIC;
10138 data.fX2APIC = !!mHWData->mX2APIC;
10139 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10140 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10141 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10142 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10143 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10144 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10145 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10146 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10147 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10148 data.cCPUs = mHWData->mCPUCount;
10149 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10150 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10151 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10152 data.strCpuProfile = mHWData->mCpuProfile;
10153
10154 data.llCpus.clear();
10155 if (data.fCpuHotPlug)
10156 {
10157 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10158 {
10159 if (mHWData->mCPUAttached[idx])
10160 {
10161 settings::Cpu cpu;
10162 cpu.ulId = idx;
10163 data.llCpus.push_back(cpu);
10164 }
10165 }
10166 }
10167
10168 /* Standard and Extended CPUID leafs. */
10169 data.llCpuIdLeafs.clear();
10170 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10171
10172 // memory
10173 data.ulMemorySizeMB = mHWData->mMemorySize;
10174 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10175
10176 // firmware
10177 data.firmwareType = mHWData->mFirmwareType;
10178
10179 // HID
10180 data.pointingHIDType = mHWData->mPointingHIDType;
10181 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10182
10183 // chipset
10184 data.chipsetType = mHWData->mChipsetType;
10185
10186 // paravirt
10187 data.paravirtProvider = mHWData->mParavirtProvider;
10188 data.strParavirtDebug = mHWData->mParavirtDebug;
10189
10190 // emulated USB card reader
10191 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10192
10193 // HPET
10194 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10195
10196 // boot order
10197 data.mapBootOrder.clear();
10198 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10199 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10200
10201 /* VRDEServer settings (optional) */
10202 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10203 if (FAILED(rc)) throw rc;
10204
10205 /* BIOS settings (required) */
10206 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10207 if (FAILED(rc)) throw rc;
10208
10209 /* Recording settings (required) */
10210 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10211 if (FAILED(rc)) throw rc;
10212
10213 /* GraphicsAdapter settings (required) */
10214 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10215 if (FAILED(rc)) throw rc;
10216
10217 /* USB Controller (required) */
10218 data.usbSettings.llUSBControllers.clear();
10219 for (USBControllerList::const_iterator
10220 it = mUSBControllers->begin();
10221 it != mUSBControllers->end();
10222 ++it)
10223 {
10224 ComObjPtr<USBController> ctrl = *it;
10225 settings::USBController settingsCtrl;
10226
10227 settingsCtrl.strName = ctrl->i_getName();
10228 settingsCtrl.enmType = ctrl->i_getControllerType();
10229
10230 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10231 }
10232
10233 /* USB device filters (required) */
10234 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10235 if (FAILED(rc)) throw rc;
10236
10237 /* Network adapters (required) */
10238 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10239 data.llNetworkAdapters.clear();
10240 /* Write out only the nominal number of network adapters for this
10241 * chipset type. Since Machine::commit() hasn't been called there
10242 * may be extra NIC settings in the vector. */
10243 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10244 {
10245 settings::NetworkAdapter nic;
10246 nic.ulSlot = (uint32_t)slot;
10247 /* paranoia check... must not be NULL, but must not crash either. */
10248 if (mNetworkAdapters[slot])
10249 {
10250 if (mNetworkAdapters[slot]->i_hasDefaults())
10251 continue;
10252
10253 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10254 if (FAILED(rc)) throw rc;
10255
10256 data.llNetworkAdapters.push_back(nic);
10257 }
10258 }
10259
10260 /* Serial ports */
10261 data.llSerialPorts.clear();
10262 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10263 {
10264 if (mSerialPorts[slot]->i_hasDefaults())
10265 continue;
10266
10267 settings::SerialPort s;
10268 s.ulSlot = slot;
10269 rc = mSerialPorts[slot]->i_saveSettings(s);
10270 if (FAILED(rc)) return rc;
10271
10272 data.llSerialPorts.push_back(s);
10273 }
10274
10275 /* Parallel ports */
10276 data.llParallelPorts.clear();
10277 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10278 {
10279 if (mParallelPorts[slot]->i_hasDefaults())
10280 continue;
10281
10282 settings::ParallelPort p;
10283 p.ulSlot = slot;
10284 rc = mParallelPorts[slot]->i_saveSettings(p);
10285 if (FAILED(rc)) return rc;
10286
10287 data.llParallelPorts.push_back(p);
10288 }
10289
10290 /* Audio adapter */
10291 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10292 if (FAILED(rc)) return rc;
10293
10294 rc = i_saveStorageControllers(data.storage);
10295 if (FAILED(rc)) return rc;
10296
10297 /* Shared folders */
10298 data.llSharedFolders.clear();
10299 for (HWData::SharedFolderList::const_iterator
10300 it = mHWData->mSharedFolders.begin();
10301 it != mHWData->mSharedFolders.end();
10302 ++it)
10303 {
10304 SharedFolder *pSF = *it;
10305 AutoCaller sfCaller(pSF);
10306 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10307 settings::SharedFolder sf;
10308 sf.strName = pSF->i_getName();
10309 sf.strHostPath = pSF->i_getHostPath();
10310 sf.fWritable = !!pSF->i_isWritable();
10311 sf.fAutoMount = !!pSF->i_isAutoMounted();
10312 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10313
10314 data.llSharedFolders.push_back(sf);
10315 }
10316
10317 // clipboard
10318 data.clipboardMode = mHWData->mClipboardMode;
10319 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10320
10321 // drag'n'drop
10322 data.dndMode = mHWData->mDnDMode;
10323
10324 /* Guest */
10325 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10326
10327 // IO settings
10328 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10329 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10330
10331 /* BandwidthControl (required) */
10332 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10333 if (FAILED(rc)) throw rc;
10334
10335 /* Host PCI devices */
10336 data.pciAttachments.clear();
10337 for (HWData::PCIDeviceAssignmentList::const_iterator
10338 it = mHWData->mPCIDeviceAssignments.begin();
10339 it != mHWData->mPCIDeviceAssignments.end();
10340 ++it)
10341 {
10342 ComObjPtr<PCIDeviceAttachment> pda = *it;
10343 settings::HostPCIDeviceAttachment hpda;
10344
10345 rc = pda->i_saveSettings(hpda);
10346 if (FAILED(rc)) throw rc;
10347
10348 data.pciAttachments.push_back(hpda);
10349 }
10350
10351 // guest properties
10352 data.llGuestProperties.clear();
10353#ifdef VBOX_WITH_GUEST_PROPS
10354 for (HWData::GuestPropertyMap::const_iterator
10355 it = mHWData->mGuestProperties.begin();
10356 it != mHWData->mGuestProperties.end();
10357 ++it)
10358 {
10359 HWData::GuestProperty property = it->second;
10360
10361 /* Remove transient guest properties at shutdown unless we
10362 * are saving state. Note that restoring snapshot intentionally
10363 * keeps them, they will be removed if appropriate once the final
10364 * machine state is set (as crashes etc. need to work). */
10365 if ( ( mData->mMachineState == MachineState_PoweredOff
10366 || mData->mMachineState == MachineState_Aborted
10367 || mData->mMachineState == MachineState_Teleported)
10368 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10369 continue;
10370 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10371 prop.strName = it->first;
10372 prop.strValue = property.strValue;
10373 prop.timestamp = property.mTimestamp;
10374 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10375 GuestPropWriteFlags(property.mFlags, szFlags);
10376 prop.strFlags = szFlags;
10377
10378 data.llGuestProperties.push_back(prop);
10379 }
10380
10381 /* I presume this doesn't require a backup(). */
10382 mData->mGuestPropertiesModified = FALSE;
10383#endif /* VBOX_WITH_GUEST_PROPS defined */
10384
10385 *pDbg = mHWData->mDebugging;
10386 *pAutostart = mHWData->mAutostart;
10387
10388 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10389 }
10390 catch (std::bad_alloc &)
10391 {
10392 return E_OUTOFMEMORY;
10393 }
10394
10395 AssertComRC(rc);
10396 return rc;
10397}
10398
10399/**
10400 * Saves the storage controller configuration.
10401 *
10402 * @param data storage settings.
10403 */
10404HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10405{
10406 data.llStorageControllers.clear();
10407
10408 for (StorageControllerList::const_iterator
10409 it = mStorageControllers->begin();
10410 it != mStorageControllers->end();
10411 ++it)
10412 {
10413 HRESULT rc;
10414 ComObjPtr<StorageController> pCtl = *it;
10415
10416 settings::StorageController ctl;
10417 ctl.strName = pCtl->i_getName();
10418 ctl.controllerType = pCtl->i_getControllerType();
10419 ctl.storageBus = pCtl->i_getStorageBus();
10420 ctl.ulInstance = pCtl->i_getInstance();
10421 ctl.fBootable = pCtl->i_getBootable();
10422
10423 /* Save the port count. */
10424 ULONG portCount;
10425 rc = pCtl->COMGETTER(PortCount)(&portCount);
10426 ComAssertComRCRet(rc, rc);
10427 ctl.ulPortCount = portCount;
10428
10429 /* Save fUseHostIOCache */
10430 BOOL fUseHostIOCache;
10431 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10432 ComAssertComRCRet(rc, rc);
10433 ctl.fUseHostIOCache = !!fUseHostIOCache;
10434
10435 /* save the devices now. */
10436 rc = i_saveStorageDevices(pCtl, ctl);
10437 ComAssertComRCRet(rc, rc);
10438
10439 data.llStorageControllers.push_back(ctl);
10440 }
10441
10442 return S_OK;
10443}
10444
10445/**
10446 * Saves the hard disk configuration.
10447 */
10448HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10449 settings::StorageController &data)
10450{
10451 MediumAttachmentList atts;
10452
10453 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10454 if (FAILED(rc)) return rc;
10455
10456 data.llAttachedDevices.clear();
10457 for (MediumAttachmentList::const_iterator
10458 it = atts.begin();
10459 it != atts.end();
10460 ++it)
10461 {
10462 settings::AttachedDevice dev;
10463 IMediumAttachment *iA = *it;
10464 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10465 Medium *pMedium = pAttach->i_getMedium();
10466
10467 dev.deviceType = pAttach->i_getType();
10468 dev.lPort = pAttach->i_getPort();
10469 dev.lDevice = pAttach->i_getDevice();
10470 dev.fPassThrough = pAttach->i_getPassthrough();
10471 dev.fHotPluggable = pAttach->i_getHotPluggable();
10472 if (pMedium)
10473 {
10474 if (pMedium->i_isHostDrive())
10475 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10476 else
10477 dev.uuid = pMedium->i_getId();
10478 dev.fTempEject = pAttach->i_getTempEject();
10479 dev.fNonRotational = pAttach->i_getNonRotational();
10480 dev.fDiscard = pAttach->i_getDiscard();
10481 }
10482
10483 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10484
10485 data.llAttachedDevices.push_back(dev);
10486 }
10487
10488 return S_OK;
10489}
10490
10491/**
10492 * Saves machine state settings as defined by aFlags
10493 * (SaveSTS_* values).
10494 *
10495 * @param aFlags Combination of SaveSTS_* flags.
10496 *
10497 * @note Locks objects for writing.
10498 */
10499HRESULT Machine::i_saveStateSettings(int aFlags)
10500{
10501 if (aFlags == 0)
10502 return S_OK;
10503
10504 AutoCaller autoCaller(this);
10505 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10506
10507 /* This object's write lock is also necessary to serialize file access
10508 * (prevent concurrent reads and writes) */
10509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10510
10511 HRESULT rc = S_OK;
10512
10513 Assert(mData->pMachineConfigFile);
10514
10515 try
10516 {
10517 if (aFlags & SaveSTS_CurStateModified)
10518 mData->pMachineConfigFile->fCurrentStateModified = true;
10519
10520 if (aFlags & SaveSTS_StateFilePath)
10521 {
10522 if (!mSSData->strStateFilePath.isEmpty())
10523 /* try to make the file name relative to the settings file dir */
10524 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10525 else
10526 mData->pMachineConfigFile->strStateFile.setNull();
10527 }
10528
10529 if (aFlags & SaveSTS_StateTimeStamp)
10530 {
10531 Assert( mData->mMachineState != MachineState_Aborted
10532 || mSSData->strStateFilePath.isEmpty());
10533
10534 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10535
10536 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10537/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10538 }
10539
10540 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10541 }
10542 catch (...)
10543 {
10544 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10545 }
10546
10547 return rc;
10548}
10549
10550/**
10551 * Ensures that the given medium is added to a media registry. If this machine
10552 * was created with 4.0 or later, then the machine registry is used. Otherwise
10553 * the global VirtualBox media registry is used.
10554 *
10555 * Caller must NOT hold machine lock, media tree or any medium locks!
10556 *
10557 * @param pMedium
10558 */
10559void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10560{
10561 /* Paranoia checks: do not hold machine or media tree locks. */
10562 AssertReturnVoid(!isWriteLockOnCurrentThread());
10563 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10564
10565 ComObjPtr<Medium> pBase;
10566 {
10567 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10568 pBase = pMedium->i_getBase();
10569 }
10570
10571 /* Paranoia checks: do not hold medium locks. */
10572 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10573 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10574
10575 // decide which medium registry to use now that the medium is attached:
10576 Guid uuid;
10577 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10578 if (fCanHaveOwnMediaRegistry)
10579 // machine XML is VirtualBox 4.0 or higher:
10580 uuid = i_getId(); // machine UUID
10581 else
10582 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10583
10584 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10585 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10586 if (pMedium->i_addRegistry(uuid))
10587 mParent->i_markRegistryModified(uuid);
10588
10589 /* For more complex hard disk structures it can happen that the base
10590 * medium isn't yet associated with any medium registry. Do that now. */
10591 if (pMedium != pBase)
10592 {
10593 /* Tree lock needed by Medium::addRegistry when recursing. */
10594 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10595 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10596 {
10597 treeLock.release();
10598 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10599 treeLock.acquire();
10600 }
10601 if (pBase->i_addRegistryRecursive(uuid))
10602 {
10603 treeLock.release();
10604 mParent->i_markRegistryModified(uuid);
10605 }
10606 }
10607}
10608
10609/**
10610 * Creates differencing hard disks for all normal hard disks attached to this
10611 * machine and a new set of attachments to refer to created disks.
10612 *
10613 * Used when taking a snapshot or when deleting the current state. Gets called
10614 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10615 *
10616 * This method assumes that mMediumAttachments contains the original hard disk
10617 * attachments it needs to create diffs for. On success, these attachments will
10618 * be replaced with the created diffs.
10619 *
10620 * Attachments with non-normal hard disks are left as is.
10621 *
10622 * If @a aOnline is @c false then the original hard disks that require implicit
10623 * diffs will be locked for reading. Otherwise it is assumed that they are
10624 * already locked for writing (when the VM was started). Note that in the latter
10625 * case it is responsibility of the caller to lock the newly created diffs for
10626 * writing if this method succeeds.
10627 *
10628 * @param aProgress Progress object to run (must contain at least as
10629 * many operations left as the number of hard disks
10630 * attached).
10631 * @param aWeight Weight of this operation.
10632 * @param aOnline Whether the VM was online prior to this operation.
10633 *
10634 * @note The progress object is not marked as completed, neither on success nor
10635 * on failure. This is a responsibility of the caller.
10636 *
10637 * @note Locks this object and the media tree for writing.
10638 */
10639HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10640 ULONG aWeight,
10641 bool aOnline)
10642{
10643 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10644
10645 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10646 AssertReturn(!!pProgressControl, E_INVALIDARG);
10647
10648 AutoCaller autoCaller(this);
10649 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10650
10651 AutoMultiWriteLock2 alock(this->lockHandle(),
10652 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10653
10654 /* must be in a protective state because we release the lock below */
10655 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10656 || mData->mMachineState == MachineState_OnlineSnapshotting
10657 || mData->mMachineState == MachineState_LiveSnapshotting
10658 || mData->mMachineState == MachineState_RestoringSnapshot
10659 || mData->mMachineState == MachineState_DeletingSnapshot
10660 , E_FAIL);
10661
10662 HRESULT rc = S_OK;
10663
10664 // use appropriate locked media map (online or offline)
10665 MediumLockListMap lockedMediaOffline;
10666 MediumLockListMap *lockedMediaMap;
10667 if (aOnline)
10668 lockedMediaMap = &mData->mSession.mLockedMedia;
10669 else
10670 lockedMediaMap = &lockedMediaOffline;
10671
10672 try
10673 {
10674 if (!aOnline)
10675 {
10676 /* lock all attached hard disks early to detect "in use"
10677 * situations before creating actual diffs */
10678 for (MediumAttachmentList::const_iterator
10679 it = mMediumAttachments->begin();
10680 it != mMediumAttachments->end();
10681 ++it)
10682 {
10683 MediumAttachment *pAtt = *it;
10684 if (pAtt->i_getType() == DeviceType_HardDisk)
10685 {
10686 Medium *pMedium = pAtt->i_getMedium();
10687 Assert(pMedium);
10688
10689 MediumLockList *pMediumLockList(new MediumLockList());
10690 alock.release();
10691 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10692 NULL /* pToLockWrite */,
10693 false /* fMediumLockWriteAll */,
10694 NULL,
10695 *pMediumLockList);
10696 alock.acquire();
10697 if (FAILED(rc))
10698 {
10699 delete pMediumLockList;
10700 throw rc;
10701 }
10702 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10703 if (FAILED(rc))
10704 {
10705 throw setError(rc,
10706 tr("Collecting locking information for all attached media failed"));
10707 }
10708 }
10709 }
10710
10711 /* Now lock all media. If this fails, nothing is locked. */
10712 alock.release();
10713 rc = lockedMediaMap->Lock();
10714 alock.acquire();
10715 if (FAILED(rc))
10716 {
10717 throw setError(rc,
10718 tr("Locking of attached media failed"));
10719 }
10720 }
10721
10722 /* remember the current list (note that we don't use backup() since
10723 * mMediumAttachments may be already backed up) */
10724 MediumAttachmentList atts = *mMediumAttachments.data();
10725
10726 /* start from scratch */
10727 mMediumAttachments->clear();
10728
10729 /* go through remembered attachments and create diffs for normal hard
10730 * disks and attach them */
10731 for (MediumAttachmentList::const_iterator
10732 it = atts.begin();
10733 it != atts.end();
10734 ++it)
10735 {
10736 MediumAttachment *pAtt = *it;
10737
10738 DeviceType_T devType = pAtt->i_getType();
10739 Medium *pMedium = pAtt->i_getMedium();
10740
10741 if ( devType != DeviceType_HardDisk
10742 || pMedium == NULL
10743 || pMedium->i_getType() != MediumType_Normal)
10744 {
10745 /* copy the attachment as is */
10746
10747 /** @todo the progress object created in SessionMachine::TakeSnaphot
10748 * only expects operations for hard disks. Later other
10749 * device types need to show up in the progress as well. */
10750 if (devType == DeviceType_HardDisk)
10751 {
10752 if (pMedium == NULL)
10753 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10754 aWeight); // weight
10755 else
10756 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10757 pMedium->i_getBase()->i_getName().c_str()).raw(),
10758 aWeight); // weight
10759 }
10760
10761 mMediumAttachments->push_back(pAtt);
10762 continue;
10763 }
10764
10765 /* need a diff */
10766 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10767 pMedium->i_getBase()->i_getName().c_str()).raw(),
10768 aWeight); // weight
10769
10770 Utf8Str strFullSnapshotFolder;
10771 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10772
10773 ComObjPtr<Medium> diff;
10774 diff.createObject();
10775 // store the diff in the same registry as the parent
10776 // (this cannot fail here because we can't create implicit diffs for
10777 // unregistered images)
10778 Guid uuidRegistryParent;
10779 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10780 Assert(fInRegistry); NOREF(fInRegistry);
10781 rc = diff->init(mParent,
10782 pMedium->i_getPreferredDiffFormat(),
10783 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10784 uuidRegistryParent,
10785 DeviceType_HardDisk);
10786 if (FAILED(rc)) throw rc;
10787
10788 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10789 * the push_back? Looks like we're going to release medium with the
10790 * wrong kind of lock (general issue with if we fail anywhere at all)
10791 * and an orphaned VDI in the snapshots folder. */
10792
10793 /* update the appropriate lock list */
10794 MediumLockList *pMediumLockList;
10795 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10796 AssertComRCThrowRC(rc);
10797 if (aOnline)
10798 {
10799 alock.release();
10800 /* The currently attached medium will be read-only, change
10801 * the lock type to read. */
10802 rc = pMediumLockList->Update(pMedium, false);
10803 alock.acquire();
10804 AssertComRCThrowRC(rc);
10805 }
10806
10807 /* release the locks before the potentially lengthy operation */
10808 alock.release();
10809 rc = pMedium->i_createDiffStorage(diff,
10810 pMedium->i_getPreferredDiffVariant(),
10811 pMediumLockList,
10812 NULL /* aProgress */,
10813 true /* aWait */,
10814 false /* aNotify */);
10815 alock.acquire();
10816 if (FAILED(rc)) throw rc;
10817
10818 /* actual lock list update is done in Machine::i_commitMedia */
10819
10820 rc = diff->i_addBackReference(mData->mUuid);
10821 AssertComRCThrowRC(rc);
10822
10823 /* add a new attachment */
10824 ComObjPtr<MediumAttachment> attachment;
10825 attachment.createObject();
10826 rc = attachment->init(this,
10827 diff,
10828 pAtt->i_getControllerName(),
10829 pAtt->i_getPort(),
10830 pAtt->i_getDevice(),
10831 DeviceType_HardDisk,
10832 true /* aImplicit */,
10833 false /* aPassthrough */,
10834 false /* aTempEject */,
10835 pAtt->i_getNonRotational(),
10836 pAtt->i_getDiscard(),
10837 pAtt->i_getHotPluggable(),
10838 pAtt->i_getBandwidthGroup());
10839 if (FAILED(rc)) throw rc;
10840
10841 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10842 AssertComRCThrowRC(rc);
10843 mMediumAttachments->push_back(attachment);
10844 }
10845 }
10846 catch (HRESULT aRC) { rc = aRC; }
10847
10848 /* unlock all hard disks we locked when there is no VM */
10849 if (!aOnline)
10850 {
10851 ErrorInfoKeeper eik;
10852
10853 HRESULT rc1 = lockedMediaMap->Clear();
10854 AssertComRC(rc1);
10855 }
10856
10857 return rc;
10858}
10859
10860/**
10861 * Deletes implicit differencing hard disks created either by
10862 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10863 * mMediumAttachments.
10864 *
10865 * Note that to delete hard disks created by #attachDevice() this method is
10866 * called from #i_rollbackMedia() when the changes are rolled back.
10867 *
10868 * @note Locks this object and the media tree for writing.
10869 */
10870HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10871{
10872 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10873
10874 AutoCaller autoCaller(this);
10875 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10876
10877 AutoMultiWriteLock2 alock(this->lockHandle(),
10878 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10879
10880 /* We absolutely must have backed up state. */
10881 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10882
10883 /* Check if there are any implicitly created diff images. */
10884 bool fImplicitDiffs = false;
10885 for (MediumAttachmentList::const_iterator
10886 it = mMediumAttachments->begin();
10887 it != mMediumAttachments->end();
10888 ++it)
10889 {
10890 const ComObjPtr<MediumAttachment> &pAtt = *it;
10891 if (pAtt->i_isImplicit())
10892 {
10893 fImplicitDiffs = true;
10894 break;
10895 }
10896 }
10897 /* If there is nothing to do, leave early. This saves lots of image locking
10898 * effort. It also avoids a MachineStateChanged event without real reason.
10899 * This is important e.g. when loading a VM config, because there should be
10900 * no events. Otherwise API clients can become thoroughly confused for
10901 * inaccessible VMs (the code for loading VM configs uses this method for
10902 * cleanup if the config makes no sense), as they take such events as an
10903 * indication that the VM is alive, and they would force the VM config to
10904 * be reread, leading to an endless loop. */
10905 if (!fImplicitDiffs)
10906 return S_OK;
10907
10908 HRESULT rc = S_OK;
10909 MachineState_T oldState = mData->mMachineState;
10910
10911 /* will release the lock before the potentially lengthy operation,
10912 * so protect with the special state (unless already protected) */
10913 if ( oldState != MachineState_Snapshotting
10914 && oldState != MachineState_OnlineSnapshotting
10915 && oldState != MachineState_LiveSnapshotting
10916 && oldState != MachineState_RestoringSnapshot
10917 && oldState != MachineState_DeletingSnapshot
10918 && oldState != MachineState_DeletingSnapshotOnline
10919 && oldState != MachineState_DeletingSnapshotPaused
10920 )
10921 i_setMachineState(MachineState_SettingUp);
10922
10923 // use appropriate locked media map (online or offline)
10924 MediumLockListMap lockedMediaOffline;
10925 MediumLockListMap *lockedMediaMap;
10926 if (aOnline)
10927 lockedMediaMap = &mData->mSession.mLockedMedia;
10928 else
10929 lockedMediaMap = &lockedMediaOffline;
10930
10931 try
10932 {
10933 if (!aOnline)
10934 {
10935 /* lock all attached hard disks early to detect "in use"
10936 * situations before deleting actual diffs */
10937 for (MediumAttachmentList::const_iterator
10938 it = mMediumAttachments->begin();
10939 it != mMediumAttachments->end();
10940 ++it)
10941 {
10942 MediumAttachment *pAtt = *it;
10943 if (pAtt->i_getType() == DeviceType_HardDisk)
10944 {
10945 Medium *pMedium = pAtt->i_getMedium();
10946 Assert(pMedium);
10947
10948 MediumLockList *pMediumLockList(new MediumLockList());
10949 alock.release();
10950 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10951 NULL /* pToLockWrite */,
10952 false /* fMediumLockWriteAll */,
10953 NULL,
10954 *pMediumLockList);
10955 alock.acquire();
10956
10957 if (FAILED(rc))
10958 {
10959 delete pMediumLockList;
10960 throw rc;
10961 }
10962
10963 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10964 if (FAILED(rc))
10965 throw rc;
10966 }
10967 }
10968
10969 if (FAILED(rc))
10970 throw rc;
10971 } // end of offline
10972
10973 /* Lock lists are now up to date and include implicitly created media */
10974
10975 /* Go through remembered attachments and delete all implicitly created
10976 * diffs and fix up the attachment information */
10977 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10978 MediumAttachmentList implicitAtts;
10979 for (MediumAttachmentList::const_iterator
10980 it = mMediumAttachments->begin();
10981 it != mMediumAttachments->end();
10982 ++it)
10983 {
10984 ComObjPtr<MediumAttachment> pAtt = *it;
10985 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10986 if (pMedium.isNull())
10987 continue;
10988
10989 // Implicit attachments go on the list for deletion and back references are removed.
10990 if (pAtt->i_isImplicit())
10991 {
10992 /* Deassociate and mark for deletion */
10993 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10994 rc = pMedium->i_removeBackReference(mData->mUuid);
10995 if (FAILED(rc))
10996 throw rc;
10997 implicitAtts.push_back(pAtt);
10998 continue;
10999 }
11000
11001 /* Was this medium attached before? */
11002 if (!i_findAttachment(oldAtts, pMedium))
11003 {
11004 /* no: de-associate */
11005 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11006 rc = pMedium->i_removeBackReference(mData->mUuid);
11007 if (FAILED(rc))
11008 throw rc;
11009 continue;
11010 }
11011 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11012 }
11013
11014 /* If there are implicit attachments to delete, throw away the lock
11015 * map contents (which will unlock all media) since the medium
11016 * attachments will be rolled back. Below we need to completely
11017 * recreate the lock map anyway since it is infinitely complex to
11018 * do this incrementally (would need reconstructing each attachment
11019 * change, which would be extremely hairy). */
11020 if (implicitAtts.size() != 0)
11021 {
11022 ErrorInfoKeeper eik;
11023
11024 HRESULT rc1 = lockedMediaMap->Clear();
11025 AssertComRC(rc1);
11026 }
11027
11028 /* rollback hard disk changes */
11029 mMediumAttachments.rollback();
11030
11031 MultiResult mrc(S_OK);
11032
11033 // Delete unused implicit diffs.
11034 if (implicitAtts.size() != 0)
11035 {
11036 alock.release();
11037
11038 for (MediumAttachmentList::const_iterator
11039 it = implicitAtts.begin();
11040 it != implicitAtts.end();
11041 ++it)
11042 {
11043 // Remove medium associated with this attachment.
11044 ComObjPtr<MediumAttachment> pAtt = *it;
11045 Assert(pAtt);
11046 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11047 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11048 Assert(pMedium);
11049
11050 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11051 // continue on delete failure, just collect error messages
11052 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11053 pMedium->i_getLocationFull().c_str() ));
11054 mrc = rc;
11055 }
11056 // Clear the list of deleted implicit attachments now, while not
11057 // holding the lock, as it will ultimately trigger Medium::uninit()
11058 // calls which assume that the media tree lock isn't held.
11059 implicitAtts.clear();
11060
11061 alock.acquire();
11062
11063 /* if there is a VM recreate media lock map as mentioned above,
11064 * otherwise it is a waste of time and we leave things unlocked */
11065 if (aOnline)
11066 {
11067 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11068 /* must never be NULL, but better safe than sorry */
11069 if (!pMachine.isNull())
11070 {
11071 alock.release();
11072 rc = mData->mSession.mMachine->i_lockMedia();
11073 alock.acquire();
11074 if (FAILED(rc))
11075 throw rc;
11076 }
11077 }
11078 }
11079 }
11080 catch (HRESULT aRC) {rc = aRC;}
11081
11082 if (mData->mMachineState == MachineState_SettingUp)
11083 i_setMachineState(oldState);
11084
11085 /* unlock all hard disks we locked when there is no VM */
11086 if (!aOnline)
11087 {
11088 ErrorInfoKeeper eik;
11089
11090 HRESULT rc1 = lockedMediaMap->Clear();
11091 AssertComRC(rc1);
11092 }
11093
11094 return rc;
11095}
11096
11097
11098/**
11099 * Looks through the given list of media attachments for one with the given parameters
11100 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11101 * can be searched as well if needed.
11102 *
11103 * @param ll
11104 * @param aControllerName
11105 * @param aControllerPort
11106 * @param aDevice
11107 * @return
11108 */
11109MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11110 const Utf8Str &aControllerName,
11111 LONG aControllerPort,
11112 LONG aDevice)
11113{
11114 for (MediumAttachmentList::const_iterator
11115 it = ll.begin();
11116 it != ll.end();
11117 ++it)
11118 {
11119 MediumAttachment *pAttach = *it;
11120 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11121 return pAttach;
11122 }
11123
11124 return NULL;
11125}
11126
11127/**
11128 * Looks through the given list of media attachments for one with the given parameters
11129 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11130 * can be searched as well if needed.
11131 *
11132 * @param ll
11133 * @param pMedium
11134 * @return
11135 */
11136MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11137 ComObjPtr<Medium> pMedium)
11138{
11139 for (MediumAttachmentList::const_iterator
11140 it = ll.begin();
11141 it != ll.end();
11142 ++it)
11143 {
11144 MediumAttachment *pAttach = *it;
11145 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11146 if (pMediumThis == pMedium)
11147 return pAttach;
11148 }
11149
11150 return NULL;
11151}
11152
11153/**
11154 * Looks through the given list of media attachments for one with the given parameters
11155 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11156 * can be searched as well if needed.
11157 *
11158 * @param ll
11159 * @param id
11160 * @return
11161 */
11162MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11163 Guid &id)
11164{
11165 for (MediumAttachmentList::const_iterator
11166 it = ll.begin();
11167 it != ll.end();
11168 ++it)
11169 {
11170 MediumAttachment *pAttach = *it;
11171 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11172 if (pMediumThis->i_getId() == id)
11173 return pAttach;
11174 }
11175
11176 return NULL;
11177}
11178
11179/**
11180 * Main implementation for Machine::DetachDevice. This also gets called
11181 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11182 *
11183 * @param pAttach Medium attachment to detach.
11184 * @param writeLock Machine write lock which the caller must have locked once.
11185 * This may be released temporarily in here.
11186 * @param pSnapshot If NULL, then the detachment is for the current machine.
11187 * Otherwise this is for a SnapshotMachine, and this must be
11188 * its snapshot.
11189 * @return
11190 */
11191HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11192 AutoWriteLock &writeLock,
11193 Snapshot *pSnapshot)
11194{
11195 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11196 DeviceType_T mediumType = pAttach->i_getType();
11197
11198 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11199
11200 if (pAttach->i_isImplicit())
11201 {
11202 /* attempt to implicitly delete the implicitly created diff */
11203
11204 /// @todo move the implicit flag from MediumAttachment to Medium
11205 /// and forbid any hard disk operation when it is implicit. Or maybe
11206 /// a special media state for it to make it even more simple.
11207
11208 Assert(mMediumAttachments.isBackedUp());
11209
11210 /* will release the lock before the potentially lengthy operation, so
11211 * protect with the special state */
11212 MachineState_T oldState = mData->mMachineState;
11213 i_setMachineState(MachineState_SettingUp);
11214
11215 writeLock.release();
11216
11217 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11218 true /*aWait*/,
11219 false /*aNotify*/);
11220
11221 writeLock.acquire();
11222
11223 i_setMachineState(oldState);
11224
11225 if (FAILED(rc)) return rc;
11226 }
11227
11228 i_setModified(IsModified_Storage);
11229 mMediumAttachments.backup();
11230 mMediumAttachments->remove(pAttach);
11231
11232 if (!oldmedium.isNull())
11233 {
11234 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11235 if (pSnapshot)
11236 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11237 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11238 else if (mediumType != DeviceType_HardDisk)
11239 oldmedium->i_removeBackReference(mData->mUuid);
11240 }
11241
11242 return S_OK;
11243}
11244
11245/**
11246 * Goes thru all media of the given list and
11247 *
11248 * 1) calls i_detachDevice() on each of them for this machine and
11249 * 2) adds all Medium objects found in the process to the given list,
11250 * depending on cleanupMode.
11251 *
11252 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11253 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11254 * media to the list.
11255 *
11256 * This gets called from Machine::Unregister, both for the actual Machine and
11257 * the SnapshotMachine objects that might be found in the snapshots.
11258 *
11259 * Requires caller and locking. The machine lock must be passed in because it
11260 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11261 *
11262 * @param writeLock Machine lock from top-level caller; this gets passed to
11263 * i_detachDevice.
11264 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11265 * object if called for a SnapshotMachine.
11266 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11267 * added to llMedia; if Full, then all media get added;
11268 * otherwise no media get added.
11269 * @param llMedia Caller's list to receive Medium objects which got detached so
11270 * caller can close() them, depending on cleanupMode.
11271 * @return
11272 */
11273HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11274 Snapshot *pSnapshot,
11275 CleanupMode_T cleanupMode,
11276 MediaList &llMedia)
11277{
11278 Assert(isWriteLockOnCurrentThread());
11279
11280 HRESULT rc;
11281
11282 // make a temporary list because i_detachDevice invalidates iterators into
11283 // mMediumAttachments
11284 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11285
11286 for (MediumAttachmentList::iterator
11287 it = llAttachments2.begin();
11288 it != llAttachments2.end();
11289 ++it)
11290 {
11291 ComObjPtr<MediumAttachment> &pAttach = *it;
11292 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11293
11294 if (!pMedium.isNull())
11295 {
11296 AutoCaller mac(pMedium);
11297 if (FAILED(mac.rc())) return mac.rc();
11298 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11299 DeviceType_T devType = pMedium->i_getDeviceType();
11300 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11301 && devType == DeviceType_HardDisk)
11302 || (cleanupMode == CleanupMode_Full)
11303 )
11304 {
11305 llMedia.push_back(pMedium);
11306 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11307 /* Not allowed to keep this lock as below we need the parent
11308 * medium lock, and the lock order is parent to child. */
11309 lock.release();
11310 /*
11311 * Search for medias which are not attached to any machine, but
11312 * in the chain to an attached disk. Mediums are only consided
11313 * if they are:
11314 * - have only one child
11315 * - no references to any machines
11316 * - are of normal medium type
11317 */
11318 while (!pParent.isNull())
11319 {
11320 AutoCaller mac1(pParent);
11321 if (FAILED(mac1.rc())) return mac1.rc();
11322 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11323 if (pParent->i_getChildren().size() == 1)
11324 {
11325 if ( pParent->i_getMachineBackRefCount() == 0
11326 && pParent->i_getType() == MediumType_Normal
11327 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11328 llMedia.push_back(pParent);
11329 }
11330 else
11331 break;
11332 pParent = pParent->i_getParent();
11333 }
11334 }
11335 }
11336
11337 // real machine: then we need to use the proper method
11338 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11339
11340 if (FAILED(rc))
11341 return rc;
11342 }
11343
11344 return S_OK;
11345}
11346
11347/**
11348 * Perform deferred hard disk detachments.
11349 *
11350 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11351 * changed (not backed up).
11352 *
11353 * If @a aOnline is @c true then this method will also unlock the old hard
11354 * disks for which the new implicit diffs were created and will lock these new
11355 * diffs for writing.
11356 *
11357 * @param aOnline Whether the VM was online prior to this operation.
11358 *
11359 * @note Locks this object for writing!
11360 */
11361void Machine::i_commitMedia(bool aOnline /*= false*/)
11362{
11363 AutoCaller autoCaller(this);
11364 AssertComRCReturnVoid(autoCaller.rc());
11365
11366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11367
11368 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11369
11370 HRESULT rc = S_OK;
11371
11372 /* no attach/detach operations -- nothing to do */
11373 if (!mMediumAttachments.isBackedUp())
11374 return;
11375
11376 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11377 bool fMediaNeedsLocking = false;
11378
11379 /* enumerate new attachments */
11380 for (MediumAttachmentList::const_iterator
11381 it = mMediumAttachments->begin();
11382 it != mMediumAttachments->end();
11383 ++it)
11384 {
11385 MediumAttachment *pAttach = *it;
11386
11387 pAttach->i_commit();
11388
11389 Medium *pMedium = pAttach->i_getMedium();
11390 bool fImplicit = pAttach->i_isImplicit();
11391
11392 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11393 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11394 fImplicit));
11395
11396 /** @todo convert all this Machine-based voodoo to MediumAttachment
11397 * based commit logic. */
11398 if (fImplicit)
11399 {
11400 /* convert implicit attachment to normal */
11401 pAttach->i_setImplicit(false);
11402
11403 if ( aOnline
11404 && pMedium
11405 && pAttach->i_getType() == DeviceType_HardDisk
11406 )
11407 {
11408 /* update the appropriate lock list */
11409 MediumLockList *pMediumLockList;
11410 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11411 AssertComRC(rc);
11412 if (pMediumLockList)
11413 {
11414 /* unlock if there's a need to change the locking */
11415 if (!fMediaNeedsLocking)
11416 {
11417 rc = mData->mSession.mLockedMedia.Unlock();
11418 AssertComRC(rc);
11419 fMediaNeedsLocking = true;
11420 }
11421 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11422 AssertComRC(rc);
11423 rc = pMediumLockList->Append(pMedium, true);
11424 AssertComRC(rc);
11425 }
11426 }
11427
11428 continue;
11429 }
11430
11431 if (pMedium)
11432 {
11433 /* was this medium attached before? */
11434 for (MediumAttachmentList::iterator
11435 oldIt = oldAtts.begin();
11436 oldIt != oldAtts.end();
11437 ++oldIt)
11438 {
11439 MediumAttachment *pOldAttach = *oldIt;
11440 if (pOldAttach->i_getMedium() == pMedium)
11441 {
11442 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11443
11444 /* yes: remove from old to avoid de-association */
11445 oldAtts.erase(oldIt);
11446 break;
11447 }
11448 }
11449 }
11450 }
11451
11452 /* enumerate remaining old attachments and de-associate from the
11453 * current machine state */
11454 for (MediumAttachmentList::const_iterator
11455 it = oldAtts.begin();
11456 it != oldAtts.end();
11457 ++it)
11458 {
11459 MediumAttachment *pAttach = *it;
11460 Medium *pMedium = pAttach->i_getMedium();
11461
11462 /* Detach only hard disks, since DVD/floppy media is detached
11463 * instantly in MountMedium. */
11464 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11465 {
11466 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11467
11468 /* now de-associate from the current machine state */
11469 rc = pMedium->i_removeBackReference(mData->mUuid);
11470 AssertComRC(rc);
11471
11472 if (aOnline)
11473 {
11474 /* unlock since medium is not used anymore */
11475 MediumLockList *pMediumLockList;
11476 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11477 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11478 {
11479 /* this happens for online snapshots, there the attachment
11480 * is changing, but only to a diff image created under
11481 * the old one, so there is no separate lock list */
11482 Assert(!pMediumLockList);
11483 }
11484 else
11485 {
11486 AssertComRC(rc);
11487 if (pMediumLockList)
11488 {
11489 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11490 AssertComRC(rc);
11491 }
11492 }
11493 }
11494 }
11495 }
11496
11497 /* take media locks again so that the locking state is consistent */
11498 if (fMediaNeedsLocking)
11499 {
11500 Assert(aOnline);
11501 rc = mData->mSession.mLockedMedia.Lock();
11502 AssertComRC(rc);
11503 }
11504
11505 /* commit the hard disk changes */
11506 mMediumAttachments.commit();
11507
11508 if (i_isSessionMachine())
11509 {
11510 /*
11511 * Update the parent machine to point to the new owner.
11512 * This is necessary because the stored parent will point to the
11513 * session machine otherwise and cause crashes or errors later
11514 * when the session machine gets invalid.
11515 */
11516 /** @todo Change the MediumAttachment class to behave like any other
11517 * class in this regard by creating peer MediumAttachment
11518 * objects for session machines and share the data with the peer
11519 * machine.
11520 */
11521 for (MediumAttachmentList::const_iterator
11522 it = mMediumAttachments->begin();
11523 it != mMediumAttachments->end();
11524 ++it)
11525 (*it)->i_updateParentMachine(mPeer);
11526
11527 /* attach new data to the primary machine and reshare it */
11528 mPeer->mMediumAttachments.attach(mMediumAttachments);
11529 }
11530
11531 return;
11532}
11533
11534/**
11535 * Perform deferred deletion of implicitly created diffs.
11536 *
11537 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11538 * changed (not backed up).
11539 *
11540 * @note Locks this object for writing!
11541 */
11542void Machine::i_rollbackMedia()
11543{
11544 AutoCaller autoCaller(this);
11545 AssertComRCReturnVoid(autoCaller.rc());
11546
11547 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11548 LogFlowThisFunc(("Entering rollbackMedia\n"));
11549
11550 HRESULT rc = S_OK;
11551
11552 /* no attach/detach operations -- nothing to do */
11553 if (!mMediumAttachments.isBackedUp())
11554 return;
11555
11556 /* enumerate new attachments */
11557 for (MediumAttachmentList::const_iterator
11558 it = mMediumAttachments->begin();
11559 it != mMediumAttachments->end();
11560 ++it)
11561 {
11562 MediumAttachment *pAttach = *it;
11563 /* Fix up the backrefs for DVD/floppy media. */
11564 if (pAttach->i_getType() != DeviceType_HardDisk)
11565 {
11566 Medium *pMedium = pAttach->i_getMedium();
11567 if (pMedium)
11568 {
11569 rc = pMedium->i_removeBackReference(mData->mUuid);
11570 AssertComRC(rc);
11571 }
11572 }
11573
11574 (*it)->i_rollback();
11575
11576 pAttach = *it;
11577 /* Fix up the backrefs for DVD/floppy media. */
11578 if (pAttach->i_getType() != DeviceType_HardDisk)
11579 {
11580 Medium *pMedium = pAttach->i_getMedium();
11581 if (pMedium)
11582 {
11583 rc = pMedium->i_addBackReference(mData->mUuid);
11584 AssertComRC(rc);
11585 }
11586 }
11587 }
11588
11589 /** @todo convert all this Machine-based voodoo to MediumAttachment
11590 * based rollback logic. */
11591 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11592
11593 return;
11594}
11595
11596/**
11597 * Returns true if the settings file is located in the directory named exactly
11598 * as the machine; this means, among other things, that the machine directory
11599 * should be auto-renamed.
11600 *
11601 * @param aSettingsDir if not NULL, the full machine settings file directory
11602 * name will be assigned there.
11603 *
11604 * @note Doesn't lock anything.
11605 * @note Not thread safe (must be called from this object's lock).
11606 */
11607bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11608{
11609 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11610 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11611 if (aSettingsDir)
11612 *aSettingsDir = strMachineDirName;
11613 strMachineDirName.stripPath(); // vmname
11614 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11615 strConfigFileOnly.stripPath() // vmname.vbox
11616 .stripSuffix(); // vmname
11617 /** @todo hack, make somehow use of ComposeMachineFilename */
11618 if (mUserData->s.fDirectoryIncludesUUID)
11619 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11620
11621 AssertReturn(!strMachineDirName.isEmpty(), false);
11622 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11623
11624 return strMachineDirName == strConfigFileOnly;
11625}
11626
11627/**
11628 * Discards all changes to machine settings.
11629 *
11630 * @param aNotify Whether to notify the direct session about changes or not.
11631 *
11632 * @note Locks objects for writing!
11633 */
11634void Machine::i_rollback(bool aNotify)
11635{
11636 AutoCaller autoCaller(this);
11637 AssertComRCReturn(autoCaller.rc(), (void)0);
11638
11639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11640
11641 if (!mStorageControllers.isNull())
11642 {
11643 if (mStorageControllers.isBackedUp())
11644 {
11645 /* unitialize all new devices (absent in the backed up list). */
11646 StorageControllerList *backedList = mStorageControllers.backedUpData();
11647 for (StorageControllerList::const_iterator
11648 it = mStorageControllers->begin();
11649 it != mStorageControllers->end();
11650 ++it)
11651 {
11652 if ( std::find(backedList->begin(), backedList->end(), *it)
11653 == backedList->end()
11654 )
11655 {
11656 (*it)->uninit();
11657 }
11658 }
11659
11660 /* restore the list */
11661 mStorageControllers.rollback();
11662 }
11663
11664 /* rollback any changes to devices after restoring the list */
11665 if (mData->flModifications & IsModified_Storage)
11666 {
11667 for (StorageControllerList::const_iterator
11668 it = mStorageControllers->begin();
11669 it != mStorageControllers->end();
11670 ++it)
11671 {
11672 (*it)->i_rollback();
11673 }
11674 }
11675 }
11676
11677 if (!mUSBControllers.isNull())
11678 {
11679 if (mUSBControllers.isBackedUp())
11680 {
11681 /* unitialize all new devices (absent in the backed up list). */
11682 USBControllerList *backedList = mUSBControllers.backedUpData();
11683 for (USBControllerList::const_iterator
11684 it = mUSBControllers->begin();
11685 it != mUSBControllers->end();
11686 ++it)
11687 {
11688 if ( std::find(backedList->begin(), backedList->end(), *it)
11689 == backedList->end()
11690 )
11691 {
11692 (*it)->uninit();
11693 }
11694 }
11695
11696 /* restore the list */
11697 mUSBControllers.rollback();
11698 }
11699
11700 /* rollback any changes to devices after restoring the list */
11701 if (mData->flModifications & IsModified_USB)
11702 {
11703 for (USBControllerList::const_iterator
11704 it = mUSBControllers->begin();
11705 it != mUSBControllers->end();
11706 ++it)
11707 {
11708 (*it)->i_rollback();
11709 }
11710 }
11711 }
11712
11713 mUserData.rollback();
11714
11715 mHWData.rollback();
11716
11717 if (mData->flModifications & IsModified_Storage)
11718 i_rollbackMedia();
11719
11720 if (mBIOSSettings)
11721 mBIOSSettings->i_rollback();
11722
11723 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11724 mRecordingSettings->i_rollback();
11725
11726 if (mGraphicsAdapter)
11727 mGraphicsAdapter->i_rollback();
11728
11729 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11730 mVRDEServer->i_rollback();
11731
11732 if (mAudioAdapter)
11733 mAudioAdapter->i_rollback();
11734
11735 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11736 mUSBDeviceFilters->i_rollback();
11737
11738 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11739 mBandwidthControl->i_rollback();
11740
11741 if (!mHWData.isNull())
11742 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11743 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11744 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11745 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11746
11747 if (mData->flModifications & IsModified_NetworkAdapters)
11748 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11749 if ( mNetworkAdapters[slot]
11750 && mNetworkAdapters[slot]->i_isModified())
11751 {
11752 mNetworkAdapters[slot]->i_rollback();
11753 networkAdapters[slot] = mNetworkAdapters[slot];
11754 }
11755
11756 if (mData->flModifications & IsModified_SerialPorts)
11757 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11758 if ( mSerialPorts[slot]
11759 && mSerialPorts[slot]->i_isModified())
11760 {
11761 mSerialPorts[slot]->i_rollback();
11762 serialPorts[slot] = mSerialPorts[slot];
11763 }
11764
11765 if (mData->flModifications & IsModified_ParallelPorts)
11766 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11767 if ( mParallelPorts[slot]
11768 && mParallelPorts[slot]->i_isModified())
11769 {
11770 mParallelPorts[slot]->i_rollback();
11771 parallelPorts[slot] = mParallelPorts[slot];
11772 }
11773
11774 if (aNotify)
11775 {
11776 /* inform the direct session about changes */
11777
11778 ComObjPtr<Machine> that = this;
11779 uint32_t flModifications = mData->flModifications;
11780 alock.release();
11781
11782 if (flModifications & IsModified_SharedFolders)
11783 that->i_onSharedFolderChange();
11784
11785 if (flModifications & IsModified_VRDEServer)
11786 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11787 if (flModifications & IsModified_USB)
11788 that->i_onUSBControllerChange();
11789
11790 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11791 if (networkAdapters[slot])
11792 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11793 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11794 if (serialPorts[slot])
11795 that->i_onSerialPortChange(serialPorts[slot]);
11796 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11797 if (parallelPorts[slot])
11798 that->i_onParallelPortChange(parallelPorts[slot]);
11799
11800 if (flModifications & IsModified_Storage)
11801 {
11802 for (StorageControllerList::const_iterator
11803 it = mStorageControllers->begin();
11804 it != mStorageControllers->end();
11805 ++it)
11806 {
11807 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11808 }
11809 }
11810
11811
11812#if 0
11813 if (flModifications & IsModified_BandwidthControl)
11814 that->onBandwidthControlChange();
11815#endif
11816 }
11817}
11818
11819/**
11820 * Commits all the changes to machine settings.
11821 *
11822 * Note that this operation is supposed to never fail.
11823 *
11824 * @note Locks this object and children for writing.
11825 */
11826void Machine::i_commit()
11827{
11828 AutoCaller autoCaller(this);
11829 AssertComRCReturnVoid(autoCaller.rc());
11830
11831 AutoCaller peerCaller(mPeer);
11832 AssertComRCReturnVoid(peerCaller.rc());
11833
11834 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11835
11836 /*
11837 * use safe commit to ensure Snapshot machines (that share mUserData)
11838 * will still refer to a valid memory location
11839 */
11840 mUserData.commitCopy();
11841
11842 mHWData.commit();
11843
11844 if (mMediumAttachments.isBackedUp())
11845 i_commitMedia(Global::IsOnline(mData->mMachineState));
11846
11847 mBIOSSettings->i_commit();
11848 mRecordingSettings->i_commit();
11849 mGraphicsAdapter->i_commit();
11850 mVRDEServer->i_commit();
11851 mAudioAdapter->i_commit();
11852 mUSBDeviceFilters->i_commit();
11853 mBandwidthControl->i_commit();
11854
11855 /* Since mNetworkAdapters is a list which might have been changed (resized)
11856 * without using the Backupable<> template we need to handle the copying
11857 * of the list entries manually, including the creation of peers for the
11858 * new objects. */
11859 bool commitNetworkAdapters = false;
11860 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11861 if (mPeer)
11862 {
11863 /* commit everything, even the ones which will go away */
11864 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11865 mNetworkAdapters[slot]->i_commit();
11866 /* copy over the new entries, creating a peer and uninit the original */
11867 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11868 for (size_t slot = 0; slot < newSize; slot++)
11869 {
11870 /* look if this adapter has a peer device */
11871 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11872 if (!peer)
11873 {
11874 /* no peer means the adapter is a newly created one;
11875 * create a peer owning data this data share it with */
11876 peer.createObject();
11877 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11878 }
11879 mPeer->mNetworkAdapters[slot] = peer;
11880 }
11881 /* uninit any no longer needed network adapters */
11882 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11883 mNetworkAdapters[slot]->uninit();
11884 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11885 {
11886 if (mPeer->mNetworkAdapters[slot])
11887 mPeer->mNetworkAdapters[slot]->uninit();
11888 }
11889 /* Keep the original network adapter count until this point, so that
11890 * discarding a chipset type change will not lose settings. */
11891 mNetworkAdapters.resize(newSize);
11892 mPeer->mNetworkAdapters.resize(newSize);
11893 }
11894 else
11895 {
11896 /* we have no peer (our parent is the newly created machine);
11897 * just commit changes to the network adapters */
11898 commitNetworkAdapters = true;
11899 }
11900 if (commitNetworkAdapters)
11901 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11902 mNetworkAdapters[slot]->i_commit();
11903
11904 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11905 mSerialPorts[slot]->i_commit();
11906 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11907 mParallelPorts[slot]->i_commit();
11908
11909 bool commitStorageControllers = false;
11910
11911 if (mStorageControllers.isBackedUp())
11912 {
11913 mStorageControllers.commit();
11914
11915 if (mPeer)
11916 {
11917 /* Commit all changes to new controllers (this will reshare data with
11918 * peers for those who have peers) */
11919 StorageControllerList *newList = new StorageControllerList();
11920 for (StorageControllerList::const_iterator
11921 it = mStorageControllers->begin();
11922 it != mStorageControllers->end();
11923 ++it)
11924 {
11925 (*it)->i_commit();
11926
11927 /* look if this controller has a peer device */
11928 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11929 if (!peer)
11930 {
11931 /* no peer means the device is a newly created one;
11932 * create a peer owning data this device share it with */
11933 peer.createObject();
11934 peer->init(mPeer, *it, true /* aReshare */);
11935 }
11936 else
11937 {
11938 /* remove peer from the old list */
11939 mPeer->mStorageControllers->remove(peer);
11940 }
11941 /* and add it to the new list */
11942 newList->push_back(peer);
11943 }
11944
11945 /* uninit old peer's controllers that are left */
11946 for (StorageControllerList::const_iterator
11947 it = mPeer->mStorageControllers->begin();
11948 it != mPeer->mStorageControllers->end();
11949 ++it)
11950 {
11951 (*it)->uninit();
11952 }
11953
11954 /* attach new list of controllers to our peer */
11955 mPeer->mStorageControllers.attach(newList);
11956 }
11957 else
11958 {
11959 /* we have no peer (our parent is the newly created machine);
11960 * just commit changes to devices */
11961 commitStorageControllers = true;
11962 }
11963 }
11964 else
11965 {
11966 /* the list of controllers itself is not changed,
11967 * just commit changes to controllers themselves */
11968 commitStorageControllers = true;
11969 }
11970
11971 if (commitStorageControllers)
11972 {
11973 for (StorageControllerList::const_iterator
11974 it = mStorageControllers->begin();
11975 it != mStorageControllers->end();
11976 ++it)
11977 {
11978 (*it)->i_commit();
11979 }
11980 }
11981
11982 bool commitUSBControllers = false;
11983
11984 if (mUSBControllers.isBackedUp())
11985 {
11986 mUSBControllers.commit();
11987
11988 if (mPeer)
11989 {
11990 /* Commit all changes to new controllers (this will reshare data with
11991 * peers for those who have peers) */
11992 USBControllerList *newList = new USBControllerList();
11993 for (USBControllerList::const_iterator
11994 it = mUSBControllers->begin();
11995 it != mUSBControllers->end();
11996 ++it)
11997 {
11998 (*it)->i_commit();
11999
12000 /* look if this controller has a peer device */
12001 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12002 if (!peer)
12003 {
12004 /* no peer means the device is a newly created one;
12005 * create a peer owning data this device share it with */
12006 peer.createObject();
12007 peer->init(mPeer, *it, true /* aReshare */);
12008 }
12009 else
12010 {
12011 /* remove peer from the old list */
12012 mPeer->mUSBControllers->remove(peer);
12013 }
12014 /* and add it to the new list */
12015 newList->push_back(peer);
12016 }
12017
12018 /* uninit old peer's controllers that are left */
12019 for (USBControllerList::const_iterator
12020 it = mPeer->mUSBControllers->begin();
12021 it != mPeer->mUSBControllers->end();
12022 ++it)
12023 {
12024 (*it)->uninit();
12025 }
12026
12027 /* attach new list of controllers to our peer */
12028 mPeer->mUSBControllers.attach(newList);
12029 }
12030 else
12031 {
12032 /* we have no peer (our parent is the newly created machine);
12033 * just commit changes to devices */
12034 commitUSBControllers = true;
12035 }
12036 }
12037 else
12038 {
12039 /* the list of controllers itself is not changed,
12040 * just commit changes to controllers themselves */
12041 commitUSBControllers = true;
12042 }
12043
12044 if (commitUSBControllers)
12045 {
12046 for (USBControllerList::const_iterator
12047 it = mUSBControllers->begin();
12048 it != mUSBControllers->end();
12049 ++it)
12050 {
12051 (*it)->i_commit();
12052 }
12053 }
12054
12055 if (i_isSessionMachine())
12056 {
12057 /* attach new data to the primary machine and reshare it */
12058 mPeer->mUserData.attach(mUserData);
12059 mPeer->mHWData.attach(mHWData);
12060 /* mmMediumAttachments is reshared by fixupMedia */
12061 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12062 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12063 }
12064}
12065
12066/**
12067 * Copies all the hardware data from the given machine.
12068 *
12069 * Currently, only called when the VM is being restored from a snapshot. In
12070 * particular, this implies that the VM is not running during this method's
12071 * call.
12072 *
12073 * @note This method must be called from under this object's lock.
12074 *
12075 * @note This method doesn't call #i_commit(), so all data remains backed up and
12076 * unsaved.
12077 */
12078void Machine::i_copyFrom(Machine *aThat)
12079{
12080 AssertReturnVoid(!i_isSnapshotMachine());
12081 AssertReturnVoid(aThat->i_isSnapshotMachine());
12082
12083 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12084
12085 mHWData.assignCopy(aThat->mHWData);
12086
12087 // create copies of all shared folders (mHWData after attaching a copy
12088 // contains just references to original objects)
12089 for (HWData::SharedFolderList::iterator
12090 it = mHWData->mSharedFolders.begin();
12091 it != mHWData->mSharedFolders.end();
12092 ++it)
12093 {
12094 ComObjPtr<SharedFolder> folder;
12095 folder.createObject();
12096 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12097 AssertComRC(rc);
12098 *it = folder;
12099 }
12100
12101 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12102 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12103 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12104 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12105 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12106 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12107 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12108
12109 /* create private copies of all controllers */
12110 mStorageControllers.backup();
12111 mStorageControllers->clear();
12112 for (StorageControllerList::const_iterator
12113 it = aThat->mStorageControllers->begin();
12114 it != aThat->mStorageControllers->end();
12115 ++it)
12116 {
12117 ComObjPtr<StorageController> ctrl;
12118 ctrl.createObject();
12119 ctrl->initCopy(this, *it);
12120 mStorageControllers->push_back(ctrl);
12121 }
12122
12123 /* create private copies of all USB controllers */
12124 mUSBControllers.backup();
12125 mUSBControllers->clear();
12126 for (USBControllerList::const_iterator
12127 it = aThat->mUSBControllers->begin();
12128 it != aThat->mUSBControllers->end();
12129 ++it)
12130 {
12131 ComObjPtr<USBController> ctrl;
12132 ctrl.createObject();
12133 ctrl->initCopy(this, *it);
12134 mUSBControllers->push_back(ctrl);
12135 }
12136
12137 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12138 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12139 {
12140 if (mNetworkAdapters[slot].isNotNull())
12141 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12142 else
12143 {
12144 unconst(mNetworkAdapters[slot]).createObject();
12145 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12146 }
12147 }
12148 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12149 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12150 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12151 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12152}
12153
12154/**
12155 * Returns whether the given storage controller is hotplug capable.
12156 *
12157 * @returns true if the controller supports hotplugging
12158 * false otherwise.
12159 * @param enmCtrlType The controller type to check for.
12160 */
12161bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12162{
12163 ComPtr<ISystemProperties> systemProperties;
12164 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12165 if (FAILED(rc))
12166 return false;
12167
12168 BOOL aHotplugCapable = FALSE;
12169 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12170
12171 return RT_BOOL(aHotplugCapable);
12172}
12173
12174#ifdef VBOX_WITH_RESOURCE_USAGE_API
12175
12176void Machine::i_getDiskList(MediaList &list)
12177{
12178 for (MediumAttachmentList::const_iterator
12179 it = mMediumAttachments->begin();
12180 it != mMediumAttachments->end();
12181 ++it)
12182 {
12183 MediumAttachment *pAttach = *it;
12184 /* just in case */
12185 AssertContinue(pAttach);
12186
12187 AutoCaller localAutoCallerA(pAttach);
12188 if (FAILED(localAutoCallerA.rc())) continue;
12189
12190 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12191
12192 if (pAttach->i_getType() == DeviceType_HardDisk)
12193 list.push_back(pAttach->i_getMedium());
12194 }
12195}
12196
12197void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12198{
12199 AssertReturnVoid(isWriteLockOnCurrentThread());
12200 AssertPtrReturnVoid(aCollector);
12201
12202 pm::CollectorHAL *hal = aCollector->getHAL();
12203 /* Create sub metrics */
12204 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12205 "Percentage of processor time spent in user mode by the VM process.");
12206 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12207 "Percentage of processor time spent in kernel mode by the VM process.");
12208 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12209 "Size of resident portion of VM process in memory.");
12210 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12211 "Actual size of all VM disks combined.");
12212 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12213 "Network receive rate.");
12214 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12215 "Network transmit rate.");
12216 /* Create and register base metrics */
12217 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12218 cpuLoadUser, cpuLoadKernel);
12219 aCollector->registerBaseMetric(cpuLoad);
12220 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12221 ramUsageUsed);
12222 aCollector->registerBaseMetric(ramUsage);
12223 MediaList disks;
12224 i_getDiskList(disks);
12225 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12226 diskUsageUsed);
12227 aCollector->registerBaseMetric(diskUsage);
12228
12229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12230 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12231 new pm::AggregateAvg()));
12232 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12233 new pm::AggregateMin()));
12234 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12235 new pm::AggregateMax()));
12236 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12237 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12238 new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12240 new pm::AggregateMin()));
12241 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12242 new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12245 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12246 new pm::AggregateAvg()));
12247 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12248 new pm::AggregateMin()));
12249 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12250 new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12253 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12254 new pm::AggregateAvg()));
12255 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12256 new pm::AggregateMin()));
12257 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12258 new pm::AggregateMax()));
12259
12260
12261 /* Guest metrics collector */
12262 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12263 aCollector->registerGuest(mCollectorGuest);
12264 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12265
12266 /* Create sub metrics */
12267 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12268 "Percentage of processor time spent in user mode as seen by the guest.");
12269 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12270 "Percentage of processor time spent in kernel mode as seen by the guest.");
12271 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12272 "Percentage of processor time spent idling as seen by the guest.");
12273
12274 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12275 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12276 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12277 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12278 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12279 pm::SubMetric *guestMemCache = new pm::SubMetric(
12280 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12281
12282 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12283 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12284
12285 /* Create and register base metrics */
12286 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12287 machineNetRx, machineNetTx);
12288 aCollector->registerBaseMetric(machineNetRate);
12289
12290 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12291 guestLoadUser, guestLoadKernel, guestLoadIdle);
12292 aCollector->registerBaseMetric(guestCpuLoad);
12293
12294 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12295 guestMemTotal, guestMemFree,
12296 guestMemBalloon, guestMemShared,
12297 guestMemCache, guestPagedTotal);
12298 aCollector->registerBaseMetric(guestCpuMem);
12299
12300 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12301 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12306 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12314
12315 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12316 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12319
12320 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12321 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12329
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12346 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12349
12350 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12351 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12354}
12355
12356void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12357{
12358 AssertReturnVoid(isWriteLockOnCurrentThread());
12359
12360 if (aCollector)
12361 {
12362 aCollector->unregisterMetricsFor(aMachine);
12363 aCollector->unregisterBaseMetricsFor(aMachine);
12364 }
12365}
12366
12367#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12368
12369
12370////////////////////////////////////////////////////////////////////////////////
12371
12372DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12373
12374HRESULT SessionMachine::FinalConstruct()
12375{
12376 LogFlowThisFunc(("\n"));
12377
12378 mClientToken = NULL;
12379
12380 return BaseFinalConstruct();
12381}
12382
12383void SessionMachine::FinalRelease()
12384{
12385 LogFlowThisFunc(("\n"));
12386
12387 Assert(!mClientToken);
12388 /* paranoia, should not hang around any more */
12389 if (mClientToken)
12390 {
12391 delete mClientToken;
12392 mClientToken = NULL;
12393 }
12394
12395 uninit(Uninit::Unexpected);
12396
12397 BaseFinalRelease();
12398}
12399
12400/**
12401 * @note Must be called only by Machine::LockMachine() from its own write lock.
12402 */
12403HRESULT SessionMachine::init(Machine *aMachine)
12404{
12405 LogFlowThisFuncEnter();
12406 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12407
12408 AssertReturn(aMachine, E_INVALIDARG);
12409
12410 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12411
12412 /* Enclose the state transition NotReady->InInit->Ready */
12413 AutoInitSpan autoInitSpan(this);
12414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12415
12416 HRESULT rc = S_OK;
12417
12418 RT_ZERO(mAuthLibCtx);
12419
12420 /* create the machine client token */
12421 try
12422 {
12423 mClientToken = new ClientToken(aMachine, this);
12424 if (!mClientToken->isReady())
12425 {
12426 delete mClientToken;
12427 mClientToken = NULL;
12428 rc = E_FAIL;
12429 }
12430 }
12431 catch (std::bad_alloc &)
12432 {
12433 rc = E_OUTOFMEMORY;
12434 }
12435 if (FAILED(rc))
12436 return rc;
12437
12438 /* memorize the peer Machine */
12439 unconst(mPeer) = aMachine;
12440 /* share the parent pointer */
12441 unconst(mParent) = aMachine->mParent;
12442
12443 /* take the pointers to data to share */
12444 mData.share(aMachine->mData);
12445 mSSData.share(aMachine->mSSData);
12446
12447 mUserData.share(aMachine->mUserData);
12448 mHWData.share(aMachine->mHWData);
12449 mMediumAttachments.share(aMachine->mMediumAttachments);
12450
12451 mStorageControllers.allocate();
12452 for (StorageControllerList::const_iterator
12453 it = aMachine->mStorageControllers->begin();
12454 it != aMachine->mStorageControllers->end();
12455 ++it)
12456 {
12457 ComObjPtr<StorageController> ctl;
12458 ctl.createObject();
12459 ctl->init(this, *it);
12460 mStorageControllers->push_back(ctl);
12461 }
12462
12463 mUSBControllers.allocate();
12464 for (USBControllerList::const_iterator
12465 it = aMachine->mUSBControllers->begin();
12466 it != aMachine->mUSBControllers->end();
12467 ++it)
12468 {
12469 ComObjPtr<USBController> ctl;
12470 ctl.createObject();
12471 ctl->init(this, *it);
12472 mUSBControllers->push_back(ctl);
12473 }
12474
12475 unconst(mBIOSSettings).createObject();
12476 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12477 unconst(mRecordingSettings).createObject();
12478 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12479 /* create another GraphicsAdapter object that will be mutable */
12480 unconst(mGraphicsAdapter).createObject();
12481 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12482 /* create another VRDEServer object that will be mutable */
12483 unconst(mVRDEServer).createObject();
12484 mVRDEServer->init(this, aMachine->mVRDEServer);
12485 /* create another audio adapter object that will be mutable */
12486 unconst(mAudioAdapter).createObject();
12487 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12488 /* create a list of serial ports that will be mutable */
12489 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12490 {
12491 unconst(mSerialPorts[slot]).createObject();
12492 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12493 }
12494 /* create a list of parallel ports that will be mutable */
12495 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12496 {
12497 unconst(mParallelPorts[slot]).createObject();
12498 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12499 }
12500
12501 /* create another USB device filters object that will be mutable */
12502 unconst(mUSBDeviceFilters).createObject();
12503 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12504
12505 /* create a list of network adapters that will be mutable */
12506 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12507 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12508 {
12509 unconst(mNetworkAdapters[slot]).createObject();
12510 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12511 }
12512
12513 /* create another bandwidth control object that will be mutable */
12514 unconst(mBandwidthControl).createObject();
12515 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12516
12517 /* default is to delete saved state on Saved -> PoweredOff transition */
12518 mRemoveSavedState = true;
12519
12520 /* Confirm a successful initialization when it's the case */
12521 autoInitSpan.setSucceeded();
12522
12523 miNATNetworksStarted = 0;
12524
12525 LogFlowThisFuncLeave();
12526 return rc;
12527}
12528
12529/**
12530 * Uninitializes this session object. If the reason is other than
12531 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12532 * or the client watcher code.
12533 *
12534 * @param aReason uninitialization reason
12535 *
12536 * @note Locks mParent + this object for writing.
12537 */
12538void SessionMachine::uninit(Uninit::Reason aReason)
12539{
12540 LogFlowThisFuncEnter();
12541 LogFlowThisFunc(("reason=%d\n", aReason));
12542
12543 /*
12544 * Strongly reference ourselves to prevent this object deletion after
12545 * mData->mSession.mMachine.setNull() below (which can release the last
12546 * reference and call the destructor). Important: this must be done before
12547 * accessing any members (and before AutoUninitSpan that does it as well).
12548 * This self reference will be released as the very last step on return.
12549 */
12550 ComObjPtr<SessionMachine> selfRef;
12551 if (aReason != Uninit::Unexpected)
12552 selfRef = this;
12553
12554 /* Enclose the state transition Ready->InUninit->NotReady */
12555 AutoUninitSpan autoUninitSpan(this);
12556 if (autoUninitSpan.uninitDone())
12557 {
12558 LogFlowThisFunc(("Already uninitialized\n"));
12559 LogFlowThisFuncLeave();
12560 return;
12561 }
12562
12563 if (autoUninitSpan.initFailed())
12564 {
12565 /* We've been called by init() because it's failed. It's not really
12566 * necessary (nor it's safe) to perform the regular uninit sequence
12567 * below, the following is enough.
12568 */
12569 LogFlowThisFunc(("Initialization failed.\n"));
12570 /* destroy the machine client token */
12571 if (mClientToken)
12572 {
12573 delete mClientToken;
12574 mClientToken = NULL;
12575 }
12576 uninitDataAndChildObjects();
12577 mData.free();
12578 unconst(mParent) = NULL;
12579 unconst(mPeer) = NULL;
12580 LogFlowThisFuncLeave();
12581 return;
12582 }
12583
12584 MachineState_T lastState;
12585 {
12586 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12587 lastState = mData->mMachineState;
12588 }
12589 NOREF(lastState);
12590
12591#ifdef VBOX_WITH_USB
12592 // release all captured USB devices, but do this before requesting the locks below
12593 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12594 {
12595 /* Console::captureUSBDevices() is called in the VM process only after
12596 * setting the machine state to Starting or Restoring.
12597 * Console::detachAllUSBDevices() will be called upon successful
12598 * termination. So, we need to release USB devices only if there was
12599 * an abnormal termination of a running VM.
12600 *
12601 * This is identical to SessionMachine::DetachAllUSBDevices except
12602 * for the aAbnormal argument. */
12603 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12604 AssertComRC(rc);
12605 NOREF(rc);
12606
12607 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12608 if (service)
12609 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12610 }
12611#endif /* VBOX_WITH_USB */
12612
12613 // we need to lock this object in uninit() because the lock is shared
12614 // with mPeer (as well as data we modify below). mParent lock is needed
12615 // by several calls to it.
12616 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12617
12618#ifdef VBOX_WITH_RESOURCE_USAGE_API
12619 /*
12620 * It is safe to call Machine::i_unregisterMetrics() here because
12621 * PerformanceCollector::samplerCallback no longer accesses guest methods
12622 * holding the lock.
12623 */
12624 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12625 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12626 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12627 if (mCollectorGuest)
12628 {
12629 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12630 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12631 mCollectorGuest = NULL;
12632 }
12633#endif
12634
12635 if (aReason == Uninit::Abnormal)
12636 {
12637 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12638
12639 /* reset the state to Aborted */
12640 if (mData->mMachineState != MachineState_Aborted)
12641 i_setMachineState(MachineState_Aborted);
12642 }
12643
12644 // any machine settings modified?
12645 if (mData->flModifications)
12646 {
12647 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12648 i_rollback(false /* aNotify */);
12649 }
12650
12651 mData->mSession.mPID = NIL_RTPROCESS;
12652
12653 if (aReason == Uninit::Unexpected)
12654 {
12655 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12656 * client watcher thread to update the set of machines that have open
12657 * sessions. */
12658 mParent->i_updateClientWatcher();
12659 }
12660
12661 /* uninitialize all remote controls */
12662 if (mData->mSession.mRemoteControls.size())
12663 {
12664 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12665 mData->mSession.mRemoteControls.size()));
12666
12667 /* Always restart a the beginning, since the iterator is invalidated
12668 * by using erase(). */
12669 for (Data::Session::RemoteControlList::iterator
12670 it = mData->mSession.mRemoteControls.begin();
12671 it != mData->mSession.mRemoteControls.end();
12672 it = mData->mSession.mRemoteControls.begin())
12673 {
12674 ComPtr<IInternalSessionControl> pControl = *it;
12675 mData->mSession.mRemoteControls.erase(it);
12676 multilock.release();
12677 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12678 HRESULT rc = pControl->Uninitialize();
12679 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12680 if (FAILED(rc))
12681 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12682 multilock.acquire();
12683 }
12684 mData->mSession.mRemoteControls.clear();
12685 }
12686
12687 /* Remove all references to the NAT network service. The service will stop
12688 * if all references (also from other VMs) are removed. */
12689 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12690 {
12691 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12692 {
12693 BOOL enabled;
12694 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12695 if ( FAILED(hrc)
12696 || !enabled)
12697 continue;
12698
12699 NetworkAttachmentType_T type;
12700 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12701 if ( SUCCEEDED(hrc)
12702 && type == NetworkAttachmentType_NATNetwork)
12703 {
12704 Bstr name;
12705 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12706 if (SUCCEEDED(hrc))
12707 {
12708 multilock.release();
12709 Utf8Str strName(name);
12710 LogRel(("VM '%s' stops using NAT network '%s'\n",
12711 mUserData->s.strName.c_str(), strName.c_str()));
12712 mParent->i_natNetworkRefDec(strName);
12713 multilock.acquire();
12714 }
12715 }
12716 }
12717 }
12718
12719 /*
12720 * An expected uninitialization can come only from #i_checkForDeath().
12721 * Otherwise it means that something's gone really wrong (for example,
12722 * the Session implementation has released the VirtualBox reference
12723 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12724 * etc). However, it's also possible, that the client releases the IPC
12725 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12726 * but the VirtualBox release event comes first to the server process.
12727 * This case is practically possible, so we should not assert on an
12728 * unexpected uninit, just log a warning.
12729 */
12730
12731 if (aReason == Uninit::Unexpected)
12732 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12733
12734 if (aReason != Uninit::Normal)
12735 {
12736 mData->mSession.mDirectControl.setNull();
12737 }
12738 else
12739 {
12740 /* this must be null here (see #OnSessionEnd()) */
12741 Assert(mData->mSession.mDirectControl.isNull());
12742 Assert(mData->mSession.mState == SessionState_Unlocking);
12743 Assert(!mData->mSession.mProgress.isNull());
12744 }
12745 if (mData->mSession.mProgress)
12746 {
12747 if (aReason == Uninit::Normal)
12748 mData->mSession.mProgress->i_notifyComplete(S_OK);
12749 else
12750 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12751 COM_IIDOF(ISession),
12752 getComponentName(),
12753 tr("The VM session was aborted"));
12754 mData->mSession.mProgress.setNull();
12755 }
12756
12757 if (mConsoleTaskData.mProgress)
12758 {
12759 Assert(aReason == Uninit::Abnormal);
12760 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12761 COM_IIDOF(ISession),
12762 getComponentName(),
12763 tr("The VM session was aborted"));
12764 mConsoleTaskData.mProgress.setNull();
12765 }
12766
12767 /* remove the association between the peer machine and this session machine */
12768 Assert( (SessionMachine*)mData->mSession.mMachine == this
12769 || aReason == Uninit::Unexpected);
12770
12771 /* reset the rest of session data */
12772 mData->mSession.mLockType = LockType_Null;
12773 mData->mSession.mMachine.setNull();
12774 mData->mSession.mState = SessionState_Unlocked;
12775 mData->mSession.mName.setNull();
12776
12777 /* destroy the machine client token before leaving the exclusive lock */
12778 if (mClientToken)
12779 {
12780 delete mClientToken;
12781 mClientToken = NULL;
12782 }
12783
12784 /* fire an event */
12785 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12786
12787 uninitDataAndChildObjects();
12788
12789 /* free the essential data structure last */
12790 mData.free();
12791
12792 /* release the exclusive lock before setting the below two to NULL */
12793 multilock.release();
12794
12795 unconst(mParent) = NULL;
12796 unconst(mPeer) = NULL;
12797
12798 AuthLibUnload(&mAuthLibCtx);
12799
12800 LogFlowThisFuncLeave();
12801}
12802
12803// util::Lockable interface
12804////////////////////////////////////////////////////////////////////////////////
12805
12806/**
12807 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12808 * with the primary Machine instance (mPeer).
12809 */
12810RWLockHandle *SessionMachine::lockHandle() const
12811{
12812 AssertReturn(mPeer != NULL, NULL);
12813 return mPeer->lockHandle();
12814}
12815
12816// IInternalMachineControl methods
12817////////////////////////////////////////////////////////////////////////////////
12818
12819/**
12820 * Passes collected guest statistics to performance collector object
12821 */
12822HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12823 ULONG aCpuKernel, ULONG aCpuIdle,
12824 ULONG aMemTotal, ULONG aMemFree,
12825 ULONG aMemBalloon, ULONG aMemShared,
12826 ULONG aMemCache, ULONG aPageTotal,
12827 ULONG aAllocVMM, ULONG aFreeVMM,
12828 ULONG aBalloonedVMM, ULONG aSharedVMM,
12829 ULONG aVmNetRx, ULONG aVmNetTx)
12830{
12831#ifdef VBOX_WITH_RESOURCE_USAGE_API
12832 if (mCollectorGuest)
12833 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12834 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12835 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12836 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12837
12838 return S_OK;
12839#else
12840 NOREF(aValidStats);
12841 NOREF(aCpuUser);
12842 NOREF(aCpuKernel);
12843 NOREF(aCpuIdle);
12844 NOREF(aMemTotal);
12845 NOREF(aMemFree);
12846 NOREF(aMemBalloon);
12847 NOREF(aMemShared);
12848 NOREF(aMemCache);
12849 NOREF(aPageTotal);
12850 NOREF(aAllocVMM);
12851 NOREF(aFreeVMM);
12852 NOREF(aBalloonedVMM);
12853 NOREF(aSharedVMM);
12854 NOREF(aVmNetRx);
12855 NOREF(aVmNetTx);
12856 return E_NOTIMPL;
12857#endif
12858}
12859
12860////////////////////////////////////////////////////////////////////////////////
12861//
12862// SessionMachine task records
12863//
12864////////////////////////////////////////////////////////////////////////////////
12865
12866/**
12867 * Task record for saving the machine state.
12868 */
12869class SessionMachine::SaveStateTask
12870 : public Machine::Task
12871{
12872public:
12873 SaveStateTask(SessionMachine *m,
12874 Progress *p,
12875 const Utf8Str &t,
12876 Reason_T enmReason,
12877 const Utf8Str &strStateFilePath)
12878 : Task(m, p, t),
12879 m_enmReason(enmReason),
12880 m_strStateFilePath(strStateFilePath)
12881 {}
12882
12883private:
12884 void handler()
12885 {
12886 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12887 }
12888
12889 Reason_T m_enmReason;
12890 Utf8Str m_strStateFilePath;
12891
12892 friend class SessionMachine;
12893};
12894
12895/**
12896 * Task thread implementation for SessionMachine::SaveState(), called from
12897 * SessionMachine::taskHandler().
12898 *
12899 * @note Locks this object for writing.
12900 *
12901 * @param task
12902 * @return
12903 */
12904void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12905{
12906 LogFlowThisFuncEnter();
12907
12908 AutoCaller autoCaller(this);
12909 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12910 if (FAILED(autoCaller.rc()))
12911 {
12912 /* we might have been uninitialized because the session was accidentally
12913 * closed by the client, so don't assert */
12914 HRESULT rc = setError(E_FAIL,
12915 tr("The session has been accidentally closed"));
12916 task.m_pProgress->i_notifyComplete(rc);
12917 LogFlowThisFuncLeave();
12918 return;
12919 }
12920
12921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12922
12923 HRESULT rc = S_OK;
12924
12925 try
12926 {
12927 ComPtr<IInternalSessionControl> directControl;
12928 if (mData->mSession.mLockType == LockType_VM)
12929 directControl = mData->mSession.mDirectControl;
12930 if (directControl.isNull())
12931 throw setError(VBOX_E_INVALID_VM_STATE,
12932 tr("Trying to save state without a running VM"));
12933 alock.release();
12934 BOOL fSuspendedBySave;
12935 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12936 Assert(!fSuspendedBySave);
12937 alock.acquire();
12938
12939 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12940 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12941 throw E_FAIL);
12942
12943 if (SUCCEEDED(rc))
12944 {
12945 mSSData->strStateFilePath = task.m_strStateFilePath;
12946
12947 /* save all VM settings */
12948 rc = i_saveSettings(NULL);
12949 // no need to check whether VirtualBox.xml needs saving also since
12950 // we can't have a name change pending at this point
12951 }
12952 else
12953 {
12954 // On failure, set the state to the state we had at the beginning.
12955 i_setMachineState(task.m_machineStateBackup);
12956 i_updateMachineStateOnClient();
12957
12958 // Delete the saved state file (might have been already created).
12959 // No need to check whether this is shared with a snapshot here
12960 // because we certainly created a fresh saved state file here.
12961 RTFileDelete(task.m_strStateFilePath.c_str());
12962 }
12963 }
12964 catch (HRESULT aRC) { rc = aRC; }
12965
12966 task.m_pProgress->i_notifyComplete(rc);
12967
12968 LogFlowThisFuncLeave();
12969}
12970
12971/**
12972 * @note Locks this object for writing.
12973 */
12974HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12975{
12976 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12977}
12978
12979HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12980{
12981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12982
12983 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12984 if (FAILED(rc)) return rc;
12985
12986 if ( mData->mMachineState != MachineState_Running
12987 && mData->mMachineState != MachineState_Paused
12988 )
12989 return setError(VBOX_E_INVALID_VM_STATE,
12990 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12991 Global::stringifyMachineState(mData->mMachineState));
12992
12993 ComObjPtr<Progress> pProgress;
12994 pProgress.createObject();
12995 rc = pProgress->init(i_getVirtualBox(),
12996 static_cast<IMachine *>(this) /* aInitiator */,
12997 tr("Saving the execution state of the virtual machine"),
12998 FALSE /* aCancelable */);
12999 if (FAILED(rc))
13000 return rc;
13001
13002 Utf8Str strStateFilePath;
13003 i_composeSavedStateFilename(strStateFilePath);
13004
13005 /* create and start the task on a separate thread (note that it will not
13006 * start working until we release alock) */
13007 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13008 rc = pTask->createThread();
13009 if (FAILED(rc))
13010 return rc;
13011
13012 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13013 i_setMachineState(MachineState_Saving);
13014 i_updateMachineStateOnClient();
13015
13016 pProgress.queryInterfaceTo(aProgress.asOutParam());
13017
13018 return S_OK;
13019}
13020
13021/**
13022 * @note Locks this object for writing.
13023 */
13024HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13025{
13026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13027
13028 HRESULT rc = i_checkStateDependency(MutableStateDep);
13029 if (FAILED(rc)) return rc;
13030
13031 if ( mData->mMachineState != MachineState_PoweredOff
13032 && mData->mMachineState != MachineState_Teleported
13033 && mData->mMachineState != MachineState_Aborted
13034 )
13035 return setError(VBOX_E_INVALID_VM_STATE,
13036 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13037 Global::stringifyMachineState(mData->mMachineState));
13038
13039 com::Utf8Str stateFilePathFull;
13040 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13041 if (RT_FAILURE(vrc))
13042 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13043 tr("Invalid saved state file path '%s' (%Rrc)"),
13044 aSavedStateFile.c_str(),
13045 vrc);
13046
13047 mSSData->strStateFilePath = stateFilePathFull;
13048
13049 /* The below i_setMachineState() will detect the state transition and will
13050 * update the settings file */
13051
13052 return i_setMachineState(MachineState_Saved);
13053}
13054
13055/**
13056 * @note Locks this object for writing.
13057 */
13058HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13059{
13060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13061
13062 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13063 if (FAILED(rc)) return rc;
13064
13065 if (mData->mMachineState != MachineState_Saved)
13066 return setError(VBOX_E_INVALID_VM_STATE,
13067 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13068 Global::stringifyMachineState(mData->mMachineState));
13069
13070 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13071
13072 /*
13073 * Saved -> PoweredOff transition will be detected in the SessionMachine
13074 * and properly handled.
13075 */
13076 rc = i_setMachineState(MachineState_PoweredOff);
13077 return rc;
13078}
13079
13080
13081/**
13082 * @note Locks the same as #i_setMachineState() does.
13083 */
13084HRESULT SessionMachine::updateState(MachineState_T aState)
13085{
13086 return i_setMachineState(aState);
13087}
13088
13089/**
13090 * @note Locks this object for writing.
13091 */
13092HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13093{
13094 IProgress *pProgress(aProgress);
13095
13096 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13097
13098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13099
13100 if (mData->mSession.mState != SessionState_Locked)
13101 return VBOX_E_INVALID_OBJECT_STATE;
13102
13103 if (!mData->mSession.mProgress.isNull())
13104 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13105
13106 /* If we didn't reference the NAT network service yet, add a reference to
13107 * force a start */
13108 if (miNATNetworksStarted < 1)
13109 {
13110 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13111 {
13112 BOOL enabled;
13113 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13114 if ( FAILED(hrc)
13115 || !enabled)
13116 continue;
13117
13118 NetworkAttachmentType_T type;
13119 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13120 if ( SUCCEEDED(hrc)
13121 && type == NetworkAttachmentType_NATNetwork)
13122 {
13123 Bstr name;
13124 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13125 if (SUCCEEDED(hrc))
13126 {
13127 Utf8Str strName(name);
13128 LogRel(("VM '%s' starts using NAT network '%s'\n",
13129 mUserData->s.strName.c_str(), strName.c_str()));
13130 mPeer->lockHandle()->unlockWrite();
13131 mParent->i_natNetworkRefInc(strName);
13132#ifdef RT_LOCK_STRICT
13133 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13134#else
13135 mPeer->lockHandle()->lockWrite();
13136#endif
13137 }
13138 }
13139 }
13140 miNATNetworksStarted++;
13141 }
13142
13143 LogFlowThisFunc(("returns S_OK.\n"));
13144 return S_OK;
13145}
13146
13147/**
13148 * @note Locks this object for writing.
13149 */
13150HRESULT SessionMachine::endPowerUp(LONG aResult)
13151{
13152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13153
13154 if (mData->mSession.mState != SessionState_Locked)
13155 return VBOX_E_INVALID_OBJECT_STATE;
13156
13157 /* Finalize the LaunchVMProcess progress object. */
13158 if (mData->mSession.mProgress)
13159 {
13160 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13161 mData->mSession.mProgress.setNull();
13162 }
13163
13164 if (SUCCEEDED((HRESULT)aResult))
13165 {
13166#ifdef VBOX_WITH_RESOURCE_USAGE_API
13167 /* The VM has been powered up successfully, so it makes sense
13168 * now to offer the performance metrics for a running machine
13169 * object. Doing it earlier wouldn't be safe. */
13170 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13171 mData->mSession.mPID);
13172#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13173 }
13174
13175 return S_OK;
13176}
13177
13178/**
13179 * @note Locks this object for writing.
13180 */
13181HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13182{
13183 LogFlowThisFuncEnter();
13184
13185#ifdef VBOX_WITH_CLOUD_NET
13186 mPeer->i_disconnectFromCloudNetwork();
13187#endif /* VBOX_WITH_CLOUD_NET */
13188
13189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13190
13191 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13192 E_FAIL);
13193
13194 /* create a progress object to track operation completion */
13195 ComObjPtr<Progress> pProgress;
13196 pProgress.createObject();
13197 pProgress->init(i_getVirtualBox(),
13198 static_cast<IMachine *>(this) /* aInitiator */,
13199 tr("Stopping the virtual machine"),
13200 FALSE /* aCancelable */);
13201
13202 /* fill in the console task data */
13203 mConsoleTaskData.mLastState = mData->mMachineState;
13204 mConsoleTaskData.mProgress = pProgress;
13205
13206 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13207 i_setMachineState(MachineState_Stopping);
13208
13209 pProgress.queryInterfaceTo(aProgress.asOutParam());
13210
13211 return S_OK;
13212}
13213
13214/**
13215 * @note Locks this object for writing.
13216 */
13217HRESULT SessionMachine::endPoweringDown(LONG aResult,
13218 const com::Utf8Str &aErrMsg)
13219{
13220 LogFlowThisFuncEnter();
13221
13222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13223
13224 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13225 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13226 && mConsoleTaskData.mLastState != MachineState_Null,
13227 E_FAIL);
13228
13229 /*
13230 * On failure, set the state to the state we had when BeginPoweringDown()
13231 * was called (this is expected by Console::PowerDown() and the associated
13232 * task). On success the VM process already changed the state to
13233 * MachineState_PoweredOff, so no need to do anything.
13234 */
13235 if (FAILED(aResult))
13236 i_setMachineState(mConsoleTaskData.mLastState);
13237
13238 /* notify the progress object about operation completion */
13239 Assert(mConsoleTaskData.mProgress);
13240 if (SUCCEEDED(aResult))
13241 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13242 else
13243 {
13244 if (aErrMsg.length())
13245 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13246 COM_IIDOF(ISession),
13247 getComponentName(),
13248 aErrMsg.c_str());
13249 else
13250 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13251 }
13252
13253 /* clear out the temporary saved state data */
13254 mConsoleTaskData.mLastState = MachineState_Null;
13255 mConsoleTaskData.mProgress.setNull();
13256
13257 LogFlowThisFuncLeave();
13258 return S_OK;
13259}
13260
13261
13262/**
13263 * Goes through the USB filters of the given machine to see if the given
13264 * device matches any filter or not.
13265 *
13266 * @note Locks the same as USBController::hasMatchingFilter() does.
13267 */
13268HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13269 BOOL *aMatched,
13270 ULONG *aMaskedInterfaces)
13271{
13272 LogFlowThisFunc(("\n"));
13273
13274#ifdef VBOX_WITH_USB
13275 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13276#else
13277 NOREF(aDevice);
13278 NOREF(aMaskedInterfaces);
13279 *aMatched = FALSE;
13280#endif
13281
13282 return S_OK;
13283}
13284
13285/**
13286 * @note Locks the same as Host::captureUSBDevice() does.
13287 */
13288HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13289{
13290 LogFlowThisFunc(("\n"));
13291
13292#ifdef VBOX_WITH_USB
13293 /* if captureDeviceForVM() fails, it must have set extended error info */
13294 clearError();
13295 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13296 if (FAILED(rc)) return rc;
13297
13298 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13299 AssertReturn(service, E_FAIL);
13300 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13301#else
13302 RT_NOREF(aId, aCaptureFilename);
13303 return E_NOTIMPL;
13304#endif
13305}
13306
13307/**
13308 * @note Locks the same as Host::detachUSBDevice() does.
13309 */
13310HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13311 BOOL aDone)
13312{
13313 LogFlowThisFunc(("\n"));
13314
13315#ifdef VBOX_WITH_USB
13316 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13317 AssertReturn(service, E_FAIL);
13318 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13319#else
13320 NOREF(aId);
13321 NOREF(aDone);
13322 return E_NOTIMPL;
13323#endif
13324}
13325
13326/**
13327 * Inserts all machine filters to the USB proxy service and then calls
13328 * Host::autoCaptureUSBDevices().
13329 *
13330 * Called by Console from the VM process upon VM startup.
13331 *
13332 * @note Locks what called methods lock.
13333 */
13334HRESULT SessionMachine::autoCaptureUSBDevices()
13335{
13336 LogFlowThisFunc(("\n"));
13337
13338#ifdef VBOX_WITH_USB
13339 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13340 AssertComRC(rc);
13341 NOREF(rc);
13342
13343 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13344 AssertReturn(service, E_FAIL);
13345 return service->autoCaptureDevicesForVM(this);
13346#else
13347 return S_OK;
13348#endif
13349}
13350
13351/**
13352 * Removes all machine filters from the USB proxy service and then calls
13353 * Host::detachAllUSBDevices().
13354 *
13355 * Called by Console from the VM process upon normal VM termination or by
13356 * SessionMachine::uninit() upon abnormal VM termination (from under the
13357 * Machine/SessionMachine lock).
13358 *
13359 * @note Locks what called methods lock.
13360 */
13361HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13362{
13363 LogFlowThisFunc(("\n"));
13364
13365#ifdef VBOX_WITH_USB
13366 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13367 AssertComRC(rc);
13368 NOREF(rc);
13369
13370 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13371 AssertReturn(service, E_FAIL);
13372 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13373#else
13374 NOREF(aDone);
13375 return S_OK;
13376#endif
13377}
13378
13379/**
13380 * @note Locks this object for writing.
13381 */
13382HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13383 ComPtr<IProgress> &aProgress)
13384{
13385 LogFlowThisFuncEnter();
13386
13387 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13388 /*
13389 * We don't assert below because it might happen that a non-direct session
13390 * informs us it is closed right after we've been uninitialized -- it's ok.
13391 */
13392
13393 /* get IInternalSessionControl interface */
13394 ComPtr<IInternalSessionControl> control(aSession);
13395
13396 ComAssertRet(!control.isNull(), E_INVALIDARG);
13397
13398 /* Creating a Progress object requires the VirtualBox lock, and
13399 * thus locking it here is required by the lock order rules. */
13400 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13401
13402 if (control == mData->mSession.mDirectControl)
13403 {
13404 /* The direct session is being normally closed by the client process
13405 * ----------------------------------------------------------------- */
13406
13407 /* go to the closing state (essential for all open*Session() calls and
13408 * for #i_checkForDeath()) */
13409 Assert(mData->mSession.mState == SessionState_Locked);
13410 mData->mSession.mState = SessionState_Unlocking;
13411
13412 /* set direct control to NULL to release the remote instance */
13413 mData->mSession.mDirectControl.setNull();
13414 LogFlowThisFunc(("Direct control is set to NULL\n"));
13415
13416 if (mData->mSession.mProgress)
13417 {
13418 /* finalize the progress, someone might wait if a frontend
13419 * closes the session before powering on the VM. */
13420 mData->mSession.mProgress->notifyComplete(E_FAIL,
13421 COM_IIDOF(ISession),
13422 getComponentName(),
13423 tr("The VM session was closed before any attempt to power it on"));
13424 mData->mSession.mProgress.setNull();
13425 }
13426
13427 /* Create the progress object the client will use to wait until
13428 * #i_checkForDeath() is called to uninitialize this session object after
13429 * it releases the IPC semaphore.
13430 * Note! Because we're "reusing" mProgress here, this must be a proxy
13431 * object just like for LaunchVMProcess. */
13432 Assert(mData->mSession.mProgress.isNull());
13433 ComObjPtr<ProgressProxy> progress;
13434 progress.createObject();
13435 ComPtr<IUnknown> pPeer(mPeer);
13436 progress->init(mParent, pPeer,
13437 Bstr(tr("Closing session")).raw(),
13438 FALSE /* aCancelable */);
13439 progress.queryInterfaceTo(aProgress.asOutParam());
13440 mData->mSession.mProgress = progress;
13441 }
13442 else
13443 {
13444 /* the remote session is being normally closed */
13445 bool found = false;
13446 for (Data::Session::RemoteControlList::iterator
13447 it = mData->mSession.mRemoteControls.begin();
13448 it != mData->mSession.mRemoteControls.end();
13449 ++it)
13450 {
13451 if (control == *it)
13452 {
13453 found = true;
13454 // This MUST be erase(it), not remove(*it) as the latter
13455 // triggers a very nasty use after free due to the place where
13456 // the value "lives".
13457 mData->mSession.mRemoteControls.erase(it);
13458 break;
13459 }
13460 }
13461 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13462 E_INVALIDARG);
13463 }
13464
13465 /* signal the client watcher thread, because the client is going away */
13466 mParent->i_updateClientWatcher();
13467
13468 LogFlowThisFuncLeave();
13469 return S_OK;
13470}
13471
13472HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13473{
13474#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13475 ULONG uID;
13476 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13477 if (RT_SUCCESS(rc))
13478 {
13479 if (aID)
13480 *aID = uID;
13481 return S_OK;
13482 }
13483 return E_FAIL;
13484#else
13485 RT_NOREF(aParms, aID);
13486 ReturnComNotImplemented();
13487#endif
13488}
13489
13490HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13491{
13492#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13493 return mParent->i_onClipboardAreaUnregister(aID);
13494#else
13495 RT_NOREF(aID);
13496 ReturnComNotImplemented();
13497#endif
13498}
13499
13500HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13501{
13502#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13503 return mParent->i_onClipboardAreaAttach(aID);
13504#else
13505 RT_NOREF(aID);
13506 ReturnComNotImplemented();
13507#endif
13508}
13509HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13510{
13511#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13512 return mParent->i_onClipboardAreaDetach(aID);
13513#else
13514 RT_NOREF(aID);
13515 ReturnComNotImplemented();
13516#endif
13517}
13518
13519HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13520{
13521#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13522 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13523 if (aID)
13524 *aID = uID;
13525 return S_OK;
13526#else
13527 RT_NOREF(aID);
13528 ReturnComNotImplemented();
13529#endif
13530}
13531
13532HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13533{
13534#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13535 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13536 if (aRefCount)
13537 *aRefCount = uRefCount;
13538 return S_OK;
13539#else
13540 RT_NOREF(aID, aRefCount);
13541 ReturnComNotImplemented();
13542#endif
13543}
13544
13545HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13546 std::vector<com::Utf8Str> &aValues,
13547 std::vector<LONG64> &aTimestamps,
13548 std::vector<com::Utf8Str> &aFlags)
13549{
13550 LogFlowThisFunc(("\n"));
13551
13552#ifdef VBOX_WITH_GUEST_PROPS
13553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13554
13555 size_t cEntries = mHWData->mGuestProperties.size();
13556 aNames.resize(cEntries);
13557 aValues.resize(cEntries);
13558 aTimestamps.resize(cEntries);
13559 aFlags.resize(cEntries);
13560
13561 size_t i = 0;
13562 for (HWData::GuestPropertyMap::const_iterator
13563 it = mHWData->mGuestProperties.begin();
13564 it != mHWData->mGuestProperties.end();
13565 ++it, ++i)
13566 {
13567 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13568 aNames[i] = it->first;
13569 aValues[i] = it->second.strValue;
13570 aTimestamps[i] = it->second.mTimestamp;
13571
13572 /* If it is NULL, keep it NULL. */
13573 if (it->second.mFlags)
13574 {
13575 GuestPropWriteFlags(it->second.mFlags, szFlags);
13576 aFlags[i] = szFlags;
13577 }
13578 else
13579 aFlags[i] = "";
13580 }
13581 return S_OK;
13582#else
13583 ReturnComNotImplemented();
13584#endif
13585}
13586
13587HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13588 const com::Utf8Str &aValue,
13589 LONG64 aTimestamp,
13590 const com::Utf8Str &aFlags)
13591{
13592 LogFlowThisFunc(("\n"));
13593
13594#ifdef VBOX_WITH_GUEST_PROPS
13595 try
13596 {
13597 /*
13598 * Convert input up front.
13599 */
13600 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13601 if (aFlags.length())
13602 {
13603 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13604 AssertRCReturn(vrc, E_INVALIDARG);
13605 }
13606
13607 /*
13608 * Now grab the object lock, validate the state and do the update.
13609 */
13610
13611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13612
13613 if (!Global::IsOnline(mData->mMachineState))
13614 {
13615 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13616 VBOX_E_INVALID_VM_STATE);
13617 }
13618
13619 i_setModified(IsModified_MachineData);
13620 mHWData.backup();
13621
13622 bool fDelete = !aValue.length();
13623 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13624 if (it != mHWData->mGuestProperties.end())
13625 {
13626 if (!fDelete)
13627 {
13628 it->second.strValue = aValue;
13629 it->second.mTimestamp = aTimestamp;
13630 it->second.mFlags = fFlags;
13631 }
13632 else
13633 mHWData->mGuestProperties.erase(it);
13634
13635 mData->mGuestPropertiesModified = TRUE;
13636 }
13637 else if (!fDelete)
13638 {
13639 HWData::GuestProperty prop;
13640 prop.strValue = aValue;
13641 prop.mTimestamp = aTimestamp;
13642 prop.mFlags = fFlags;
13643
13644 mHWData->mGuestProperties[aName] = prop;
13645 mData->mGuestPropertiesModified = TRUE;
13646 }
13647
13648 alock.release();
13649
13650 mParent->i_onGuestPropertyChange(mData->mUuid,
13651 Bstr(aName).raw(),
13652 Bstr(aValue).raw(),
13653 Bstr(aFlags).raw());
13654 }
13655 catch (...)
13656 {
13657 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13658 }
13659 return S_OK;
13660#else
13661 ReturnComNotImplemented();
13662#endif
13663}
13664
13665
13666HRESULT SessionMachine::lockMedia()
13667{
13668 AutoMultiWriteLock2 alock(this->lockHandle(),
13669 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13670
13671 AssertReturn( mData->mMachineState == MachineState_Starting
13672 || mData->mMachineState == MachineState_Restoring
13673 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13674
13675 clearError();
13676 alock.release();
13677 return i_lockMedia();
13678}
13679
13680HRESULT SessionMachine::unlockMedia()
13681{
13682 HRESULT hrc = i_unlockMedia();
13683 return hrc;
13684}
13685
13686HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13687 ComPtr<IMediumAttachment> &aNewAttachment)
13688{
13689 // request the host lock first, since might be calling Host methods for getting host drives;
13690 // next, protect the media tree all the while we're in here, as well as our member variables
13691 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13692 this->lockHandle(),
13693 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13694
13695 IMediumAttachment *iAttach = aAttachment;
13696 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13697
13698 Utf8Str ctrlName;
13699 LONG lPort;
13700 LONG lDevice;
13701 bool fTempEject;
13702 {
13703 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13704
13705 /* Need to query the details first, as the IMediumAttachment reference
13706 * might be to the original settings, which we are going to change. */
13707 ctrlName = pAttach->i_getControllerName();
13708 lPort = pAttach->i_getPort();
13709 lDevice = pAttach->i_getDevice();
13710 fTempEject = pAttach->i_getTempEject();
13711 }
13712
13713 if (!fTempEject)
13714 {
13715 /* Remember previously mounted medium. The medium before taking the
13716 * backup is not necessarily the same thing. */
13717 ComObjPtr<Medium> oldmedium;
13718 oldmedium = pAttach->i_getMedium();
13719
13720 i_setModified(IsModified_Storage);
13721 mMediumAttachments.backup();
13722
13723 // The backup operation makes the pAttach reference point to the
13724 // old settings. Re-get the correct reference.
13725 pAttach = i_findAttachment(*mMediumAttachments.data(),
13726 ctrlName,
13727 lPort,
13728 lDevice);
13729
13730 {
13731 AutoCaller autoAttachCaller(this);
13732 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13733
13734 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13735 if (!oldmedium.isNull())
13736 oldmedium->i_removeBackReference(mData->mUuid);
13737
13738 pAttach->i_updateMedium(NULL);
13739 pAttach->i_updateEjected();
13740 }
13741
13742 i_setModified(IsModified_Storage);
13743 }
13744 else
13745 {
13746 {
13747 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13748 pAttach->i_updateEjected();
13749 }
13750 }
13751
13752 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13753
13754 return S_OK;
13755}
13756
13757HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13758 com::Utf8Str &aResult)
13759{
13760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13761
13762 HRESULT hr = S_OK;
13763
13764 if (!mAuthLibCtx.hAuthLibrary)
13765 {
13766 /* Load the external authentication library. */
13767 Bstr authLibrary;
13768 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13769
13770 Utf8Str filename = authLibrary;
13771
13772 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13773 if (RT_FAILURE(vrc))
13774 hr = setErrorBoth(E_FAIL, vrc,
13775 tr("Could not load the external authentication library '%s' (%Rrc)"),
13776 filename.c_str(), vrc);
13777 }
13778
13779 /* The auth library might need the machine lock. */
13780 alock.release();
13781
13782 if (FAILED(hr))
13783 return hr;
13784
13785 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13786 {
13787 enum VRDEAuthParams
13788 {
13789 parmUuid = 1,
13790 parmGuestJudgement,
13791 parmUser,
13792 parmPassword,
13793 parmDomain,
13794 parmClientId
13795 };
13796
13797 AuthResult result = AuthResultAccessDenied;
13798
13799 Guid uuid(aAuthParams[parmUuid]);
13800 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13801 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13802
13803 result = AuthLibAuthenticate(&mAuthLibCtx,
13804 uuid.raw(), guestJudgement,
13805 aAuthParams[parmUser].c_str(),
13806 aAuthParams[parmPassword].c_str(),
13807 aAuthParams[parmDomain].c_str(),
13808 u32ClientId);
13809
13810 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13811 size_t cbPassword = aAuthParams[parmPassword].length();
13812 if (cbPassword)
13813 {
13814 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13815 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13816 }
13817
13818 if (result == AuthResultAccessGranted)
13819 aResult = "granted";
13820 else
13821 aResult = "denied";
13822
13823 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13824 aAuthParams[parmUser].c_str(), aResult.c_str()));
13825 }
13826 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13827 {
13828 enum VRDEAuthDisconnectParams
13829 {
13830 parmUuid = 1,
13831 parmClientId
13832 };
13833
13834 Guid uuid(aAuthParams[parmUuid]);
13835 uint32_t u32ClientId = 0;
13836 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13837 }
13838 else
13839 {
13840 hr = E_INVALIDARG;
13841 }
13842
13843 return hr;
13844}
13845
13846// public methods only for internal purposes
13847/////////////////////////////////////////////////////////////////////////////
13848
13849#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13850/**
13851 * Called from the client watcher thread to check for expected or unexpected
13852 * death of the client process that has a direct session to this machine.
13853 *
13854 * On Win32 and on OS/2, this method is called only when we've got the
13855 * mutex (i.e. the client has either died or terminated normally) so it always
13856 * returns @c true (the client is terminated, the session machine is
13857 * uninitialized).
13858 *
13859 * On other platforms, the method returns @c true if the client process has
13860 * terminated normally or abnormally and the session machine was uninitialized,
13861 * and @c false if the client process is still alive.
13862 *
13863 * @note Locks this object for writing.
13864 */
13865bool SessionMachine::i_checkForDeath()
13866{
13867 Uninit::Reason reason;
13868 bool terminated = false;
13869
13870 /* Enclose autoCaller with a block because calling uninit() from under it
13871 * will deadlock. */
13872 {
13873 AutoCaller autoCaller(this);
13874 if (!autoCaller.isOk())
13875 {
13876 /* return true if not ready, to cause the client watcher to exclude
13877 * the corresponding session from watching */
13878 LogFlowThisFunc(("Already uninitialized!\n"));
13879 return true;
13880 }
13881
13882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13883
13884 /* Determine the reason of death: if the session state is Closing here,
13885 * everything is fine. Otherwise it means that the client did not call
13886 * OnSessionEnd() before it released the IPC semaphore. This may happen
13887 * either because the client process has abnormally terminated, or
13888 * because it simply forgot to call ISession::Close() before exiting. We
13889 * threat the latter also as an abnormal termination (see
13890 * Session::uninit() for details). */
13891 reason = mData->mSession.mState == SessionState_Unlocking ?
13892 Uninit::Normal :
13893 Uninit::Abnormal;
13894
13895 if (mClientToken)
13896 terminated = mClientToken->release();
13897 } /* AutoCaller block */
13898
13899 if (terminated)
13900 uninit(reason);
13901
13902 return terminated;
13903}
13904
13905void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13906{
13907 LogFlowThisFunc(("\n"));
13908
13909 strTokenId.setNull();
13910
13911 AutoCaller autoCaller(this);
13912 AssertComRCReturnVoid(autoCaller.rc());
13913
13914 Assert(mClientToken);
13915 if (mClientToken)
13916 mClientToken->getId(strTokenId);
13917}
13918#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13919IToken *SessionMachine::i_getToken()
13920{
13921 LogFlowThisFunc(("\n"));
13922
13923 AutoCaller autoCaller(this);
13924 AssertComRCReturn(autoCaller.rc(), NULL);
13925
13926 Assert(mClientToken);
13927 if (mClientToken)
13928 return mClientToken->getToken();
13929 else
13930 return NULL;
13931}
13932#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13933
13934Machine::ClientToken *SessionMachine::i_getClientToken()
13935{
13936 LogFlowThisFunc(("\n"));
13937
13938 AutoCaller autoCaller(this);
13939 AssertComRCReturn(autoCaller.rc(), NULL);
13940
13941 return mClientToken;
13942}
13943
13944
13945/**
13946 * @note Locks this object for reading.
13947 */
13948HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 AutoCaller autoCaller(this);
13953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13954
13955 ComPtr<IInternalSessionControl> directControl;
13956 {
13957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13958 if (mData->mSession.mLockType == LockType_VM)
13959 directControl = mData->mSession.mDirectControl;
13960 }
13961
13962 /* ignore notifications sent after #OnSessionEnd() is called */
13963 if (!directControl)
13964 return S_OK;
13965
13966 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13967}
13968
13969/**
13970 * @note Locks this object for reading.
13971 */
13972HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13973 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13974 IN_BSTR aGuestIp, LONG aGuestPort)
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 if (mData->mSession.mLockType == LockType_VM)
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991 /*
13992 * instead acting like callback we ask IVirtualBox deliver corresponding event
13993 */
13994
13995 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13996 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13997 return S_OK;
13998}
13999
14000/**
14001 * @note Locks this object for reading.
14002 */
14003HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14004{
14005 LogFlowThisFunc(("\n"));
14006
14007 AutoCaller autoCaller(this);
14008 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14009
14010 ComPtr<IInternalSessionControl> directControl;
14011 {
14012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14013 if (mData->mSession.mLockType == LockType_VM)
14014 directControl = mData->mSession.mDirectControl;
14015 }
14016
14017 /* ignore notifications sent after #OnSessionEnd() is called */
14018 if (!directControl)
14019 return S_OK;
14020
14021 return directControl->OnAudioAdapterChange(audioAdapter);
14022}
14023
14024/**
14025 * @note Locks this object for reading.
14026 */
14027HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14028{
14029 LogFlowThisFunc(("\n"));
14030
14031 AutoCaller autoCaller(this);
14032 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14033
14034 ComPtr<IInternalSessionControl> directControl;
14035 {
14036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14037 if (mData->mSession.mLockType == LockType_VM)
14038 directControl = mData->mSession.mDirectControl;
14039 }
14040
14041 /* ignore notifications sent after #OnSessionEnd() is called */
14042 if (!directControl)
14043 return S_OK;
14044
14045 return directControl->OnSerialPortChange(serialPort);
14046}
14047
14048/**
14049 * @note Locks this object for reading.
14050 */
14051HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14052{
14053 LogFlowThisFunc(("\n"));
14054
14055 AutoCaller autoCaller(this);
14056 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14057
14058 ComPtr<IInternalSessionControl> directControl;
14059 {
14060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14061 if (mData->mSession.mLockType == LockType_VM)
14062 directControl = mData->mSession.mDirectControl;
14063 }
14064
14065 /* ignore notifications sent after #OnSessionEnd() is called */
14066 if (!directControl)
14067 return S_OK;
14068
14069 return directControl->OnParallelPortChange(parallelPort);
14070}
14071
14072/**
14073 * @note Locks this object for reading.
14074 */
14075HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14076{
14077 LogFlowThisFunc(("\n"));
14078
14079 AutoCaller autoCaller(this);
14080 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14081
14082 ComPtr<IInternalSessionControl> directControl;
14083 {
14084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14085 if (mData->mSession.mLockType == LockType_VM)
14086 directControl = mData->mSession.mDirectControl;
14087 }
14088
14089 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14090
14091 /* ignore notifications sent after #OnSessionEnd() is called */
14092 if (!directControl)
14093 return S_OK;
14094
14095 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14096}
14097
14098/**
14099 * @note Locks this object for reading.
14100 */
14101HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14102{
14103 LogFlowThisFunc(("\n"));
14104
14105 AutoCaller autoCaller(this);
14106 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14107
14108 ComPtr<IInternalSessionControl> directControl;
14109 {
14110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14111 if (mData->mSession.mLockType == LockType_VM)
14112 directControl = mData->mSession.mDirectControl;
14113 }
14114
14115 mParent->i_onMediumChanged(aAttachment);
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120
14121 return directControl->OnMediumChange(aAttachment, aForce);
14122}
14123
14124HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14125{
14126 LogFlowThisFunc(("\n"));
14127
14128 AutoCaller autoCaller(this);
14129 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14130
14131 ComPtr<IInternalSessionControl> directControl;
14132 {
14133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14134 if (mData->mSession.mLockType == LockType_VM)
14135 directControl = mData->mSession.mDirectControl;
14136 }
14137
14138 /* ignore notifications sent after #OnSessionEnd() is called */
14139 if (!directControl)
14140 return S_OK;
14141
14142 return directControl->OnVMProcessPriorityChange(aPriority);
14143}
14144
14145/**
14146 * @note Locks this object for reading.
14147 */
14148HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14149{
14150 LogFlowThisFunc(("\n"));
14151
14152 AutoCaller autoCaller(this);
14153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14154
14155 ComPtr<IInternalSessionControl> directControl;
14156 {
14157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14158 if (mData->mSession.mLockType == LockType_VM)
14159 directControl = mData->mSession.mDirectControl;
14160 }
14161
14162 /* ignore notifications sent after #OnSessionEnd() is called */
14163 if (!directControl)
14164 return S_OK;
14165
14166 return directControl->OnCPUChange(aCPU, aRemove);
14167}
14168
14169HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14170{
14171 LogFlowThisFunc(("\n"));
14172
14173 AutoCaller autoCaller(this);
14174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14175
14176 ComPtr<IInternalSessionControl> directControl;
14177 {
14178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14179 if (mData->mSession.mLockType == LockType_VM)
14180 directControl = mData->mSession.mDirectControl;
14181 }
14182
14183 /* ignore notifications sent after #OnSessionEnd() is called */
14184 if (!directControl)
14185 return S_OK;
14186
14187 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14188}
14189
14190/**
14191 * @note Locks this object for reading.
14192 */
14193HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14194{
14195 LogFlowThisFunc(("\n"));
14196
14197 AutoCaller autoCaller(this);
14198 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14199
14200 ComPtr<IInternalSessionControl> directControl;
14201 {
14202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14203 if (mData->mSession.mLockType == LockType_VM)
14204 directControl = mData->mSession.mDirectControl;
14205 }
14206
14207 /* ignore notifications sent after #OnSessionEnd() is called */
14208 if (!directControl)
14209 return S_OK;
14210
14211 return directControl->OnVRDEServerChange(aRestart);
14212}
14213
14214/**
14215 * @note Locks this object for reading.
14216 */
14217HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14218{
14219 LogFlowThisFunc(("\n"));
14220
14221 AutoCaller autoCaller(this);
14222 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14223
14224 ComPtr<IInternalSessionControl> directControl;
14225 {
14226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14227 if (mData->mSession.mLockType == LockType_VM)
14228 directControl = mData->mSession.mDirectControl;
14229 }
14230
14231 /* ignore notifications sent after #OnSessionEnd() is called */
14232 if (!directControl)
14233 return S_OK;
14234
14235 return directControl->OnRecordingChange(aEnable);
14236}
14237
14238/**
14239 * @note Locks this object for reading.
14240 */
14241HRESULT SessionMachine::i_onUSBControllerChange()
14242{
14243 LogFlowThisFunc(("\n"));
14244
14245 AutoCaller autoCaller(this);
14246 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14247
14248 ComPtr<IInternalSessionControl> directControl;
14249 {
14250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14251 if (mData->mSession.mLockType == LockType_VM)
14252 directControl = mData->mSession.mDirectControl;
14253 }
14254
14255 /* ignore notifications sent after #OnSessionEnd() is called */
14256 if (!directControl)
14257 return S_OK;
14258
14259 return directControl->OnUSBControllerChange();
14260}
14261
14262/**
14263 * @note Locks this object for reading.
14264 */
14265HRESULT SessionMachine::i_onSharedFolderChange()
14266{
14267 LogFlowThisFunc(("\n"));
14268
14269 AutoCaller autoCaller(this);
14270 AssertComRCReturnRC(autoCaller.rc());
14271
14272 ComPtr<IInternalSessionControl> directControl;
14273 {
14274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14275 if (mData->mSession.mLockType == LockType_VM)
14276 directControl = mData->mSession.mDirectControl;
14277 }
14278
14279 /* ignore notifications sent after #OnSessionEnd() is called */
14280 if (!directControl)
14281 return S_OK;
14282
14283 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14284}
14285
14286/**
14287 * @note Locks this object for reading.
14288 */
14289HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14290{
14291 LogFlowThisFunc(("\n"));
14292
14293 AutoCaller autoCaller(this);
14294 AssertComRCReturnRC(autoCaller.rc());
14295
14296 ComPtr<IInternalSessionControl> directControl;
14297 {
14298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14299 if (mData->mSession.mLockType == LockType_VM)
14300 directControl = mData->mSession.mDirectControl;
14301 }
14302
14303 /* ignore notifications sent after #OnSessionEnd() is called */
14304 if (!directControl)
14305 return S_OK;
14306
14307 return directControl->OnClipboardModeChange(aClipboardMode);
14308}
14309
14310/**
14311 * @note Locks this object for reading.
14312 */
14313HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14314{
14315 LogFlowThisFunc(("\n"));
14316
14317 AutoCaller autoCaller(this);
14318 AssertComRCReturnRC(autoCaller.rc());
14319
14320 ComPtr<IInternalSessionControl> directControl;
14321 {
14322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14323 if (mData->mSession.mLockType == LockType_VM)
14324 directControl = mData->mSession.mDirectControl;
14325 }
14326
14327 /* ignore notifications sent after #OnSessionEnd() is called */
14328 if (!directControl)
14329 return S_OK;
14330
14331 return directControl->OnClipboardFileTransferModeChange(aEnable);
14332}
14333
14334/**
14335 * @note Locks this object for reading.
14336 */
14337HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14338{
14339 LogFlowThisFunc(("\n"));
14340
14341 AutoCaller autoCaller(this);
14342 AssertComRCReturnRC(autoCaller.rc());
14343
14344 ComPtr<IInternalSessionControl> directControl;
14345 {
14346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14347 if (mData->mSession.mLockType == LockType_VM)
14348 directControl = mData->mSession.mDirectControl;
14349 }
14350
14351 /* ignore notifications sent after #OnSessionEnd() is called */
14352 if (!directControl)
14353 return S_OK;
14354
14355 return directControl->OnDnDModeChange(aDnDMode);
14356}
14357
14358/**
14359 * @note Locks this object for reading.
14360 */
14361HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14362{
14363 LogFlowThisFunc(("\n"));
14364
14365 AutoCaller autoCaller(this);
14366 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14367
14368 ComPtr<IInternalSessionControl> directControl;
14369 {
14370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14371 if (mData->mSession.mLockType == LockType_VM)
14372 directControl = mData->mSession.mDirectControl;
14373 }
14374
14375 /* ignore notifications sent after #OnSessionEnd() is called */
14376 if (!directControl)
14377 return S_OK;
14378
14379 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14380}
14381
14382/**
14383 * @note Locks this object for reading.
14384 */
14385HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14386{
14387 LogFlowThisFunc(("\n"));
14388
14389 AutoCaller autoCaller(this);
14390 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14391
14392 ComPtr<IInternalSessionControl> directControl;
14393 {
14394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14395 if (mData->mSession.mLockType == LockType_VM)
14396 directControl = mData->mSession.mDirectControl;
14397 }
14398
14399 /* ignore notifications sent after #OnSessionEnd() is called */
14400 if (!directControl)
14401 return S_OK;
14402
14403 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14404}
14405
14406/**
14407 * Returns @c true if this machine's USB controller reports it has a matching
14408 * filter for the given USB device and @c false otherwise.
14409 *
14410 * @note locks this object for reading.
14411 */
14412bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14413{
14414 AutoCaller autoCaller(this);
14415 /* silently return if not ready -- this method may be called after the
14416 * direct machine session has been called */
14417 if (!autoCaller.isOk())
14418 return false;
14419
14420#ifdef VBOX_WITH_USB
14421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14422
14423 switch (mData->mMachineState)
14424 {
14425 case MachineState_Starting:
14426 case MachineState_Restoring:
14427 case MachineState_TeleportingIn:
14428 case MachineState_Paused:
14429 case MachineState_Running:
14430 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14431 * elsewhere... */
14432 alock.release();
14433 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14434 default: break;
14435 }
14436#else
14437 NOREF(aDevice);
14438 NOREF(aMaskedIfs);
14439#endif
14440 return false;
14441}
14442
14443/**
14444 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14445 */
14446HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14447 IVirtualBoxErrorInfo *aError,
14448 ULONG aMaskedIfs,
14449 const com::Utf8Str &aCaptureFilename)
14450{
14451 LogFlowThisFunc(("\n"));
14452
14453 AutoCaller autoCaller(this);
14454
14455 /* This notification may happen after the machine object has been
14456 * uninitialized (the session was closed), so don't assert. */
14457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14458
14459 ComPtr<IInternalSessionControl> directControl;
14460 {
14461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14462 if (mData->mSession.mLockType == LockType_VM)
14463 directControl = mData->mSession.mDirectControl;
14464 }
14465
14466 /* fail on notifications sent after #OnSessionEnd() is called, it is
14467 * expected by the caller */
14468 if (!directControl)
14469 return E_FAIL;
14470
14471 /* No locks should be held at this point. */
14472 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14473 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14474
14475 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14476}
14477
14478/**
14479 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14480 */
14481HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14482 IVirtualBoxErrorInfo *aError)
14483{
14484 LogFlowThisFunc(("\n"));
14485
14486 AutoCaller autoCaller(this);
14487
14488 /* This notification may happen after the machine object has been
14489 * uninitialized (the session was closed), so don't assert. */
14490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14491
14492 ComPtr<IInternalSessionControl> directControl;
14493 {
14494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14495 if (mData->mSession.mLockType == LockType_VM)
14496 directControl = mData->mSession.mDirectControl;
14497 }
14498
14499 /* fail on notifications sent after #OnSessionEnd() is called, it is
14500 * expected by the caller */
14501 if (!directControl)
14502 return E_FAIL;
14503
14504 /* No locks should be held at this point. */
14505 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14506 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14507
14508 return directControl->OnUSBDeviceDetach(aId, aError);
14509}
14510
14511// protected methods
14512/////////////////////////////////////////////////////////////////////////////
14513
14514/**
14515 * Deletes the given file if it is no longer in use by either the current machine state
14516 * (if the machine is "saved") or any of the machine's snapshots.
14517 *
14518 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14519 * but is different for each SnapshotMachine. When calling this, the order of calling this
14520 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14521 * is therefore critical. I know, it's all rather messy.
14522 *
14523 * @param strStateFile
14524 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14525 * the test for whether the saved state file is in use.
14526 */
14527void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14528 Snapshot *pSnapshotToIgnore)
14529{
14530 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14531 if ( (strStateFile.isNotEmpty())
14532 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14533 )
14534 // ... and it must also not be shared with other snapshots
14535 if ( !mData->mFirstSnapshot
14536 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14537 // this checks the SnapshotMachine's state file paths
14538 )
14539 RTFileDelete(strStateFile.c_str());
14540}
14541
14542/**
14543 * Locks the attached media.
14544 *
14545 * All attached hard disks are locked for writing and DVD/floppy are locked for
14546 * reading. Parents of attached hard disks (if any) are locked for reading.
14547 *
14548 * This method also performs accessibility check of all media it locks: if some
14549 * media is inaccessible, the method will return a failure and a bunch of
14550 * extended error info objects per each inaccessible medium.
14551 *
14552 * Note that this method is atomic: if it returns a success, all media are
14553 * locked as described above; on failure no media is locked at all (all
14554 * succeeded individual locks will be undone).
14555 *
14556 * The caller is responsible for doing the necessary state sanity checks.
14557 *
14558 * The locks made by this method must be undone by calling #unlockMedia() when
14559 * no more needed.
14560 */
14561HRESULT SessionMachine::i_lockMedia()
14562{
14563 AutoCaller autoCaller(this);
14564 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14565
14566 AutoMultiWriteLock2 alock(this->lockHandle(),
14567 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14568
14569 /* bail out if trying to lock things with already set up locking */
14570 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14571
14572 MultiResult mrc(S_OK);
14573
14574 /* Collect locking information for all medium objects attached to the VM. */
14575 for (MediumAttachmentList::const_iterator
14576 it = mMediumAttachments->begin();
14577 it != mMediumAttachments->end();
14578 ++it)
14579 {
14580 MediumAttachment *pAtt = *it;
14581 DeviceType_T devType = pAtt->i_getType();
14582 Medium *pMedium = pAtt->i_getMedium();
14583
14584 MediumLockList *pMediumLockList(new MediumLockList());
14585 // There can be attachments without a medium (floppy/dvd), and thus
14586 // it's impossible to create a medium lock list. It still makes sense
14587 // to have the empty medium lock list in the map in case a medium is
14588 // attached later.
14589 if (pMedium != NULL)
14590 {
14591 MediumType_T mediumType = pMedium->i_getType();
14592 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14593 || mediumType == MediumType_Shareable;
14594 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14595
14596 alock.release();
14597 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14598 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14599 false /* fMediumLockWriteAll */,
14600 NULL,
14601 *pMediumLockList);
14602 alock.acquire();
14603 if (FAILED(mrc))
14604 {
14605 delete pMediumLockList;
14606 mData->mSession.mLockedMedia.Clear();
14607 break;
14608 }
14609 }
14610
14611 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14612 if (FAILED(rc))
14613 {
14614 mData->mSession.mLockedMedia.Clear();
14615 mrc = setError(rc,
14616 tr("Collecting locking information for all attached media failed"));
14617 break;
14618 }
14619 }
14620
14621 if (SUCCEEDED(mrc))
14622 {
14623 /* Now lock all media. If this fails, nothing is locked. */
14624 alock.release();
14625 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14626 alock.acquire();
14627 if (FAILED(rc))
14628 {
14629 mrc = setError(rc,
14630 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14631 }
14632 }
14633
14634 return mrc;
14635}
14636
14637/**
14638 * Undoes the locks made by by #lockMedia().
14639 */
14640HRESULT SessionMachine::i_unlockMedia()
14641{
14642 AutoCaller autoCaller(this);
14643 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14644
14645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14646
14647 /* we may be holding important error info on the current thread;
14648 * preserve it */
14649 ErrorInfoKeeper eik;
14650
14651 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14652 AssertComRC(rc);
14653 return rc;
14654}
14655
14656/**
14657 * Helper to change the machine state (reimplementation).
14658 *
14659 * @note Locks this object for writing.
14660 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14661 * it can cause crashes in random places due to unexpectedly committing
14662 * the current settings. The caller is responsible for that. The call
14663 * to saveStateSettings is fine, because this method does not commit.
14664 */
14665HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14666{
14667 LogFlowThisFuncEnter();
14668 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14669
14670 AutoCaller autoCaller(this);
14671 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14672
14673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14674
14675 MachineState_T oldMachineState = mData->mMachineState;
14676
14677 AssertMsgReturn(oldMachineState != aMachineState,
14678 ("oldMachineState=%s, aMachineState=%s\n",
14679 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14680 E_FAIL);
14681
14682 HRESULT rc = S_OK;
14683
14684 int stsFlags = 0;
14685 bool deleteSavedState = false;
14686
14687 /* detect some state transitions */
14688
14689 if ( ( oldMachineState == MachineState_Saved
14690 && aMachineState == MachineState_Restoring)
14691 || ( ( oldMachineState == MachineState_PoweredOff
14692 || oldMachineState == MachineState_Teleported
14693 || oldMachineState == MachineState_Aborted
14694 )
14695 && ( aMachineState == MachineState_TeleportingIn
14696 || aMachineState == MachineState_Starting
14697 )
14698 )
14699 )
14700 {
14701 /* The EMT thread is about to start */
14702
14703 /* Nothing to do here for now... */
14704
14705 /// @todo NEWMEDIA don't let mDVDDrive and other children
14706 /// change anything when in the Starting/Restoring state
14707 }
14708 else if ( ( oldMachineState == MachineState_Running
14709 || oldMachineState == MachineState_Paused
14710 || oldMachineState == MachineState_Teleporting
14711 || oldMachineState == MachineState_OnlineSnapshotting
14712 || oldMachineState == MachineState_LiveSnapshotting
14713 || oldMachineState == MachineState_Stuck
14714 || oldMachineState == MachineState_Starting
14715 || oldMachineState == MachineState_Stopping
14716 || oldMachineState == MachineState_Saving
14717 || oldMachineState == MachineState_Restoring
14718 || oldMachineState == MachineState_TeleportingPausedVM
14719 || oldMachineState == MachineState_TeleportingIn
14720 )
14721 && ( aMachineState == MachineState_PoweredOff
14722 || aMachineState == MachineState_Saved
14723 || aMachineState == MachineState_Teleported
14724 || aMachineState == MachineState_Aborted
14725 )
14726 )
14727 {
14728 /* The EMT thread has just stopped, unlock attached media. Note that as
14729 * opposed to locking that is done from Console, we do unlocking here
14730 * because the VM process may have aborted before having a chance to
14731 * properly unlock all media it locked. */
14732
14733 unlockMedia();
14734 }
14735
14736 if (oldMachineState == MachineState_Restoring)
14737 {
14738 if (aMachineState != MachineState_Saved)
14739 {
14740 /*
14741 * delete the saved state file once the machine has finished
14742 * restoring from it (note that Console sets the state from
14743 * Restoring to Saved if the VM couldn't restore successfully,
14744 * to give the user an ability to fix an error and retry --
14745 * we keep the saved state file in this case)
14746 */
14747 deleteSavedState = true;
14748 }
14749 }
14750 else if ( oldMachineState == MachineState_Saved
14751 && ( aMachineState == MachineState_PoweredOff
14752 || aMachineState == MachineState_Aborted
14753 || aMachineState == MachineState_Teleported
14754 )
14755 )
14756 {
14757 /*
14758 * delete the saved state after SessionMachine::ForgetSavedState() is called
14759 * or if the VM process (owning a direct VM session) crashed while the
14760 * VM was Saved
14761 */
14762
14763 /// @todo (dmik)
14764 // Not sure that deleting the saved state file just because of the
14765 // client death before it attempted to restore the VM is a good
14766 // thing. But when it crashes we need to go to the Aborted state
14767 // which cannot have the saved state file associated... The only
14768 // way to fix this is to make the Aborted condition not a VM state
14769 // but a bool flag: i.e., when a crash occurs, set it to true and
14770 // change the state to PoweredOff or Saved depending on the
14771 // saved state presence.
14772
14773 deleteSavedState = true;
14774 mData->mCurrentStateModified = TRUE;
14775 stsFlags |= SaveSTS_CurStateModified;
14776 }
14777
14778 if ( aMachineState == MachineState_Starting
14779 || aMachineState == MachineState_Restoring
14780 || aMachineState == MachineState_TeleportingIn
14781 )
14782 {
14783 /* set the current state modified flag to indicate that the current
14784 * state is no more identical to the state in the
14785 * current snapshot */
14786 if (!mData->mCurrentSnapshot.isNull())
14787 {
14788 mData->mCurrentStateModified = TRUE;
14789 stsFlags |= SaveSTS_CurStateModified;
14790 }
14791 }
14792
14793 if (deleteSavedState)
14794 {
14795 if (mRemoveSavedState)
14796 {
14797 Assert(!mSSData->strStateFilePath.isEmpty());
14798
14799 // it is safe to delete the saved state file if ...
14800 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14801 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14802 // ... none of the snapshots share the saved state file
14803 )
14804 RTFileDelete(mSSData->strStateFilePath.c_str());
14805 }
14806
14807 mSSData->strStateFilePath.setNull();
14808 stsFlags |= SaveSTS_StateFilePath;
14809 }
14810
14811 /* redirect to the underlying peer machine */
14812 mPeer->i_setMachineState(aMachineState);
14813
14814 if ( oldMachineState != MachineState_RestoringSnapshot
14815 && ( aMachineState == MachineState_PoweredOff
14816 || aMachineState == MachineState_Teleported
14817 || aMachineState == MachineState_Aborted
14818 || aMachineState == MachineState_Saved))
14819 {
14820 /* the machine has stopped execution
14821 * (or the saved state file was adopted) */
14822 stsFlags |= SaveSTS_StateTimeStamp;
14823 }
14824
14825 if ( ( oldMachineState == MachineState_PoweredOff
14826 || oldMachineState == MachineState_Aborted
14827 || oldMachineState == MachineState_Teleported
14828 )
14829 && aMachineState == MachineState_Saved)
14830 {
14831 /* the saved state file was adopted */
14832 Assert(!mSSData->strStateFilePath.isEmpty());
14833 stsFlags |= SaveSTS_StateFilePath;
14834 }
14835
14836#ifdef VBOX_WITH_GUEST_PROPS
14837 if ( aMachineState == MachineState_PoweredOff
14838 || aMachineState == MachineState_Aborted
14839 || aMachineState == MachineState_Teleported)
14840 {
14841 /* Make sure any transient guest properties get removed from the
14842 * property store on shutdown. */
14843 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14844
14845 /* remove it from the settings representation */
14846 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14847 for (settings::GuestPropertiesList::iterator
14848 it = llGuestProperties.begin();
14849 it != llGuestProperties.end();
14850 /*nothing*/)
14851 {
14852 const settings::GuestProperty &prop = *it;
14853 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14854 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14855 {
14856 it = llGuestProperties.erase(it);
14857 fNeedsSaving = true;
14858 }
14859 else
14860 {
14861 ++it;
14862 }
14863 }
14864
14865 /* Additionally remove it from the HWData representation. Required to
14866 * keep everything in sync, as this is what the API keeps using. */
14867 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14868 for (HWData::GuestPropertyMap::iterator
14869 it = llHWGuestProperties.begin();
14870 it != llHWGuestProperties.end();
14871 /*nothing*/)
14872 {
14873 uint32_t fFlags = it->second.mFlags;
14874 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14875 {
14876 /* iterator where we need to continue after the erase call
14877 * (C++03 is a fact still, and it doesn't return the iterator
14878 * which would allow continuing) */
14879 HWData::GuestPropertyMap::iterator it2 = it;
14880 ++it2;
14881 llHWGuestProperties.erase(it);
14882 it = it2;
14883 fNeedsSaving = true;
14884 }
14885 else
14886 {
14887 ++it;
14888 }
14889 }
14890
14891 if (fNeedsSaving)
14892 {
14893 mData->mCurrentStateModified = TRUE;
14894 stsFlags |= SaveSTS_CurStateModified;
14895 }
14896 }
14897#endif /* VBOX_WITH_GUEST_PROPS */
14898
14899 rc = i_saveStateSettings(stsFlags);
14900
14901 if ( ( oldMachineState != MachineState_PoweredOff
14902 && oldMachineState != MachineState_Aborted
14903 && oldMachineState != MachineState_Teleported
14904 )
14905 && ( aMachineState == MachineState_PoweredOff
14906 || aMachineState == MachineState_Aborted
14907 || aMachineState == MachineState_Teleported
14908 )
14909 )
14910 {
14911 /* we've been shut down for any reason */
14912 /* no special action so far */
14913 }
14914
14915 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14916 LogFlowThisFuncLeave();
14917 return rc;
14918}
14919
14920/**
14921 * Sends the current machine state value to the VM process.
14922 *
14923 * @note Locks this object for reading, then calls a client process.
14924 */
14925HRESULT SessionMachine::i_updateMachineStateOnClient()
14926{
14927 AutoCaller autoCaller(this);
14928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14929
14930 ComPtr<IInternalSessionControl> directControl;
14931 {
14932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14933 AssertReturn(!!mData, E_FAIL);
14934 if (mData->mSession.mLockType == LockType_VM)
14935 directControl = mData->mSession.mDirectControl;
14936
14937 /* directControl may be already set to NULL here in #OnSessionEnd()
14938 * called too early by the direct session process while there is still
14939 * some operation (like deleting the snapshot) in progress. The client
14940 * process in this case is waiting inside Session::close() for the
14941 * "end session" process object to complete, while #uninit() called by
14942 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14943 * operation to complete. For now, we accept this inconsistent behavior
14944 * and simply do nothing here. */
14945
14946 if (mData->mSession.mState == SessionState_Unlocking)
14947 return S_OK;
14948 }
14949
14950 /* ignore notifications sent after #OnSessionEnd() is called */
14951 if (!directControl)
14952 return S_OK;
14953
14954 return directControl->UpdateMachineState(mData->mMachineState);
14955}
14956
14957
14958/*static*/
14959HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14960{
14961 va_list args;
14962 va_start(args, pcszMsg);
14963 HRESULT rc = setErrorInternal(aResultCode,
14964 getStaticClassIID(),
14965 getStaticComponentName(),
14966 Utf8Str(pcszMsg, args),
14967 false /* aWarning */,
14968 true /* aLogIt */);
14969 va_end(args);
14970 return rc;
14971}
14972
14973
14974HRESULT Machine::updateState(MachineState_T aState)
14975{
14976 NOREF(aState);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14981{
14982 NOREF(aProgress);
14983 ReturnComNotImplemented();
14984}
14985
14986HRESULT Machine::endPowerUp(LONG aResult)
14987{
14988 NOREF(aResult);
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14993{
14994 NOREF(aProgress);
14995 ReturnComNotImplemented();
14996}
14997
14998HRESULT Machine::endPoweringDown(LONG aResult,
14999 const com::Utf8Str &aErrMsg)
15000{
15001 NOREF(aResult);
15002 NOREF(aErrMsg);
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15007 BOOL *aMatched,
15008 ULONG *aMaskedInterfaces)
15009{
15010 NOREF(aDevice);
15011 NOREF(aMatched);
15012 NOREF(aMaskedInterfaces);
15013 ReturnComNotImplemented();
15014
15015}
15016
15017HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15018{
15019 NOREF(aId); NOREF(aCaptureFilename);
15020 ReturnComNotImplemented();
15021}
15022
15023HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15024 BOOL aDone)
15025{
15026 NOREF(aId);
15027 NOREF(aDone);
15028 ReturnComNotImplemented();
15029}
15030
15031HRESULT Machine::autoCaptureUSBDevices()
15032{
15033 ReturnComNotImplemented();
15034}
15035
15036HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15037{
15038 NOREF(aDone);
15039 ReturnComNotImplemented();
15040}
15041
15042HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15043 ComPtr<IProgress> &aProgress)
15044{
15045 NOREF(aSession);
15046 NOREF(aProgress);
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::finishOnlineMergeMedium()
15051{
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
15056{
15057 RT_NOREF(aParms, aID);
15058 ReturnComNotImplemented();
15059}
15060
15061HRESULT Machine::clipboardAreaUnregister(ULONG aID)
15062{
15063 RT_NOREF(aID);
15064 ReturnComNotImplemented();
15065}
15066
15067HRESULT Machine::clipboardAreaAttach(ULONG aID)
15068{
15069 RT_NOREF(aID);
15070 ReturnComNotImplemented();
15071}
15072HRESULT Machine::clipboardAreaDetach(ULONG aID)
15073{
15074 RT_NOREF(aID);
15075 ReturnComNotImplemented();
15076}
15077
15078HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
15079{
15080 RT_NOREF(aID);
15081 ReturnComNotImplemented();
15082}
15083
15084HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
15085{
15086 RT_NOREF(aID, aRefCount);
15087 ReturnComNotImplemented();
15088}
15089
15090HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15091 std::vector<com::Utf8Str> &aValues,
15092 std::vector<LONG64> &aTimestamps,
15093 std::vector<com::Utf8Str> &aFlags)
15094{
15095 NOREF(aNames);
15096 NOREF(aValues);
15097 NOREF(aTimestamps);
15098 NOREF(aFlags);
15099 ReturnComNotImplemented();
15100}
15101
15102HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15103 const com::Utf8Str &aValue,
15104 LONG64 aTimestamp,
15105 const com::Utf8Str &aFlags)
15106{
15107 NOREF(aName);
15108 NOREF(aValue);
15109 NOREF(aTimestamp);
15110 NOREF(aFlags);
15111 ReturnComNotImplemented();
15112}
15113
15114HRESULT Machine::lockMedia()
15115{
15116 ReturnComNotImplemented();
15117}
15118
15119HRESULT Machine::unlockMedia()
15120{
15121 ReturnComNotImplemented();
15122}
15123
15124HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15125 ComPtr<IMediumAttachment> &aNewAttachment)
15126{
15127 NOREF(aAttachment);
15128 NOREF(aNewAttachment);
15129 ReturnComNotImplemented();
15130}
15131
15132HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15133 ULONG aCpuUser,
15134 ULONG aCpuKernel,
15135 ULONG aCpuIdle,
15136 ULONG aMemTotal,
15137 ULONG aMemFree,
15138 ULONG aMemBalloon,
15139 ULONG aMemShared,
15140 ULONG aMemCache,
15141 ULONG aPagedTotal,
15142 ULONG aMemAllocTotal,
15143 ULONG aMemFreeTotal,
15144 ULONG aMemBalloonTotal,
15145 ULONG aMemSharedTotal,
15146 ULONG aVmNetRx,
15147 ULONG aVmNetTx)
15148{
15149 NOREF(aValidStats);
15150 NOREF(aCpuUser);
15151 NOREF(aCpuKernel);
15152 NOREF(aCpuIdle);
15153 NOREF(aMemTotal);
15154 NOREF(aMemFree);
15155 NOREF(aMemBalloon);
15156 NOREF(aMemShared);
15157 NOREF(aMemCache);
15158 NOREF(aPagedTotal);
15159 NOREF(aMemAllocTotal);
15160 NOREF(aMemFreeTotal);
15161 NOREF(aMemBalloonTotal);
15162 NOREF(aMemSharedTotal);
15163 NOREF(aVmNetRx);
15164 NOREF(aVmNetTx);
15165 ReturnComNotImplemented();
15166}
15167
15168HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15169 com::Utf8Str &aResult)
15170{
15171 NOREF(aAuthParams);
15172 NOREF(aResult);
15173 ReturnComNotImplemented();
15174}
15175
15176com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15177{
15178 com::Utf8Str strControllerName = "Unknown";
15179 switch (aBusType)
15180 {
15181 case StorageBus_IDE:
15182 {
15183 strControllerName = "IDE";
15184 break;
15185 }
15186 case StorageBus_SATA:
15187 {
15188 strControllerName = "SATA";
15189 break;
15190 }
15191 case StorageBus_SCSI:
15192 {
15193 strControllerName = "SCSI";
15194 break;
15195 }
15196 case StorageBus_Floppy:
15197 {
15198 strControllerName = "Floppy";
15199 break;
15200 }
15201 case StorageBus_SAS:
15202 {
15203 strControllerName = "SAS";
15204 break;
15205 }
15206 case StorageBus_USB:
15207 {
15208 strControllerName = "USB";
15209 break;
15210 }
15211 default:
15212 break;
15213 }
15214 return strControllerName;
15215}
15216
15217HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15218{
15219 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15220
15221 AutoCaller autoCaller(this);
15222 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15223
15224 HRESULT rc = S_OK;
15225
15226 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15227 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15228 rc = getUSBDeviceFilters(usbDeviceFilters);
15229 if (FAILED(rc)) return rc;
15230
15231 NOREF(aFlags);
15232 com::Utf8Str osTypeId;
15233 ComObjPtr<GuestOSType> osType = NULL;
15234
15235 /* Get the guest os type as a string from the VB. */
15236 rc = getOSTypeId(osTypeId);
15237 if (FAILED(rc)) return rc;
15238
15239 /* Get the os type obj that coresponds, can be used to get
15240 * the defaults for this guest OS. */
15241 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15242 if (FAILED(rc)) return rc;
15243
15244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15245
15246 /* Let the OS type select 64-bit ness. */
15247 mHWData->mLongMode = osType->i_is64Bit()
15248 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15249
15250 /* Let the OS type enable the X2APIC */
15251 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15252
15253 /* This one covers IOAPICEnabled. */
15254 mBIOSSettings->i_applyDefaults(osType);
15255
15256 /* Initialize default record settings. */
15257 mRecordingSettings->i_applyDefaults();
15258
15259 /* Initialize default BIOS settings here */
15260 /* Hardware virtualization must be ON by default */
15261 mHWData->mAPIC = true;
15262 mHWData->mHWVirtExEnabled = true;
15263
15264 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15265 if (FAILED(rc)) return rc;
15266
15267 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15268 if (FAILED(rc)) return rc;
15269
15270 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15271 if (FAILED(rc)) return rc;
15272
15273 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15274 if (FAILED(rc)) return rc;
15275
15276 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15277 if (FAILED(rc)) return rc;
15278
15279 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15280 if (FAILED(rc)) return rc;
15281
15282 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15283 if (FAILED(rc)) return rc;
15284
15285 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15286 if (FAILED(rc)) return rc;
15287
15288 BOOL mRTCUseUTC;
15289 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15290 if (FAILED(rc)) return rc;
15291
15292 setRTCUseUTC(mRTCUseUTC);
15293 if (FAILED(rc)) return rc;
15294
15295 /* the setter does more than just the assignment, so use it */
15296 ChipsetType_T enmChipsetType;
15297 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15298 if (FAILED(rc)) return rc;
15299
15300 rc = COMSETTER(ChipsetType)(enmChipsetType);
15301 if (FAILED(rc)) return rc;
15302
15303 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15304 if (FAILED(rc)) return rc;
15305
15306 /* Apply network adapters defaults */
15307 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15308 mNetworkAdapters[slot]->i_applyDefaults(osType);
15309
15310 /* Apply serial port defaults */
15311 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15312 mSerialPorts[slot]->i_applyDefaults(osType);
15313
15314 /* Apply parallel port defaults - not OS dependent*/
15315 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15316 mParallelPorts[slot]->i_applyDefaults();
15317
15318 /* Audio stuff. */
15319 AudioControllerType_T audioController;
15320 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15321 if (FAILED(rc)) return rc;
15322
15323 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15324 if (FAILED(rc)) return rc;
15325
15326 AudioCodecType_T audioCodec;
15327 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15328 if (FAILED(rc)) return rc;
15329
15330 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15331 if (FAILED(rc)) return rc;
15332
15333 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15334 if (FAILED(rc)) return rc;
15335
15336 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15337 if (FAILED(rc)) return rc;
15338
15339 /* Storage Controllers */
15340 StorageControllerType_T hdStorageControllerType;
15341 StorageBus_T hdStorageBusType;
15342 StorageControllerType_T dvdStorageControllerType;
15343 StorageBus_T dvdStorageBusType;
15344 BOOL recommendedFloppy;
15345 ComPtr<IStorageController> floppyController;
15346 ComPtr<IStorageController> hdController;
15347 ComPtr<IStorageController> dvdController;
15348 Utf8Str strFloppyName, strDVDName, strHDName;
15349
15350 /* GUI auto generates controller names using bus type. Do the same*/
15351 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15352
15353 /* Floppy recommended? add one. */
15354 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15355 if (FAILED(rc)) return rc;
15356 if (recommendedFloppy)
15357 {
15358 rc = addStorageController(strFloppyName,
15359 StorageBus_Floppy,
15360 floppyController);
15361 if (FAILED(rc)) return rc;
15362 }
15363
15364 /* Setup one DVD storage controller. */
15365 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15366 if (FAILED(rc)) return rc;
15367
15368 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15369 if (FAILED(rc)) return rc;
15370
15371 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15372
15373 rc = addStorageController(strDVDName,
15374 dvdStorageBusType,
15375 dvdController);
15376 if (FAILED(rc)) return rc;
15377
15378 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15379 if (FAILED(rc)) return rc;
15380
15381 /* Setup one HDD storage controller. */
15382 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15383 if (FAILED(rc)) return rc;
15384
15385 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15386 if (FAILED(rc)) return rc;
15387
15388 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15389
15390 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15391 {
15392 rc = addStorageController(strHDName,
15393 hdStorageBusType,
15394 hdController);
15395 if (FAILED(rc)) return rc;
15396
15397 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15398 if (FAILED(rc)) return rc;
15399 }
15400 else
15401 {
15402 /* The HD controller is the same as DVD: */
15403 hdController = dvdController;
15404 }
15405
15406 /* Limit the AHCI port count if it's used because windows has trouble with
15407 * too many ports and other guest (OS X in particular) may take extra long
15408 * boot: */
15409
15410 // pParent = static_cast<Medium*>(aP)
15411 IStorageController *temp = hdController;
15412 ComObjPtr<StorageController> storageController;
15413 storageController = static_cast<StorageController *>(temp);
15414
15415 // tempHDController = aHDController;
15416 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15417 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15418 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15419 storageController->COMSETTER(PortCount)(1);
15420
15421 /* USB stuff */
15422
15423 bool ohciEnabled = false;
15424
15425 ComPtr<IUSBController> usbController;
15426 BOOL recommendedUSB3;
15427 BOOL recommendedUSB;
15428 BOOL usbProxyAvailable;
15429
15430 getUSBProxyAvailable(&usbProxyAvailable);
15431 if (FAILED(rc)) return rc;
15432
15433 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15434 if (FAILED(rc)) return rc;
15435 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15436 if (FAILED(rc)) return rc;
15437
15438 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15439 {
15440#ifdef VBOX_WITH_EXTPACK
15441 /* USB 3.0 is only available if the proper ExtPack is installed. */
15442 ExtPackManager *aManager = mParent->i_getExtPackManager();
15443 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15444 {
15445 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15446 if (FAILED(rc)) return rc;
15447
15448 /* xHci includes OHCI */
15449 ohciEnabled = true;
15450 }
15451#endif
15452 }
15453 if ( !ohciEnabled
15454 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15455 {
15456 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15457 if (FAILED(rc)) return rc;
15458 ohciEnabled = true;
15459
15460#ifdef VBOX_WITH_EXTPACK
15461 /* USB 2.0 is only available if the proper ExtPack is installed.
15462 * Note. Configuring EHCI here and providing messages about
15463 * the missing extpack isn't exactly clean, but it is a
15464 * necessary evil to patch over legacy compatability issues
15465 * introduced by the new distribution model. */
15466 ExtPackManager *manager = mParent->i_getExtPackManager();
15467 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15468 {
15469 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15470 if (FAILED(rc)) return rc;
15471 }
15472#endif
15473 }
15474
15475 /* Set recommended human interface device types: */
15476 BOOL recommendedUSBHID;
15477 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15478 if (FAILED(rc)) return rc;
15479
15480 if (recommendedUSBHID)
15481 {
15482 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15483 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15484 if (!ohciEnabled && !usbDeviceFilters.isNull())
15485 {
15486 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15487 if (FAILED(rc)) return rc;
15488 }
15489 }
15490
15491 BOOL recommendedUSBTablet;
15492 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15493 if (FAILED(rc)) return rc;
15494
15495 if (recommendedUSBTablet)
15496 {
15497 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15498 if (!ohciEnabled && !usbDeviceFilters.isNull())
15499 {
15500 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15501 if (FAILED(rc)) return rc;
15502 }
15503 }
15504 return S_OK;
15505}
15506
15507/* This isn't handled entirely by the wrapper generator yet. */
15508#ifdef VBOX_WITH_XPCOM
15509NS_DECL_CLASSINFO(SessionMachine)
15510NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15511
15512NS_DECL_CLASSINFO(SnapshotMachine)
15513NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15514#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