VirtualBox

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

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

Main/Console+ConsoleVRDPServer+ApplianceExport+Host+Machine+USBDeviceFilters: fix missing RT_NOREF(...) if VBOX_WITH_USB is undefined

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 542.9 KB
Line 
1/* $Id: MachineImpl.cpp 81682 2019-11-05 19:08:21Z 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
1749HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1750{
1751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1752
1753 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1754
1755 return S_OK;
1756}
1757
1758HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1759{
1760 switch (aGraphicsControllerType)
1761 {
1762 case GraphicsControllerType_Null:
1763 case GraphicsControllerType_VBoxVGA:
1764#ifdef VBOX_WITH_VMSVGA
1765 case GraphicsControllerType_VMSVGA:
1766 case GraphicsControllerType_VBoxSVGA:
1767#endif
1768 break;
1769 default:
1770 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1771 }
1772
1773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1774
1775 HRESULT rc = i_checkStateDependency(MutableStateDep);
1776 if (FAILED(rc)) return rc;
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 *aVRAMSize = mHWData->mVRAMSize;
1790
1791 return S_OK;
1792}
1793
1794HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1795{
1796 /* check VRAM limits */
1797 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1798 return setError(E_INVALIDARG,
1799 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1800 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1801
1802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 HRESULT rc = i_checkStateDependency(MutableStateDep);
1805 if (FAILED(rc)) return rc;
1806
1807 i_setModified(IsModified_MachineData);
1808 mHWData.backup();
1809 mHWData->mVRAMSize = aVRAMSize;
1810
1811 return S_OK;
1812}
1813
1814/** @todo this method should not be public */
1815HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1816{
1817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1818
1819 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1820
1821 return S_OK;
1822}
1823
1824/**
1825 * Set the memory balloon size.
1826 *
1827 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1828 * we have to make sure that we never call IGuest from here.
1829 */
1830HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1831{
1832 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1833#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1834 /* check limits */
1835 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1836 return setError(E_INVALIDARG,
1837 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1838 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1839
1840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1841
1842 i_setModified(IsModified_MachineData);
1843 mHWData.backup();
1844 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1845
1846 return S_OK;
1847#else
1848 NOREF(aMemoryBalloonSize);
1849 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1850#endif
1851}
1852
1853HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1854{
1855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1856
1857 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1858 return S_OK;
1859}
1860
1861HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1862{
1863#ifdef VBOX_WITH_PAGE_SHARING
1864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1867 i_setModified(IsModified_MachineData);
1868 mHWData.backup();
1869 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1870 return S_OK;
1871#else
1872 NOREF(aPageFusionEnabled);
1873 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1874#endif
1875}
1876
1877HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1878{
1879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1882
1883 return S_OK;
1884}
1885
1886HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1887{
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 HRESULT rc = i_checkStateDependency(MutableStateDep);
1891 if (FAILED(rc)) return rc;
1892
1893 /** @todo check validity! */
1894
1895 i_setModified(IsModified_MachineData);
1896 mHWData.backup();
1897 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1898
1899 return S_OK;
1900}
1901
1902
1903HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1904{
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 /** @todo quick workaround for hang with Win10 guest when 2d accel
1908 * is enabled when non-VBoxVGA graphics is configured. */
1909 if (mHWData->mGraphicsControllerType == GraphicsControllerType_VBoxVGA)
1910 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1911 else
1912 *aAccelerate2DVideoEnabled = FALSE;
1913
1914 return S_OK;
1915}
1916
1917HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1918{
1919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 HRESULT rc = i_checkStateDependency(MutableStateDep);
1922 if (FAILED(rc)) return rc;
1923
1924 /** @todo check validity! */
1925 i_setModified(IsModified_MachineData);
1926 mHWData.backup();
1927 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1928
1929 return S_OK;
1930}
1931
1932HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1933{
1934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1935
1936 *aMonitorCount = mHWData->mMonitorCount;
1937
1938 return S_OK;
1939}
1940
1941HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1942{
1943 /* make sure monitor count is a sensible number */
1944 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1945 return setError(E_INVALIDARG,
1946 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1947 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1948
1949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1950
1951 HRESULT rc = i_checkStateDependency(MutableStateDep);
1952 if (FAILED(rc)) return rc;
1953
1954 i_setModified(IsModified_MachineData);
1955 mHWData.backup();
1956 mHWData->mMonitorCount = aMonitorCount;
1957
1958 return S_OK;
1959}
1960
1961HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1962{
1963 /* mBIOSSettings is constant during life time, no need to lock */
1964 aBIOSSettings = mBIOSSettings;
1965
1966 return S_OK;
1967}
1968
1969HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1970{
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 aRecordingSettings = mRecordingSettings;
1974
1975 return S_OK;
1976}
1977
1978HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1979{
1980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1981
1982 switch (aProperty)
1983 {
1984 case CPUPropertyType_PAE:
1985 *aValue = mHWData->mPAEEnabled;
1986 break;
1987
1988 case CPUPropertyType_LongMode:
1989 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1990 *aValue = TRUE;
1991 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1992 *aValue = FALSE;
1993#if HC_ARCH_BITS == 64
1994 else
1995 *aValue = TRUE;
1996#else
1997 else
1998 {
1999 *aValue = FALSE;
2000
2001 ComObjPtr<GuestOSType> pGuestOSType;
2002 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2003 pGuestOSType);
2004 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2005 {
2006 if (pGuestOSType->i_is64Bit())
2007 {
2008 ComObjPtr<Host> pHost = mParent->i_host();
2009 alock.release();
2010
2011 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2012 if (FAILED(hrc2))
2013 *aValue = FALSE;
2014 }
2015 }
2016 }
2017#endif
2018 break;
2019
2020 case CPUPropertyType_TripleFaultReset:
2021 *aValue = mHWData->mTripleFaultReset;
2022 break;
2023
2024 case CPUPropertyType_APIC:
2025 *aValue = mHWData->mAPIC;
2026 break;
2027
2028 case CPUPropertyType_X2APIC:
2029 *aValue = mHWData->mX2APIC;
2030 break;
2031
2032 case CPUPropertyType_IBPBOnVMExit:
2033 *aValue = mHWData->mIBPBOnVMExit;
2034 break;
2035
2036 case CPUPropertyType_IBPBOnVMEntry:
2037 *aValue = mHWData->mIBPBOnVMEntry;
2038 break;
2039
2040 case CPUPropertyType_SpecCtrl:
2041 *aValue = mHWData->mSpecCtrl;
2042 break;
2043
2044 case CPUPropertyType_SpecCtrlByHost:
2045 *aValue = mHWData->mSpecCtrlByHost;
2046 break;
2047
2048 case CPUPropertyType_HWVirt:
2049 *aValue = mHWData->mNestedHWVirt;
2050 break;
2051
2052 case CPUPropertyType_L1DFlushOnEMTScheduling:
2053 *aValue = mHWData->mL1DFlushOnSched;
2054 break;
2055
2056 case CPUPropertyType_L1DFlushOnVMEntry:
2057 *aValue = mHWData->mL1DFlushOnVMEntry;
2058 break;
2059
2060 case CPUPropertyType_MDSClearOnEMTScheduling:
2061 *aValue = mHWData->mMDSClearOnSched;
2062 break;
2063
2064 case CPUPropertyType_MDSClearOnVMEntry:
2065 *aValue = mHWData->mMDSClearOnVMEntry;
2066 break;
2067
2068 default:
2069 return E_INVALIDARG;
2070 }
2071 return S_OK;
2072}
2073
2074HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2075{
2076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 HRESULT rc = i_checkStateDependency(MutableStateDep);
2079 if (FAILED(rc)) return rc;
2080
2081 switch (aProperty)
2082 {
2083 case CPUPropertyType_PAE:
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mPAEEnabled = !!aValue;
2087 break;
2088
2089 case CPUPropertyType_LongMode:
2090 i_setModified(IsModified_MachineData);
2091 mHWData.backup();
2092 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2093 break;
2094
2095 case CPUPropertyType_TripleFaultReset:
2096 i_setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mTripleFaultReset = !!aValue;
2099 break;
2100
2101 case CPUPropertyType_APIC:
2102 if (mHWData->mX2APIC)
2103 aValue = TRUE;
2104 i_setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mAPIC = !!aValue;
2107 break;
2108
2109 case CPUPropertyType_X2APIC:
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mX2APIC = !!aValue;
2113 if (aValue)
2114 mHWData->mAPIC = !!aValue;
2115 break;
2116
2117 case CPUPropertyType_IBPBOnVMExit:
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mIBPBOnVMExit = !!aValue;
2121 break;
2122
2123 case CPUPropertyType_IBPBOnVMEntry:
2124 i_setModified(IsModified_MachineData);
2125 mHWData.backup();
2126 mHWData->mIBPBOnVMEntry = !!aValue;
2127 break;
2128
2129 case CPUPropertyType_SpecCtrl:
2130 i_setModified(IsModified_MachineData);
2131 mHWData.backup();
2132 mHWData->mSpecCtrl = !!aValue;
2133 break;
2134
2135 case CPUPropertyType_SpecCtrlByHost:
2136 i_setModified(IsModified_MachineData);
2137 mHWData.backup();
2138 mHWData->mSpecCtrlByHost = !!aValue;
2139 break;
2140
2141 case CPUPropertyType_HWVirt:
2142 i_setModified(IsModified_MachineData);
2143 mHWData.backup();
2144 mHWData->mNestedHWVirt = !!aValue;
2145 break;
2146
2147 case CPUPropertyType_L1DFlushOnEMTScheduling:
2148 i_setModified(IsModified_MachineData);
2149 mHWData.backup();
2150 mHWData->mL1DFlushOnSched = !!aValue;
2151 break;
2152
2153 case CPUPropertyType_L1DFlushOnVMEntry:
2154 i_setModified(IsModified_MachineData);
2155 mHWData.backup();
2156 mHWData->mL1DFlushOnVMEntry = !!aValue;
2157 break;
2158
2159 case CPUPropertyType_MDSClearOnEMTScheduling:
2160 i_setModified(IsModified_MachineData);
2161 mHWData.backup();
2162 mHWData->mMDSClearOnSched = !!aValue;
2163 break;
2164
2165 case CPUPropertyType_MDSClearOnVMEntry:
2166 i_setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mMDSClearOnVMEntry = !!aValue;
2169 break;
2170
2171 default:
2172 return E_INVALIDARG;
2173 }
2174 return S_OK;
2175}
2176
2177HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2178 ULONG *aValEcx, ULONG *aValEdx)
2179{
2180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2181 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2182 {
2183 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2184 it != mHWData->mCpuIdLeafList.end();
2185 ++it)
2186 {
2187 if (aOrdinal == 0)
2188 {
2189 const settings::CpuIdLeaf &rLeaf= *it;
2190 *aIdx = rLeaf.idx;
2191 *aSubIdx = rLeaf.idxSub;
2192 *aValEax = rLeaf.uEax;
2193 *aValEbx = rLeaf.uEbx;
2194 *aValEcx = rLeaf.uEcx;
2195 *aValEdx = rLeaf.uEdx;
2196 return S_OK;
2197 }
2198 aOrdinal--;
2199 }
2200 }
2201 return E_INVALIDARG;
2202}
2203
2204HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2205{
2206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 /*
2209 * Search the list.
2210 */
2211 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2212 {
2213 const settings::CpuIdLeaf &rLeaf= *it;
2214 if ( rLeaf.idx == aIdx
2215 && ( aSubIdx == UINT32_MAX
2216 || rLeaf.idxSub == aSubIdx) )
2217 {
2218 *aValEax = rLeaf.uEax;
2219 *aValEbx = rLeaf.uEbx;
2220 *aValEcx = rLeaf.uEcx;
2221 *aValEdx = rLeaf.uEdx;
2222 return S_OK;
2223 }
2224 }
2225
2226 return E_INVALIDARG;
2227}
2228
2229
2230HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2231{
2232 /*
2233 * Validate input before taking locks and checking state.
2234 */
2235 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2236 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2237 if ( aIdx >= UINT32_C(0x20)
2238 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2239 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2240 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2241
2242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2243 HRESULT rc = i_checkStateDependency(MutableStateDep);
2244 if (FAILED(rc)) return rc;
2245
2246 /*
2247 * Impose a maximum number of leaves.
2248 */
2249 if (mHWData->mCpuIdLeafList.size() > 256)
2250 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2251
2252 /*
2253 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2254 */
2255 i_setModified(IsModified_MachineData);
2256 mHWData.backup();
2257
2258 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2259 {
2260 settings::CpuIdLeaf &rLeaf= *it;
2261 if ( rLeaf.idx == aIdx
2262 && ( aSubIdx == UINT32_MAX
2263 || rLeaf.idxSub == aSubIdx) )
2264 it = mHWData->mCpuIdLeafList.erase(it);
2265 else
2266 ++it;
2267 }
2268
2269 settings::CpuIdLeaf NewLeaf;
2270 NewLeaf.idx = aIdx;
2271 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2272 NewLeaf.uEax = aValEax;
2273 NewLeaf.uEbx = aValEbx;
2274 NewLeaf.uEcx = aValEcx;
2275 NewLeaf.uEdx = aValEdx;
2276 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2277 return S_OK;
2278}
2279
2280HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2281{
2282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2283
2284 HRESULT rc = i_checkStateDependency(MutableStateDep);
2285 if (FAILED(rc)) return rc;
2286
2287 /*
2288 * Do the removal.
2289 */
2290 bool fModified = mHWData.isBackedUp();
2291 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2292 {
2293 settings::CpuIdLeaf &rLeaf= *it;
2294 if ( rLeaf.idx == aIdx
2295 && ( aSubIdx == UINT32_MAX
2296 || rLeaf.idxSub == aSubIdx) )
2297 {
2298 if (!fModified)
2299 {
2300 fModified = true;
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 // Start from the beginning, since mHWData.backup() creates
2304 // a new list, causing iterator mixup. This makes sure that
2305 // the settings are not unnecessarily marked as modified,
2306 // at the price of extra list walking.
2307 it = mHWData->mCpuIdLeafList.begin();
2308 }
2309 else
2310 it = mHWData->mCpuIdLeafList.erase(it);
2311 }
2312 else
2313 ++it;
2314 }
2315
2316 return S_OK;
2317}
2318
2319HRESULT Machine::removeAllCPUIDLeaves()
2320{
2321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2322
2323 HRESULT rc = i_checkStateDependency(MutableStateDep);
2324 if (FAILED(rc)) return rc;
2325
2326 if (mHWData->mCpuIdLeafList.size() > 0)
2327 {
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330
2331 mHWData->mCpuIdLeafList.clear();
2332 }
2333
2334 return S_OK;
2335}
2336HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2337{
2338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2339
2340 switch(aProperty)
2341 {
2342 case HWVirtExPropertyType_Enabled:
2343 *aValue = mHWData->mHWVirtExEnabled;
2344 break;
2345
2346 case HWVirtExPropertyType_VPID:
2347 *aValue = mHWData->mHWVirtExVPIDEnabled;
2348 break;
2349
2350 case HWVirtExPropertyType_NestedPaging:
2351 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2352 break;
2353
2354 case HWVirtExPropertyType_UnrestrictedExecution:
2355 *aValue = mHWData->mHWVirtExUXEnabled;
2356 break;
2357
2358 case HWVirtExPropertyType_LargePages:
2359 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2360#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2361 *aValue = FALSE;
2362#endif
2363 break;
2364
2365 case HWVirtExPropertyType_Force:
2366 *aValue = mHWData->mHWVirtExForceEnabled;
2367 break;
2368
2369 case HWVirtExPropertyType_UseNativeApi:
2370 *aValue = mHWData->mHWVirtExUseNativeApi;
2371 break;
2372
2373 default:
2374 return E_INVALIDARG;
2375 }
2376 return S_OK;
2377}
2378
2379HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2380{
2381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 HRESULT rc = i_checkStateDependency(MutableStateDep);
2384 if (FAILED(rc)) return rc;
2385
2386 switch (aProperty)
2387 {
2388 case HWVirtExPropertyType_Enabled:
2389 i_setModified(IsModified_MachineData);
2390 mHWData.backup();
2391 mHWData->mHWVirtExEnabled = !!aValue;
2392 break;
2393
2394 case HWVirtExPropertyType_VPID:
2395 i_setModified(IsModified_MachineData);
2396 mHWData.backup();
2397 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2398 break;
2399
2400 case HWVirtExPropertyType_NestedPaging:
2401 i_setModified(IsModified_MachineData);
2402 mHWData.backup();
2403 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2404 break;
2405
2406 case HWVirtExPropertyType_UnrestrictedExecution:
2407 i_setModified(IsModified_MachineData);
2408 mHWData.backup();
2409 mHWData->mHWVirtExUXEnabled = !!aValue;
2410 break;
2411
2412 case HWVirtExPropertyType_LargePages:
2413 i_setModified(IsModified_MachineData);
2414 mHWData.backup();
2415 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2416 break;
2417
2418 case HWVirtExPropertyType_Force:
2419 i_setModified(IsModified_MachineData);
2420 mHWData.backup();
2421 mHWData->mHWVirtExForceEnabled = !!aValue;
2422 break;
2423
2424 case HWVirtExPropertyType_UseNativeApi:
2425 i_setModified(IsModified_MachineData);
2426 mHWData.backup();
2427 mHWData->mHWVirtExUseNativeApi = !!aValue;
2428 break;
2429
2430 default:
2431 return E_INVALIDARG;
2432 }
2433
2434 return S_OK;
2435}
2436
2437HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2438{
2439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2440
2441 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2442
2443 return S_OK;
2444}
2445
2446HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2447{
2448 /** @todo (r=dmik):
2449 * 1. Allow to change the name of the snapshot folder containing snapshots
2450 * 2. Rename the folder on disk instead of just changing the property
2451 * value (to be smart and not to leave garbage). Note that it cannot be
2452 * done here because the change may be rolled back. Thus, the right
2453 * place is #saveSettings().
2454 */
2455
2456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2457
2458 HRESULT rc = i_checkStateDependency(MutableStateDep);
2459 if (FAILED(rc)) return rc;
2460
2461 if (!mData->mCurrentSnapshot.isNull())
2462 return setError(E_FAIL,
2463 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2464
2465 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2466
2467 if (strSnapshotFolder.isEmpty())
2468 strSnapshotFolder = "Snapshots";
2469 int vrc = i_calculateFullPath(strSnapshotFolder,
2470 strSnapshotFolder);
2471 if (RT_FAILURE(vrc))
2472 return setErrorBoth(E_FAIL, vrc,
2473 tr("Invalid snapshot folder '%s' (%Rrc)"),
2474 strSnapshotFolder.c_str(), vrc);
2475
2476 i_setModified(IsModified_MachineData);
2477 mUserData.backup();
2478
2479 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2480
2481 return S_OK;
2482}
2483
2484HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2485{
2486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2487
2488 aMediumAttachments.resize(mMediumAttachments->size());
2489 size_t i = 0;
2490 for (MediumAttachmentList::const_iterator
2491 it = mMediumAttachments->begin();
2492 it != mMediumAttachments->end();
2493 ++it, ++i)
2494 aMediumAttachments[i] = *it;
2495
2496 return S_OK;
2497}
2498
2499HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2500{
2501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2502
2503 Assert(!!mVRDEServer);
2504
2505 aVRDEServer = mVRDEServer;
2506
2507 return S_OK;
2508}
2509
2510HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2511{
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 aAudioAdapter = mAudioAdapter;
2515
2516 return S_OK;
2517}
2518
2519HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2520{
2521#ifdef VBOX_WITH_VUSB
2522 clearError();
2523 MultiResult rc(S_OK);
2524
2525# ifdef VBOX_WITH_USB
2526 rc = mParent->i_host()->i_checkUSBProxyService();
2527 if (FAILED(rc)) return rc;
2528# endif
2529
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 aUSBControllers.resize(mUSBControllers->size());
2533 size_t i = 0;
2534 for (USBControllerList::const_iterator
2535 it = mUSBControllers->begin();
2536 it != mUSBControllers->end();
2537 ++it, ++i)
2538 aUSBControllers[i] = *it;
2539
2540 return S_OK;
2541#else
2542 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2543 * extended error info to indicate that USB is simply not available
2544 * (w/o treating it as a failure), for example, as in OSE */
2545 NOREF(aUSBControllers);
2546 ReturnComNotImplemented();
2547#endif /* VBOX_WITH_VUSB */
2548}
2549
2550HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2551{
2552#ifdef VBOX_WITH_VUSB
2553 clearError();
2554 MultiResult rc(S_OK);
2555
2556# ifdef VBOX_WITH_USB
2557 rc = mParent->i_host()->i_checkUSBProxyService();
2558 if (FAILED(rc)) return rc;
2559# endif
2560
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 aUSBDeviceFilters = mUSBDeviceFilters;
2564 return rc;
2565#else
2566 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2567 * extended error info to indicate that USB is simply not available
2568 * (w/o treating it as a failure), for example, as in OSE */
2569 NOREF(aUSBDeviceFilters);
2570 ReturnComNotImplemented();
2571#endif /* VBOX_WITH_VUSB */
2572}
2573
2574HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 aSettingsFilePath = mData->m_strConfigFileFull;
2579
2580 return S_OK;
2581}
2582
2583HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2584{
2585 RT_NOREF(aSettingsFilePath);
2586 ReturnComNotImplemented();
2587}
2588
2589HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2590{
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2594 if (FAILED(rc)) return rc;
2595
2596 if (!mData->pMachineConfigFile->fileExists())
2597 // this is a new machine, and no config file exists yet:
2598 *aSettingsModified = TRUE;
2599 else
2600 *aSettingsModified = (mData->flModifications != 0);
2601
2602 return S_OK;
2603}
2604
2605HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2606{
2607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 *aSessionState = mData->mSession.mState;
2610
2611 return S_OK;
2612}
2613
2614HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2615{
2616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 aSessionName = mData->mSession.mName;
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2624{
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 *aSessionPID = mData->mSession.mPID;
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getState(MachineState_T *aState)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 *aState = mData->mMachineState;
2637 Assert(mData->mMachineState != MachineState_Null);
2638
2639 return S_OK;
2640}
2641
2642HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2643{
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2647
2648 return S_OK;
2649}
2650
2651HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2652{
2653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2654
2655 aStateFilePath = mSSData->strStateFilePath;
2656
2657 return S_OK;
2658}
2659
2660HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2661{
2662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2663
2664 i_getLogFolder(aLogFolder);
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 aCurrentSnapshot = mData->mCurrentSnapshot;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2679{
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2683 ? 0
2684 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2685
2686 return S_OK;
2687}
2688
2689HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2690{
2691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 /* Note: for machines with no snapshots, we always return FALSE
2694 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2695 * reasons :) */
2696
2697 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2698 ? FALSE
2699 : mData->mCurrentStateModified;
2700
2701 return S_OK;
2702}
2703
2704HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2705{
2706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 aSharedFolders.resize(mHWData->mSharedFolders.size());
2709 size_t i = 0;
2710 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2711 it = mHWData->mSharedFolders.begin();
2712 it != mHWData->mSharedFolders.end();
2713 ++it, ++i)
2714 aSharedFolders[i] = *it;
2715
2716 return S_OK;
2717}
2718
2719HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2720{
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aClipboardMode = mHWData->mClipboardMode;
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2729{
2730 HRESULT rc = S_OK;
2731
2732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 alock.release();
2735 rc = i_onClipboardModeChange(aClipboardMode);
2736 alock.acquire();
2737 if (FAILED(rc)) return rc;
2738
2739 i_setModified(IsModified_MachineData);
2740 mHWData.backup();
2741 mHWData->mClipboardMode = aClipboardMode;
2742
2743 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2744 if (Global::IsOnline(mData->mMachineState))
2745 i_saveSettings(NULL);
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2751{
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2760{
2761 HRESULT rc = S_OK;
2762
2763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 alock.release();
2766 rc = i_onClipboardFileTransferModeChange(aEnabled);
2767 alock.acquire();
2768 if (FAILED(rc)) return rc;
2769
2770 i_setModified(IsModified_MachineData);
2771 mHWData.backup();
2772 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2773
2774 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2775 if (Global::IsOnline(mData->mMachineState))
2776 i_saveSettings(NULL);
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2782{
2783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 *aDnDMode = mHWData->mDnDMode;
2786
2787 return S_OK;
2788}
2789
2790HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2791{
2792 HRESULT rc = S_OK;
2793
2794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2795
2796 alock.release();
2797 rc = i_onDnDModeChange(aDnDMode);
2798
2799 alock.acquire();
2800 if (FAILED(rc)) return rc;
2801
2802 i_setModified(IsModified_MachineData);
2803 mHWData.backup();
2804 mHWData->mDnDMode = aDnDMode;
2805
2806 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2807 if (Global::IsOnline(mData->mMachineState))
2808 i_saveSettings(NULL);
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2814{
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 aStorageControllers.resize(mStorageControllers->size());
2818 size_t i = 0;
2819 for (StorageControllerList::const_iterator
2820 it = mStorageControllers->begin();
2821 it != mStorageControllers->end();
2822 ++it, ++i)
2823 aStorageControllers[i] = *it;
2824
2825 return S_OK;
2826}
2827
2828HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2829{
2830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 *aEnabled = mUserData->s.fTeleporterEnabled;
2833
2834 return S_OK;
2835}
2836
2837HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2838{
2839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 /* Only allow it to be set to true when PoweredOff or Aborted.
2842 (Clearing it is always permitted.) */
2843 if ( aTeleporterEnabled
2844 && mData->mRegistered
2845 && ( !i_isSessionMachine()
2846 || ( mData->mMachineState != MachineState_PoweredOff
2847 && mData->mMachineState != MachineState_Teleported
2848 && mData->mMachineState != MachineState_Aborted
2849 )
2850 )
2851 )
2852 return setError(VBOX_E_INVALID_VM_STATE,
2853 tr("The machine is not powered off (state is %s)"),
2854 Global::stringifyMachineState(mData->mMachineState));
2855
2856 i_setModified(IsModified_MachineData);
2857 mUserData.backup();
2858 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2859
2860 return S_OK;
2861}
2862
2863HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2864{
2865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2866
2867 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2868
2869 return S_OK;
2870}
2871
2872HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2873{
2874 if (aTeleporterPort >= _64K)
2875 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2876
2877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2880 if (FAILED(rc)) return rc;
2881
2882 i_setModified(IsModified_MachineData);
2883 mUserData.backup();
2884 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2885
2886 return S_OK;
2887}
2888
2889HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2890{
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2894
2895 return S_OK;
2896}
2897
2898HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2899{
2900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2903 if (FAILED(rc)) return rc;
2904
2905 i_setModified(IsModified_MachineData);
2906 mUserData.backup();
2907 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2913{
2914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2915 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2916
2917 return S_OK;
2918}
2919
2920HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2921{
2922 /*
2923 * Hash the password first.
2924 */
2925 com::Utf8Str aT = aTeleporterPassword;
2926
2927 if (!aT.isEmpty())
2928 {
2929 if (VBoxIsPasswordHashed(&aT))
2930 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2931 VBoxHashPassword(&aT);
2932 }
2933
2934 /*
2935 * Do the update.
2936 */
2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2938 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2939 if (SUCCEEDED(hrc))
2940 {
2941 i_setModified(IsModified_MachineData);
2942 mUserData.backup();
2943 mUserData->s.strTeleporterPassword = aT;
2944 }
2945
2946 return hrc;
2947}
2948
2949HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2950{
2951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2952
2953 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2954
2955 return S_OK;
2956}
2957
2958HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2959{
2960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2961
2962 /* Only allow it to be set to true when PoweredOff or Aborted.
2963 (Clearing it is always permitted.) */
2964 if ( aRTCUseUTC
2965 && mData->mRegistered
2966 && ( !i_isSessionMachine()
2967 || ( mData->mMachineState != MachineState_PoweredOff
2968 && mData->mMachineState != MachineState_Teleported
2969 && mData->mMachineState != MachineState_Aborted
2970 )
2971 )
2972 )
2973 return setError(VBOX_E_INVALID_VM_STATE,
2974 tr("The machine is not powered off (state is %s)"),
2975 Global::stringifyMachineState(mData->mMachineState));
2976
2977 i_setModified(IsModified_MachineData);
2978 mUserData.backup();
2979 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2980
2981 return S_OK;
2982}
2983
2984HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2985{
2986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2987
2988 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2989
2990 return S_OK;
2991}
2992
2993HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2994{
2995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2996
2997 HRESULT rc = i_checkStateDependency(MutableStateDep);
2998 if (FAILED(rc)) return rc;
2999
3000 i_setModified(IsModified_MachineData);
3001 mHWData.backup();
3002 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3003
3004 return S_OK;
3005}
3006
3007HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3008{
3009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 *aIOCacheSize = mHWData->mIOCacheSize;
3012
3013 return S_OK;
3014}
3015
3016HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3017{
3018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3019
3020 HRESULT rc = i_checkStateDependency(MutableStateDep);
3021 if (FAILED(rc)) return rc;
3022
3023 i_setModified(IsModified_MachineData);
3024 mHWData.backup();
3025 mHWData->mIOCacheSize = aIOCacheSize;
3026
3027 return S_OK;
3028}
3029
3030
3031/**
3032 * @note Locks objects!
3033 */
3034HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3035 LockType_T aLockType)
3036{
3037 /* check the session state */
3038 SessionState_T state;
3039 HRESULT rc = aSession->COMGETTER(State)(&state);
3040 if (FAILED(rc)) return rc;
3041
3042 if (state != SessionState_Unlocked)
3043 return setError(VBOX_E_INVALID_OBJECT_STATE,
3044 tr("The given session is busy"));
3045
3046 // get the client's IInternalSessionControl interface
3047 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3048 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3049 E_INVALIDARG);
3050
3051 // session name (only used in some code paths)
3052 Utf8Str strSessionName;
3053
3054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 if (!mData->mRegistered)
3057 return setError(E_UNEXPECTED,
3058 tr("The machine '%s' is not registered"),
3059 mUserData->s.strName.c_str());
3060
3061 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3062
3063 SessionState_T oldState = mData->mSession.mState;
3064 /* Hack: in case the session is closing and there is a progress object
3065 * which allows waiting for the session to be closed, take the opportunity
3066 * and do a limited wait (max. 1 second). This helps a lot when the system
3067 * is busy and thus session closing can take a little while. */
3068 if ( mData->mSession.mState == SessionState_Unlocking
3069 && mData->mSession.mProgress)
3070 {
3071 alock.release();
3072 mData->mSession.mProgress->WaitForCompletion(1000);
3073 alock.acquire();
3074 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3075 }
3076
3077 // try again now
3078 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3079 // (i.e. session machine exists)
3080 && (aLockType == LockType_Shared) // caller wants a shared link to the
3081 // existing session that holds the write lock:
3082 )
3083 {
3084 // OK, share the session... we are now dealing with three processes:
3085 // 1) VBoxSVC (where this code runs);
3086 // 2) process C: the caller's client process (who wants a shared session);
3087 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3088
3089 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3090 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3091 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3092 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3093 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3094
3095 /*
3096 * Release the lock before calling the client process. It's safe here
3097 * since the only thing to do after we get the lock again is to add
3098 * the remote control to the list (which doesn't directly influence
3099 * anything).
3100 */
3101 alock.release();
3102
3103 // get the console of the session holding the write lock (this is a remote call)
3104 ComPtr<IConsole> pConsoleW;
3105 if (mData->mSession.mLockType == LockType_VM)
3106 {
3107 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3108 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3109 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3110 if (FAILED(rc))
3111 // the failure may occur w/o any error info (from RPC), so provide one
3112 return setError(VBOX_E_VM_ERROR,
3113 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3114 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3115 }
3116
3117 // share the session machine and W's console with the caller's session
3118 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3119 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3120 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3121
3122 if (FAILED(rc))
3123 // the failure may occur w/o any error info (from RPC), so provide one
3124 return setError(VBOX_E_VM_ERROR,
3125 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3126 alock.acquire();
3127
3128 // need to revalidate the state after acquiring the lock again
3129 if (mData->mSession.mState != SessionState_Locked)
3130 {
3131 pSessionControl->Uninitialize();
3132 return setError(VBOX_E_INVALID_SESSION_STATE,
3133 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3134 mUserData->s.strName.c_str());
3135 }
3136
3137 // add the caller's session to the list
3138 mData->mSession.mRemoteControls.push_back(pSessionControl);
3139 }
3140 else if ( mData->mSession.mState == SessionState_Locked
3141 || mData->mSession.mState == SessionState_Unlocking
3142 )
3143 {
3144 // sharing not permitted, or machine still unlocking:
3145 return setError(VBOX_E_INVALID_OBJECT_STATE,
3146 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3147 mUserData->s.strName.c_str());
3148 }
3149 else
3150 {
3151 // machine is not locked: then write-lock the machine (create the session machine)
3152
3153 // must not be busy
3154 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3155
3156 // get the caller's session PID
3157 RTPROCESS pid = NIL_RTPROCESS;
3158 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3159 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3160 Assert(pid != NIL_RTPROCESS);
3161
3162 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3163
3164 if (fLaunchingVMProcess)
3165 {
3166 if (mData->mSession.mPID == NIL_RTPROCESS)
3167 {
3168 // two or more clients racing for a lock, the one which set the
3169 // session state to Spawning will win, the others will get an
3170 // error as we can't decide here if waiting a little would help
3171 // (only for shared locks this would avoid an error)
3172 return setError(VBOX_E_INVALID_OBJECT_STATE,
3173 tr("The machine '%s' already has a lock request pending"),
3174 mUserData->s.strName.c_str());
3175 }
3176
3177 // this machine is awaiting for a spawning session to be opened:
3178 // then the calling process must be the one that got started by
3179 // LaunchVMProcess()
3180
3181 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3182 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3183
3184#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3185 /* Hardened windows builds spawns three processes when a VM is
3186 launched, the 3rd one is the one that will end up here. */
3187 RTPROCESS ppid;
3188 int rc = RTProcQueryParent(pid, &ppid);
3189 if (RT_SUCCESS(rc))
3190 rc = RTProcQueryParent(ppid, &ppid);
3191 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3192 || rc == VERR_ACCESS_DENIED)
3193 {
3194 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3195 mData->mSession.mPID = pid;
3196 }
3197#endif
3198
3199 if (mData->mSession.mPID != pid)
3200 return setError(E_ACCESSDENIED,
3201 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3202 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3203 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3204 }
3205
3206 // create the mutable SessionMachine from the current machine
3207 ComObjPtr<SessionMachine> sessionMachine;
3208 sessionMachine.createObject();
3209 rc = sessionMachine->init(this);
3210 AssertComRC(rc);
3211
3212 /* NOTE: doing return from this function after this point but
3213 * before the end is forbidden since it may call SessionMachine::uninit()
3214 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3215 * lock while still holding the Machine lock in alock so that a deadlock
3216 * is possible due to the wrong lock order. */
3217
3218 if (SUCCEEDED(rc))
3219 {
3220 /*
3221 * Set the session state to Spawning to protect against subsequent
3222 * attempts to open a session and to unregister the machine after
3223 * we release the lock.
3224 */
3225 SessionState_T origState = mData->mSession.mState;
3226 mData->mSession.mState = SessionState_Spawning;
3227
3228#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3229 /* Get the client token ID to be passed to the client process */
3230 Utf8Str strTokenId;
3231 sessionMachine->i_getTokenId(strTokenId);
3232 Assert(!strTokenId.isEmpty());
3233#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3234 /* Get the client token to be passed to the client process */
3235 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3236 /* The token is now "owned" by pToken, fix refcount */
3237 if (!pToken.isNull())
3238 pToken->Release();
3239#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3240
3241 /*
3242 * Release the lock before calling the client process -- it will call
3243 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3244 * because the state is Spawning, so that LaunchVMProcess() and
3245 * LockMachine() calls will fail. This method, called before we
3246 * acquire the lock again, will fail because of the wrong PID.
3247 *
3248 * Note that mData->mSession.mRemoteControls accessed outside
3249 * the lock may not be modified when state is Spawning, so it's safe.
3250 */
3251 alock.release();
3252
3253 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3254#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3255 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3256#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3257 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3258 /* Now the token is owned by the client process. */
3259 pToken.setNull();
3260#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3261 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3262
3263 /* The failure may occur w/o any error info (from RPC), so provide one */
3264 if (FAILED(rc))
3265 setError(VBOX_E_VM_ERROR,
3266 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3267
3268 // get session name, either to remember or to compare against
3269 // the already known session name.
3270 {
3271 Bstr bstrSessionName;
3272 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3273 if (SUCCEEDED(rc2))
3274 strSessionName = bstrSessionName;
3275 }
3276
3277 if ( SUCCEEDED(rc)
3278 && fLaunchingVMProcess
3279 )
3280 {
3281 /* complete the remote session initialization */
3282
3283 /* get the console from the direct session */
3284 ComPtr<IConsole> console;
3285 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3286 ComAssertComRC(rc);
3287
3288 if (SUCCEEDED(rc) && !console)
3289 {
3290 ComAssert(!!console);
3291 rc = E_FAIL;
3292 }
3293
3294 /* assign machine & console to the remote session */
3295 if (SUCCEEDED(rc))
3296 {
3297 /*
3298 * after LaunchVMProcess(), the first and the only
3299 * entry in remoteControls is that remote session
3300 */
3301 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3302 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3303 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3304
3305 /* The failure may occur w/o any error info (from RPC), so provide one */
3306 if (FAILED(rc))
3307 setError(VBOX_E_VM_ERROR,
3308 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3309 }
3310
3311 if (FAILED(rc))
3312 pSessionControl->Uninitialize();
3313 }
3314
3315 /* acquire the lock again */
3316 alock.acquire();
3317
3318 /* Restore the session state */
3319 mData->mSession.mState = origState;
3320 }
3321
3322 // finalize spawning anyway (this is why we don't return on errors above)
3323 if (fLaunchingVMProcess)
3324 {
3325 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3326 /* Note that the progress object is finalized later */
3327 /** @todo Consider checking mData->mSession.mProgress for cancellation
3328 * around here. */
3329
3330 /* We don't reset mSession.mPID here because it is necessary for
3331 * SessionMachine::uninit() to reap the child process later. */
3332
3333 if (FAILED(rc))
3334 {
3335 /* Close the remote session, remove the remote control from the list
3336 * and reset session state to Closed (@note keep the code in sync
3337 * with the relevant part in checkForSpawnFailure()). */
3338
3339 Assert(mData->mSession.mRemoteControls.size() == 1);
3340 if (mData->mSession.mRemoteControls.size() == 1)
3341 {
3342 ErrorInfoKeeper eik;
3343 mData->mSession.mRemoteControls.front()->Uninitialize();
3344 }
3345
3346 mData->mSession.mRemoteControls.clear();
3347 mData->mSession.mState = SessionState_Unlocked;
3348 }
3349 }
3350 else
3351 {
3352 /* memorize PID of the directly opened session */
3353 if (SUCCEEDED(rc))
3354 mData->mSession.mPID = pid;
3355 }
3356
3357 if (SUCCEEDED(rc))
3358 {
3359 mData->mSession.mLockType = aLockType;
3360 /* memorize the direct session control and cache IUnknown for it */
3361 mData->mSession.mDirectControl = pSessionControl;
3362 mData->mSession.mState = SessionState_Locked;
3363 if (!fLaunchingVMProcess)
3364 mData->mSession.mName = strSessionName;
3365 /* associate the SessionMachine with this Machine */
3366 mData->mSession.mMachine = sessionMachine;
3367
3368 /* request an IUnknown pointer early from the remote party for later
3369 * identity checks (it will be internally cached within mDirectControl
3370 * at least on XPCOM) */
3371 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3372 NOREF(unk);
3373 }
3374
3375 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3376 * would break the lock order */
3377 alock.release();
3378
3379 /* uninitialize the created session machine on failure */
3380 if (FAILED(rc))
3381 sessionMachine->uninit();
3382 }
3383
3384 if (SUCCEEDED(rc))
3385 {
3386 /*
3387 * tell the client watcher thread to update the set of
3388 * machines that have open sessions
3389 */
3390 mParent->i_updateClientWatcher();
3391
3392 if (oldState != SessionState_Locked)
3393 /* fire an event */
3394 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3395 }
3396
3397 return rc;
3398}
3399
3400/**
3401 * @note Locks objects!
3402 */
3403HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3404 const com::Utf8Str &aName,
3405 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3406 ComPtr<IProgress> &aProgress)
3407{
3408 Utf8Str strFrontend(aName);
3409 /* "emergencystop" doesn't need the session, so skip the checks/interface
3410 * retrieval. This code doesn't quite fit in here, but introducing a
3411 * special API method would be even more effort, and would require explicit
3412 * support by every API client. It's better to hide the feature a bit. */
3413 if (strFrontend != "emergencystop")
3414 CheckComArgNotNull(aSession);
3415
3416 HRESULT rc = S_OK;
3417 if (strFrontend.isEmpty())
3418 {
3419 Bstr bstrFrontend;
3420 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3421 if (FAILED(rc))
3422 return rc;
3423 strFrontend = bstrFrontend;
3424 if (strFrontend.isEmpty())
3425 {
3426 ComPtr<ISystemProperties> systemProperties;
3427 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3428 if (FAILED(rc))
3429 return rc;
3430 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3431 if (FAILED(rc))
3432 return rc;
3433 strFrontend = bstrFrontend;
3434 }
3435 /* paranoia - emergencystop is not a valid default */
3436 if (strFrontend == "emergencystop")
3437 strFrontend = Utf8Str::Empty;
3438 }
3439 /* default frontend: Qt GUI */
3440 if (strFrontend.isEmpty())
3441 strFrontend = "GUI/Qt";
3442
3443 if (strFrontend != "emergencystop")
3444 {
3445 /* check the session state */
3446 SessionState_T state;
3447 rc = aSession->COMGETTER(State)(&state);
3448 if (FAILED(rc))
3449 return rc;
3450
3451 if (state != SessionState_Unlocked)
3452 return setError(VBOX_E_INVALID_OBJECT_STATE,
3453 tr("The given session is busy"));
3454
3455 /* get the IInternalSessionControl interface */
3456 ComPtr<IInternalSessionControl> control(aSession);
3457 ComAssertMsgRet(!control.isNull(),
3458 ("No IInternalSessionControl interface"),
3459 E_INVALIDARG);
3460
3461 /* get the teleporter enable state for the progress object init. */
3462 BOOL fTeleporterEnabled;
3463 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3464 if (FAILED(rc))
3465 return rc;
3466
3467 /* create a progress object */
3468 ComObjPtr<ProgressProxy> progress;
3469 progress.createObject();
3470 rc = progress->init(mParent,
3471 static_cast<IMachine*>(this),
3472 Bstr(tr("Starting VM")).raw(),
3473 TRUE /* aCancelable */,
3474 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3475 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3476 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3477 2 /* uFirstOperationWeight */,
3478 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3479
3480 if (SUCCEEDED(rc))
3481 {
3482#ifdef VBOX_WITH_CLOUD_NET
3483 i_connectToCloudNetwork(progress);
3484#endif /* VBOX_WITH_CLOUD_NET */
3485
3486 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3487 if (SUCCEEDED(rc))
3488 {
3489 aProgress = progress;
3490
3491 /* signal the client watcher thread */
3492 mParent->i_updateClientWatcher();
3493
3494 /* fire an event */
3495 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3496 }
3497 }
3498 }
3499 else
3500 {
3501 /* no progress object - either instant success or failure */
3502 aProgress = NULL;
3503
3504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3505
3506 if (mData->mSession.mState != SessionState_Locked)
3507 return setError(VBOX_E_INVALID_OBJECT_STATE,
3508 tr("The machine '%s' is not locked by a session"),
3509 mUserData->s.strName.c_str());
3510
3511 /* must have a VM process associated - do not kill normal API clients
3512 * with an open session */
3513 if (!Global::IsOnline(mData->mMachineState))
3514 return setError(VBOX_E_INVALID_OBJECT_STATE,
3515 tr("The machine '%s' does not have a VM process"),
3516 mUserData->s.strName.c_str());
3517
3518 /* forcibly terminate the VM process */
3519 if (mData->mSession.mPID != NIL_RTPROCESS)
3520 RTProcTerminate(mData->mSession.mPID);
3521
3522 /* signal the client watcher thread, as most likely the client has
3523 * been terminated */
3524 mParent->i_updateClientWatcher();
3525 }
3526
3527 return rc;
3528}
3529
3530HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3531{
3532 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3533 return setError(E_INVALIDARG,
3534 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3535 aPosition, SchemaDefs::MaxBootPosition);
3536
3537 if (aDevice == DeviceType_USB)
3538 return setError(E_NOTIMPL,
3539 tr("Booting from USB device is currently not supported"));
3540
3541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3542
3543 HRESULT rc = i_checkStateDependency(MutableStateDep);
3544 if (FAILED(rc)) return rc;
3545
3546 i_setModified(IsModified_MachineData);
3547 mHWData.backup();
3548 mHWData->mBootOrder[aPosition - 1] = aDevice;
3549
3550 return S_OK;
3551}
3552
3553HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3554{
3555 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3556 return setError(E_INVALIDARG,
3557 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3558 aPosition, SchemaDefs::MaxBootPosition);
3559
3560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3561
3562 *aDevice = mHWData->mBootOrder[aPosition - 1];
3563
3564 return S_OK;
3565}
3566
3567HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3568 LONG aControllerPort,
3569 LONG aDevice,
3570 DeviceType_T aType,
3571 const ComPtr<IMedium> &aMedium)
3572{
3573 IMedium *aM = aMedium;
3574 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3575 aName.c_str(), aControllerPort, aDevice, aType, aM));
3576
3577 // request the host lock first, since might be calling Host methods for getting host drives;
3578 // next, protect the media tree all the while we're in here, as well as our member variables
3579 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3580 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3581
3582 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3583 if (FAILED(rc)) return rc;
3584
3585 /// @todo NEWMEDIA implicit machine registration
3586 if (!mData->mRegistered)
3587 return setError(VBOX_E_INVALID_OBJECT_STATE,
3588 tr("Cannot attach storage devices to an unregistered machine"));
3589
3590 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3591
3592 /* Check for an existing controller. */
3593 ComObjPtr<StorageController> ctl;
3594 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3595 if (FAILED(rc)) return rc;
3596
3597 StorageControllerType_T ctrlType;
3598 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3599 if (FAILED(rc))
3600 return setError(E_FAIL,
3601 tr("Could not get type of controller '%s'"),
3602 aName.c_str());
3603
3604 bool fSilent = false;
3605 Utf8Str strReconfig;
3606
3607 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3608 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3609 if ( mData->mMachineState == MachineState_Paused
3610 && strReconfig == "1")
3611 fSilent = true;
3612
3613 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3614 bool fHotplug = false;
3615 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3616 fHotplug = true;
3617
3618 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3619 return setError(VBOX_E_INVALID_VM_STATE,
3620 tr("Controller '%s' does not support hotplugging"),
3621 aName.c_str());
3622
3623 // check that the port and device are not out of range
3624 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3625 if (FAILED(rc)) return rc;
3626
3627 /* check if the device slot is already busy */
3628 MediumAttachment *pAttachTemp;
3629 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3630 aName,
3631 aControllerPort,
3632 aDevice)))
3633 {
3634 Medium *pMedium = pAttachTemp->i_getMedium();
3635 if (pMedium)
3636 {
3637 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3638 return setError(VBOX_E_OBJECT_IN_USE,
3639 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3640 pMedium->i_getLocationFull().c_str(),
3641 aControllerPort,
3642 aDevice,
3643 aName.c_str());
3644 }
3645 else
3646 return setError(VBOX_E_OBJECT_IN_USE,
3647 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3648 aControllerPort, aDevice, aName.c_str());
3649 }
3650
3651 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3652 if (aMedium && medium.isNull())
3653 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3654
3655 AutoCaller mediumCaller(medium);
3656 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3657
3658 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3659
3660 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3661 && !medium.isNull()
3662 )
3663 return setError(VBOX_E_OBJECT_IN_USE,
3664 tr("Medium '%s' is already attached to this virtual machine"),
3665 medium->i_getLocationFull().c_str());
3666
3667 if (!medium.isNull())
3668 {
3669 MediumType_T mtype = medium->i_getType();
3670 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3671 // For DVDs it's not written to the config file, so needs no global config
3672 // version bump. For floppies it's a new attribute "type", which is ignored
3673 // by older VirtualBox version, so needs no global config version bump either.
3674 // For hard disks this type is not accepted.
3675 if (mtype == MediumType_MultiAttach)
3676 {
3677 // This type is new with VirtualBox 4.0 and therefore requires settings
3678 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3679 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3680 // two reasons: The medium type is a property of the media registry tree, which
3681 // can reside in the global config file (for pre-4.0 media); we would therefore
3682 // possibly need to bump the global config version. We don't want to do that though
3683 // because that might make downgrading to pre-4.0 impossible.
3684 // As a result, we can only use these two new types if the medium is NOT in the
3685 // global registry:
3686 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3687 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3688 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3689 )
3690 return setError(VBOX_E_INVALID_OBJECT_STATE,
3691 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3692 "to machines that were created with VirtualBox 4.0 or later"),
3693 medium->i_getLocationFull().c_str());
3694 }
3695 }
3696
3697 bool fIndirect = false;
3698 if (!medium.isNull())
3699 fIndirect = medium->i_isReadOnly();
3700 bool associate = true;
3701
3702 do
3703 {
3704 if ( aType == DeviceType_HardDisk
3705 && mMediumAttachments.isBackedUp())
3706 {
3707 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3708
3709 /* check if the medium was attached to the VM before we started
3710 * changing attachments in which case the attachment just needs to
3711 * be restored */
3712 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3713 {
3714 AssertReturn(!fIndirect, E_FAIL);
3715
3716 /* see if it's the same bus/channel/device */
3717 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3718 {
3719 /* the simplest case: restore the whole attachment
3720 * and return, nothing else to do */
3721 mMediumAttachments->push_back(pAttachTemp);
3722
3723 /* Reattach the medium to the VM. */
3724 if (fHotplug || fSilent)
3725 {
3726 mediumLock.release();
3727 treeLock.release();
3728 alock.release();
3729
3730 MediumLockList *pMediumLockList(new MediumLockList());
3731
3732 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3733 medium /* pToLockWrite */,
3734 false /* fMediumLockWriteAll */,
3735 NULL,
3736 *pMediumLockList);
3737 alock.acquire();
3738 if (FAILED(rc))
3739 delete pMediumLockList;
3740 else
3741 {
3742 mData->mSession.mLockedMedia.Unlock();
3743 alock.release();
3744 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3745 mData->mSession.mLockedMedia.Lock();
3746 alock.acquire();
3747 }
3748 alock.release();
3749
3750 if (SUCCEEDED(rc))
3751 {
3752 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3753 /* Remove lock list in case of error. */
3754 if (FAILED(rc))
3755 {
3756 mData->mSession.mLockedMedia.Unlock();
3757 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3758 mData->mSession.mLockedMedia.Lock();
3759 }
3760 }
3761 }
3762
3763 return S_OK;
3764 }
3765
3766 /* bus/channel/device differ; we need a new attachment object,
3767 * but don't try to associate it again */
3768 associate = false;
3769 break;
3770 }
3771 }
3772
3773 /* go further only if the attachment is to be indirect */
3774 if (!fIndirect)
3775 break;
3776
3777 /* perform the so called smart attachment logic for indirect
3778 * attachments. Note that smart attachment is only applicable to base
3779 * hard disks. */
3780
3781 if (medium->i_getParent().isNull())
3782 {
3783 /* first, investigate the backup copy of the current hard disk
3784 * attachments to make it possible to re-attach existing diffs to
3785 * another device slot w/o losing their contents */
3786 if (mMediumAttachments.isBackedUp())
3787 {
3788 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3789
3790 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3791 uint32_t foundLevel = 0;
3792
3793 for (MediumAttachmentList::const_iterator
3794 it = oldAtts.begin();
3795 it != oldAtts.end();
3796 ++it)
3797 {
3798 uint32_t level = 0;
3799 MediumAttachment *pAttach = *it;
3800 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3801 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3802 if (pMedium.isNull())
3803 continue;
3804
3805 if (pMedium->i_getBase(&level) == medium)
3806 {
3807 /* skip the hard disk if its currently attached (we
3808 * cannot attach the same hard disk twice) */
3809 if (i_findAttachment(*mMediumAttachments.data(),
3810 pMedium))
3811 continue;
3812
3813 /* matched device, channel and bus (i.e. attached to the
3814 * same place) will win and immediately stop the search;
3815 * otherwise the attachment that has the youngest
3816 * descendant of medium will be used
3817 */
3818 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3819 {
3820 /* the simplest case: restore the whole attachment
3821 * and return, nothing else to do */
3822 mMediumAttachments->push_back(*it);
3823
3824 /* Reattach the medium to the VM. */
3825 if (fHotplug || fSilent)
3826 {
3827 mediumLock.release();
3828 treeLock.release();
3829 alock.release();
3830
3831 MediumLockList *pMediumLockList(new MediumLockList());
3832
3833 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3834 medium /* pToLockWrite */,
3835 false /* fMediumLockWriteAll */,
3836 NULL,
3837 *pMediumLockList);
3838 alock.acquire();
3839 if (FAILED(rc))
3840 delete pMediumLockList;
3841 else
3842 {
3843 mData->mSession.mLockedMedia.Unlock();
3844 alock.release();
3845 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3846 mData->mSession.mLockedMedia.Lock();
3847 alock.acquire();
3848 }
3849 alock.release();
3850
3851 if (SUCCEEDED(rc))
3852 {
3853 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3854 /* Remove lock list in case of error. */
3855 if (FAILED(rc))
3856 {
3857 mData->mSession.mLockedMedia.Unlock();
3858 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3859 mData->mSession.mLockedMedia.Lock();
3860 }
3861 }
3862 }
3863
3864 return S_OK;
3865 }
3866 else if ( foundIt == oldAtts.end()
3867 || level > foundLevel /* prefer younger */
3868 )
3869 {
3870 foundIt = it;
3871 foundLevel = level;
3872 }
3873 }
3874 }
3875
3876 if (foundIt != oldAtts.end())
3877 {
3878 /* use the previously attached hard disk */
3879 medium = (*foundIt)->i_getMedium();
3880 mediumCaller.attach(medium);
3881 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3882 mediumLock.attach(medium);
3883 /* not implicit, doesn't require association with this VM */
3884 fIndirect = false;
3885 associate = false;
3886 /* go right to the MediumAttachment creation */
3887 break;
3888 }
3889 }
3890
3891 /* must give up the medium lock and medium tree lock as below we
3892 * go over snapshots, which needs a lock with higher lock order. */
3893 mediumLock.release();
3894 treeLock.release();
3895
3896 /* then, search through snapshots for the best diff in the given
3897 * hard disk's chain to base the new diff on */
3898
3899 ComObjPtr<Medium> base;
3900 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3901 while (snap)
3902 {
3903 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3904
3905 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3906
3907 MediumAttachment *pAttachFound = NULL;
3908 uint32_t foundLevel = 0;
3909
3910 for (MediumAttachmentList::const_iterator
3911 it = snapAtts.begin();
3912 it != snapAtts.end();
3913 ++it)
3914 {
3915 MediumAttachment *pAttach = *it;
3916 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3917 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3918 if (pMedium.isNull())
3919 continue;
3920
3921 uint32_t level = 0;
3922 if (pMedium->i_getBase(&level) == medium)
3923 {
3924 /* matched device, channel and bus (i.e. attached to the
3925 * same place) will win and immediately stop the search;
3926 * otherwise the attachment that has the youngest
3927 * descendant of medium will be used
3928 */
3929 if ( pAttach->i_getDevice() == aDevice
3930 && pAttach->i_getPort() == aControllerPort
3931 && pAttach->i_getControllerName() == aName
3932 )
3933 {
3934 pAttachFound = pAttach;
3935 break;
3936 }
3937 else if ( !pAttachFound
3938 || level > foundLevel /* prefer younger */
3939 )
3940 {
3941 pAttachFound = pAttach;
3942 foundLevel = level;
3943 }
3944 }
3945 }
3946
3947 if (pAttachFound)
3948 {
3949 base = pAttachFound->i_getMedium();
3950 break;
3951 }
3952
3953 snap = snap->i_getParent();
3954 }
3955
3956 /* re-lock medium tree and the medium, as we need it below */
3957 treeLock.acquire();
3958 mediumLock.acquire();
3959
3960 /* found a suitable diff, use it as a base */
3961 if (!base.isNull())
3962 {
3963 medium = base;
3964 mediumCaller.attach(medium);
3965 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3966 mediumLock.attach(medium);
3967 }
3968 }
3969
3970 Utf8Str strFullSnapshotFolder;
3971 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3972
3973 ComObjPtr<Medium> diff;
3974 diff.createObject();
3975 // store this diff in the same registry as the parent
3976 Guid uuidRegistryParent;
3977 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3978 {
3979 // parent image has no registry: this can happen if we're attaching a new immutable
3980 // image that has not yet been attached (medium then points to the base and we're
3981 // creating the diff image for the immutable, and the parent is not yet registered);
3982 // put the parent in the machine registry then
3983 mediumLock.release();
3984 treeLock.release();
3985 alock.release();
3986 i_addMediumToRegistry(medium);
3987 alock.acquire();
3988 treeLock.acquire();
3989 mediumLock.acquire();
3990 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3991 }
3992 rc = diff->init(mParent,
3993 medium->i_getPreferredDiffFormat(),
3994 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3995 uuidRegistryParent,
3996 DeviceType_HardDisk);
3997 if (FAILED(rc)) return rc;
3998
3999 /* Apply the normal locking logic to the entire chain. */
4000 MediumLockList *pMediumLockList(new MediumLockList());
4001 mediumLock.release();
4002 treeLock.release();
4003 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4004 diff /* pToLockWrite */,
4005 false /* fMediumLockWriteAll */,
4006 medium,
4007 *pMediumLockList);
4008 treeLock.acquire();
4009 mediumLock.acquire();
4010 if (SUCCEEDED(rc))
4011 {
4012 mediumLock.release();
4013 treeLock.release();
4014 rc = pMediumLockList->Lock();
4015 treeLock.acquire();
4016 mediumLock.acquire();
4017 if (FAILED(rc))
4018 setError(rc,
4019 tr("Could not lock medium when creating diff '%s'"),
4020 diff->i_getLocationFull().c_str());
4021 else
4022 {
4023 /* will release the lock before the potentially lengthy
4024 * operation, so protect with the special state */
4025 MachineState_T oldState = mData->mMachineState;
4026 i_setMachineState(MachineState_SettingUp);
4027
4028 mediumLock.release();
4029 treeLock.release();
4030 alock.release();
4031
4032 rc = medium->i_createDiffStorage(diff,
4033 medium->i_getPreferredDiffVariant(),
4034 pMediumLockList,
4035 NULL /* aProgress */,
4036 true /* aWait */,
4037 false /* aNotify */);
4038
4039 alock.acquire();
4040 treeLock.acquire();
4041 mediumLock.acquire();
4042
4043 i_setMachineState(oldState);
4044 }
4045 }
4046
4047 /* Unlock the media and free the associated memory. */
4048 delete pMediumLockList;
4049
4050 if (FAILED(rc)) return rc;
4051
4052 /* use the created diff for the actual attachment */
4053 medium = diff;
4054 mediumCaller.attach(medium);
4055 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4056 mediumLock.attach(medium);
4057 }
4058 while (0);
4059
4060 ComObjPtr<MediumAttachment> attachment;
4061 attachment.createObject();
4062 rc = attachment->init(this,
4063 medium,
4064 aName,
4065 aControllerPort,
4066 aDevice,
4067 aType,
4068 fIndirect,
4069 false /* fPassthrough */,
4070 false /* fTempEject */,
4071 false /* fNonRotational */,
4072 false /* fDiscard */,
4073 fHotplug /* fHotPluggable */,
4074 Utf8Str::Empty);
4075 if (FAILED(rc)) return rc;
4076
4077 if (associate && !medium.isNull())
4078 {
4079 // as the last step, associate the medium to the VM
4080 rc = medium->i_addBackReference(mData->mUuid);
4081 // here we can fail because of Deleting, or being in process of creating a Diff
4082 if (FAILED(rc)) return rc;
4083
4084 mediumLock.release();
4085 treeLock.release();
4086 alock.release();
4087 i_addMediumToRegistry(medium);
4088 alock.acquire();
4089 treeLock.acquire();
4090 mediumLock.acquire();
4091 }
4092
4093 /* success: finally remember the attachment */
4094 i_setModified(IsModified_Storage);
4095 mMediumAttachments.backup();
4096 mMediumAttachments->push_back(attachment);
4097
4098 mediumLock.release();
4099 treeLock.release();
4100 alock.release();
4101
4102 if (fHotplug || fSilent)
4103 {
4104 if (!medium.isNull())
4105 {
4106 MediumLockList *pMediumLockList(new MediumLockList());
4107
4108 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4109 medium /* pToLockWrite */,
4110 false /* fMediumLockWriteAll */,
4111 NULL,
4112 *pMediumLockList);
4113 alock.acquire();
4114 if (FAILED(rc))
4115 delete pMediumLockList;
4116 else
4117 {
4118 mData->mSession.mLockedMedia.Unlock();
4119 alock.release();
4120 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4121 mData->mSession.mLockedMedia.Lock();
4122 alock.acquire();
4123 }
4124 alock.release();
4125 }
4126
4127 if (SUCCEEDED(rc))
4128 {
4129 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4130 /* Remove lock list in case of error. */
4131 if (FAILED(rc))
4132 {
4133 mData->mSession.mLockedMedia.Unlock();
4134 mData->mSession.mLockedMedia.Remove(attachment);
4135 mData->mSession.mLockedMedia.Lock();
4136 }
4137 }
4138 }
4139
4140 /* Save modified registries, but skip this machine as it's the caller's
4141 * job to save its settings like all other settings changes. */
4142 mParent->i_unmarkRegistryModified(i_getId());
4143 mParent->i_saveModifiedRegistries();
4144
4145 if (SUCCEEDED(rc))
4146 {
4147 if (fIndirect && medium != aM)
4148 mParent->i_onMediumConfigChanged(medium);
4149 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4150 }
4151
4152 return rc;
4153}
4154
4155HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4156 LONG aDevice)
4157{
4158 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4159 aName.c_str(), aControllerPort, aDevice));
4160
4161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4162
4163 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4164 if (FAILED(rc)) return rc;
4165
4166 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4167
4168 /* Check for an existing controller. */
4169 ComObjPtr<StorageController> ctl;
4170 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4171 if (FAILED(rc)) return rc;
4172
4173 StorageControllerType_T ctrlType;
4174 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4175 if (FAILED(rc))
4176 return setError(E_FAIL,
4177 tr("Could not get type of controller '%s'"),
4178 aName.c_str());
4179
4180 bool fSilent = false;
4181 Utf8Str strReconfig;
4182
4183 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4184 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4185 if ( mData->mMachineState == MachineState_Paused
4186 && strReconfig == "1")
4187 fSilent = true;
4188
4189 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4190 bool fHotplug = false;
4191 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4192 fHotplug = true;
4193
4194 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4195 return setError(VBOX_E_INVALID_VM_STATE,
4196 tr("Controller '%s' does not support hotplugging"),
4197 aName.c_str());
4198
4199 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4200 aName,
4201 aControllerPort,
4202 aDevice);
4203 if (!pAttach)
4204 return setError(VBOX_E_OBJECT_NOT_FOUND,
4205 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4206 aDevice, aControllerPort, aName.c_str());
4207
4208 if (fHotplug && !pAttach->i_getHotPluggable())
4209 return setError(VBOX_E_NOT_SUPPORTED,
4210 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4211 aDevice, aControllerPort, aName.c_str());
4212
4213 /*
4214 * The VM has to detach the device before we delete any implicit diffs.
4215 * If this fails we can roll back without loosing data.
4216 */
4217 if (fHotplug || fSilent)
4218 {
4219 alock.release();
4220 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4221 alock.acquire();
4222 }
4223 if (FAILED(rc)) return rc;
4224
4225 /* If we are here everything went well and we can delete the implicit now. */
4226 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4227
4228 alock.release();
4229
4230 /* Save modified registries, but skip this machine as it's the caller's
4231 * job to save its settings like all other settings changes. */
4232 mParent->i_unmarkRegistryModified(i_getId());
4233 mParent->i_saveModifiedRegistries();
4234
4235 if (SUCCEEDED(rc))
4236 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4237
4238 return rc;
4239}
4240
4241HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4242 LONG aDevice, BOOL aPassthrough)
4243{
4244 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4245 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4246
4247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4248
4249 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4250 if (FAILED(rc)) return rc;
4251
4252 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4253
4254 /* Check for an existing controller. */
4255 ComObjPtr<StorageController> ctl;
4256 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4257 if (FAILED(rc)) return rc;
4258
4259 StorageControllerType_T ctrlType;
4260 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4261 if (FAILED(rc))
4262 return setError(E_FAIL,
4263 tr("Could not get type of controller '%s'"),
4264 aName.c_str());
4265
4266 bool fSilent = false;
4267 Utf8Str strReconfig;
4268
4269 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4270 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4271 if ( mData->mMachineState == MachineState_Paused
4272 && strReconfig == "1")
4273 fSilent = true;
4274
4275 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4276 bool fHotplug = false;
4277 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4278 fHotplug = true;
4279
4280 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4281 return setError(VBOX_E_INVALID_VM_STATE,
4282 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4283 aName.c_str());
4284
4285 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4286 aName,
4287 aControllerPort,
4288 aDevice);
4289 if (!pAttach)
4290 return setError(VBOX_E_OBJECT_NOT_FOUND,
4291 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4292 aDevice, aControllerPort, aName.c_str());
4293
4294
4295 i_setModified(IsModified_Storage);
4296 mMediumAttachments.backup();
4297
4298 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4299
4300 if (pAttach->i_getType() != DeviceType_DVD)
4301 return setError(E_INVALIDARG,
4302 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4303 aDevice, aControllerPort, aName.c_str());
4304
4305 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4306
4307 pAttach->i_updatePassthrough(!!aPassthrough);
4308
4309 attLock.release();
4310 alock.release();
4311 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4312 if (SUCCEEDED(rc) && fValueChanged)
4313 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4314
4315 return rc;
4316}
4317
4318HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4319 LONG aDevice, BOOL aTemporaryEject)
4320{
4321
4322 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4323 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4324
4325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4326
4327 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4328 if (FAILED(rc)) return rc;
4329
4330 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4331 aName,
4332 aControllerPort,
4333 aDevice);
4334 if (!pAttach)
4335 return setError(VBOX_E_OBJECT_NOT_FOUND,
4336 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4337 aDevice, aControllerPort, aName.c_str());
4338
4339
4340 i_setModified(IsModified_Storage);
4341 mMediumAttachments.backup();
4342
4343 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4344
4345 if (pAttach->i_getType() != DeviceType_DVD)
4346 return setError(E_INVALIDARG,
4347 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4348 aDevice, aControllerPort, aName.c_str());
4349 pAttach->i_updateTempEject(!!aTemporaryEject);
4350
4351 return S_OK;
4352}
4353
4354HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4355 LONG aDevice, BOOL aNonRotational)
4356{
4357
4358 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4359 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4360
4361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4362
4363 HRESULT rc = i_checkStateDependency(MutableStateDep);
4364 if (FAILED(rc)) return rc;
4365
4366 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4367
4368 if (Global::IsOnlineOrTransient(mData->mMachineState))
4369 return setError(VBOX_E_INVALID_VM_STATE,
4370 tr("Invalid machine state: %s"),
4371 Global::stringifyMachineState(mData->mMachineState));
4372
4373 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4374 aName,
4375 aControllerPort,
4376 aDevice);
4377 if (!pAttach)
4378 return setError(VBOX_E_OBJECT_NOT_FOUND,
4379 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4380 aDevice, aControllerPort, aName.c_str());
4381
4382
4383 i_setModified(IsModified_Storage);
4384 mMediumAttachments.backup();
4385
4386 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4387
4388 if (pAttach->i_getType() != DeviceType_HardDisk)
4389 return setError(E_INVALIDARG,
4390 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"),
4391 aDevice, aControllerPort, aName.c_str());
4392 pAttach->i_updateNonRotational(!!aNonRotational);
4393
4394 return S_OK;
4395}
4396
4397HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4398 LONG aDevice, BOOL aDiscard)
4399{
4400
4401 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4402 aName.c_str(), aControllerPort, aDevice, aDiscard));
4403
4404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4405
4406 HRESULT rc = i_checkStateDependency(MutableStateDep);
4407 if (FAILED(rc)) return rc;
4408
4409 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4410
4411 if (Global::IsOnlineOrTransient(mData->mMachineState))
4412 return setError(VBOX_E_INVALID_VM_STATE,
4413 tr("Invalid machine state: %s"),
4414 Global::stringifyMachineState(mData->mMachineState));
4415
4416 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4417 aName,
4418 aControllerPort,
4419 aDevice);
4420 if (!pAttach)
4421 return setError(VBOX_E_OBJECT_NOT_FOUND,
4422 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4423 aDevice, aControllerPort, aName.c_str());
4424
4425
4426 i_setModified(IsModified_Storage);
4427 mMediumAttachments.backup();
4428
4429 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4430
4431 if (pAttach->i_getType() != DeviceType_HardDisk)
4432 return setError(E_INVALIDARG,
4433 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"),
4434 aDevice, aControllerPort, aName.c_str());
4435 pAttach->i_updateDiscard(!!aDiscard);
4436
4437 return S_OK;
4438}
4439
4440HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4441 LONG aDevice, BOOL aHotPluggable)
4442{
4443 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4444 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4445
4446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4447
4448 HRESULT rc = i_checkStateDependency(MutableStateDep);
4449 if (FAILED(rc)) return rc;
4450
4451 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4452
4453 if (Global::IsOnlineOrTransient(mData->mMachineState))
4454 return setError(VBOX_E_INVALID_VM_STATE,
4455 tr("Invalid machine state: %s"),
4456 Global::stringifyMachineState(mData->mMachineState));
4457
4458 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4459 aName,
4460 aControllerPort,
4461 aDevice);
4462 if (!pAttach)
4463 return setError(VBOX_E_OBJECT_NOT_FOUND,
4464 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4465 aDevice, aControllerPort, aName.c_str());
4466
4467 /* Check for an existing controller. */
4468 ComObjPtr<StorageController> ctl;
4469 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4470 if (FAILED(rc)) return rc;
4471
4472 StorageControllerType_T ctrlType;
4473 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4474 if (FAILED(rc))
4475 return setError(E_FAIL,
4476 tr("Could not get type of controller '%s'"),
4477 aName.c_str());
4478
4479 if (!i_isControllerHotplugCapable(ctrlType))
4480 return setError(VBOX_E_NOT_SUPPORTED,
4481 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4482 aName.c_str());
4483
4484 i_setModified(IsModified_Storage);
4485 mMediumAttachments.backup();
4486
4487 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4488
4489 if (pAttach->i_getType() == DeviceType_Floppy)
4490 return setError(E_INVALIDARG,
4491 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"),
4492 aDevice, aControllerPort, aName.c_str());
4493 pAttach->i_updateHotPluggable(!!aHotPluggable);
4494
4495 return S_OK;
4496}
4497
4498HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4499 LONG aDevice)
4500{
4501 int rc = S_OK;
4502 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4503 aName.c_str(), aControllerPort, aDevice));
4504
4505 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4506
4507 return rc;
4508}
4509
4510HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4511 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4512{
4513 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4514 aName.c_str(), aControllerPort, aDevice));
4515
4516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4517
4518 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4519 if (FAILED(rc)) return rc;
4520
4521 if (Global::IsOnlineOrTransient(mData->mMachineState))
4522 return setError(VBOX_E_INVALID_VM_STATE,
4523 tr("Invalid machine state: %s"),
4524 Global::stringifyMachineState(mData->mMachineState));
4525
4526 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4527 aName,
4528 aControllerPort,
4529 aDevice);
4530 if (!pAttach)
4531 return setError(VBOX_E_OBJECT_NOT_FOUND,
4532 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4533 aDevice, aControllerPort, aName.c_str());
4534
4535
4536 i_setModified(IsModified_Storage);
4537 mMediumAttachments.backup();
4538
4539 IBandwidthGroup *iB = aBandwidthGroup;
4540 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4541 if (aBandwidthGroup && group.isNull())
4542 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4543
4544 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4545
4546 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4547 if (strBandwidthGroupOld.isNotEmpty())
4548 {
4549 /* Get the bandwidth group object and release it - this must not fail. */
4550 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4551 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4552 Assert(SUCCEEDED(rc));
4553
4554 pBandwidthGroupOld->i_release();
4555 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4556 }
4557
4558 if (!group.isNull())
4559 {
4560 group->i_reference();
4561 pAttach->i_updateBandwidthGroup(group->i_getName());
4562 }
4563
4564 return S_OK;
4565}
4566
4567HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4568 LONG aControllerPort,
4569 LONG aDevice,
4570 DeviceType_T aType)
4571{
4572 HRESULT rc = S_OK;
4573
4574 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4575 aName.c_str(), aControllerPort, aDevice, aType));
4576
4577 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4578
4579 return rc;
4580}
4581
4582
4583HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4584 LONG aControllerPort,
4585 LONG aDevice,
4586 BOOL aForce)
4587{
4588 int rc = S_OK;
4589 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4590 aName.c_str(), aControllerPort, aForce));
4591
4592 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4593
4594 return rc;
4595}
4596
4597HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4598 LONG aControllerPort,
4599 LONG aDevice,
4600 const ComPtr<IMedium> &aMedium,
4601 BOOL aForce)
4602{
4603 int rc = S_OK;
4604 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4605 aName.c_str(), aControllerPort, aDevice, aForce));
4606
4607 // request the host lock first, since might be calling Host methods for getting host drives;
4608 // next, protect the media tree all the while we're in here, as well as our member variables
4609 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4610 this->lockHandle(),
4611 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4612
4613 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4614 aName,
4615 aControllerPort,
4616 aDevice);
4617 if (pAttach.isNull())
4618 return setError(VBOX_E_OBJECT_NOT_FOUND,
4619 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4620 aDevice, aControllerPort, aName.c_str());
4621
4622 /* Remember previously mounted medium. The medium before taking the
4623 * backup is not necessarily the same thing. */
4624 ComObjPtr<Medium> oldmedium;
4625 oldmedium = pAttach->i_getMedium();
4626
4627 IMedium *iM = aMedium;
4628 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4629 if (aMedium && pMedium.isNull())
4630 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4631
4632 AutoCaller mediumCaller(pMedium);
4633 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4634
4635 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4636 if (pMedium)
4637 {
4638 DeviceType_T mediumType = pAttach->i_getType();
4639 switch (mediumType)
4640 {
4641 case DeviceType_DVD:
4642 case DeviceType_Floppy:
4643 break;
4644
4645 default:
4646 return setError(VBOX_E_INVALID_OBJECT_STATE,
4647 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4648 aControllerPort,
4649 aDevice,
4650 aName.c_str());
4651 }
4652 }
4653
4654 i_setModified(IsModified_Storage);
4655 mMediumAttachments.backup();
4656
4657 {
4658 // The backup operation makes the pAttach reference point to the
4659 // old settings. Re-get the correct reference.
4660 pAttach = i_findAttachment(*mMediumAttachments.data(),
4661 aName,
4662 aControllerPort,
4663 aDevice);
4664 if (!oldmedium.isNull())
4665 oldmedium->i_removeBackReference(mData->mUuid);
4666 if (!pMedium.isNull())
4667 {
4668 pMedium->i_addBackReference(mData->mUuid);
4669
4670 mediumLock.release();
4671 multiLock.release();
4672 i_addMediumToRegistry(pMedium);
4673 multiLock.acquire();
4674 mediumLock.acquire();
4675 }
4676
4677 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4678 pAttach->i_updateMedium(pMedium);
4679 }
4680
4681 i_setModified(IsModified_Storage);
4682
4683 mediumLock.release();
4684 multiLock.release();
4685 rc = i_onMediumChange(pAttach, aForce);
4686 multiLock.acquire();
4687 mediumLock.acquire();
4688
4689 /* On error roll back this change only. */
4690 if (FAILED(rc))
4691 {
4692 if (!pMedium.isNull())
4693 pMedium->i_removeBackReference(mData->mUuid);
4694 pAttach = i_findAttachment(*mMediumAttachments.data(),
4695 aName,
4696 aControllerPort,
4697 aDevice);
4698 /* If the attachment is gone in the meantime, bail out. */
4699 if (pAttach.isNull())
4700 return rc;
4701 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4702 if (!oldmedium.isNull())
4703 oldmedium->i_addBackReference(mData->mUuid);
4704 pAttach->i_updateMedium(oldmedium);
4705 }
4706
4707 mediumLock.release();
4708 multiLock.release();
4709
4710 /* Save modified registries, but skip this machine as it's the caller's
4711 * job to save its settings like all other settings changes. */
4712 mParent->i_unmarkRegistryModified(i_getId());
4713 mParent->i_saveModifiedRegistries();
4714
4715 return rc;
4716}
4717HRESULT Machine::getMedium(const com::Utf8Str &aName,
4718 LONG aControllerPort,
4719 LONG aDevice,
4720 ComPtr<IMedium> &aMedium)
4721{
4722 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4723 aName.c_str(), aControllerPort, aDevice));
4724
4725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4726
4727 aMedium = NULL;
4728
4729 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4730 aName,
4731 aControllerPort,
4732 aDevice);
4733 if (pAttach.isNull())
4734 return setError(VBOX_E_OBJECT_NOT_FOUND,
4735 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4736 aDevice, aControllerPort, aName.c_str());
4737
4738 aMedium = pAttach->i_getMedium();
4739
4740 return S_OK;
4741}
4742
4743HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4744{
4745
4746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4747
4748 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4749
4750 return S_OK;
4751}
4752
4753HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4754{
4755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4756
4757 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4758
4759 return S_OK;
4760}
4761
4762HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4763{
4764 /* Do not assert if slot is out of range, just return the advertised
4765 status. testdriver/vbox.py triggers this in logVmInfo. */
4766 if (aSlot >= mNetworkAdapters.size())
4767 return setError(E_INVALIDARG,
4768 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4769 aSlot, mNetworkAdapters.size());
4770
4771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4772
4773 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4774
4775 return S_OK;
4776}
4777
4778HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4779{
4780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4781
4782 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4783 size_t i = 0;
4784 for (settings::StringsMap::const_iterator
4785 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4786 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4787 ++it, ++i)
4788 aKeys[i] = it->first;
4789
4790 return S_OK;
4791}
4792
4793 /**
4794 * @note Locks this object for reading.
4795 */
4796HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4797 com::Utf8Str &aValue)
4798{
4799 /* start with nothing found */
4800 aValue = "";
4801
4802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4803
4804 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4805 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4806 // found:
4807 aValue = it->second; // source is a Utf8Str
4808
4809 /* return the result to caller (may be empty) */
4810 return S_OK;
4811}
4812
4813 /**
4814 * @note Locks mParent for writing + this object for writing.
4815 */
4816HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4817{
4818 /* Because control characters in aKey have caused problems in the settings
4819 * they are rejected unless the key should be deleted. */
4820 if (!aValue.isEmpty())
4821 {
4822 for (size_t i = 0; i < aKey.length(); ++i)
4823 {
4824 char ch = aKey[i];
4825 if (RTLocCIsCntrl(ch))
4826 return E_INVALIDARG;
4827 }
4828 }
4829
4830 Utf8Str strOldValue; // empty
4831
4832 // locking note: we only hold the read lock briefly to look up the old value,
4833 // then release it and call the onExtraCanChange callbacks. There is a small
4834 // chance of a race insofar as the callback might be called twice if two callers
4835 // change the same key at the same time, but that's a much better solution
4836 // than the deadlock we had here before. The actual changing of the extradata
4837 // is then performed under the write lock and race-free.
4838
4839 // look up the old value first; if nothing has changed then we need not do anything
4840 {
4841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4842
4843 // For snapshots don't even think about allowing changes, extradata
4844 // is global for a machine, so there is nothing snapshot specific.
4845 if (i_isSnapshotMachine())
4846 return setError(VBOX_E_INVALID_VM_STATE,
4847 tr("Cannot set extradata for a snapshot"));
4848
4849 // check if the right IMachine instance is used
4850 if (mData->mRegistered && !i_isSessionMachine())
4851 return setError(VBOX_E_INVALID_VM_STATE,
4852 tr("Cannot set extradata for an immutable machine"));
4853
4854 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4855 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4856 strOldValue = it->second;
4857 }
4858
4859 bool fChanged;
4860 if ((fChanged = (strOldValue != aValue)))
4861 {
4862 // ask for permission from all listeners outside the locks;
4863 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4864 // lock to copy the list of callbacks to invoke
4865 Bstr error;
4866 Bstr bstrValue(aValue);
4867
4868 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4869 {
4870 const char *sep = error.isEmpty() ? "" : ": ";
4871 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4872 return setError(E_ACCESSDENIED,
4873 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4874 aKey.c_str(),
4875 aValue.c_str(),
4876 sep,
4877 error.raw());
4878 }
4879
4880 // data is changing and change not vetoed: then write it out under the lock
4881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4882
4883 if (aValue.isEmpty())
4884 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4885 else
4886 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4887 // creates a new key if needed
4888
4889 bool fNeedsGlobalSaveSettings = false;
4890 // This saving of settings is tricky: there is no "old state" for the
4891 // extradata items at all (unlike all other settings), so the old/new
4892 // settings comparison would give a wrong result!
4893 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4894
4895 if (fNeedsGlobalSaveSettings)
4896 {
4897 // save the global settings; for that we should hold only the VirtualBox lock
4898 alock.release();
4899 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4900 mParent->i_saveSettings();
4901 }
4902 }
4903
4904 // fire notification outside the lock
4905 if (fChanged)
4906 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4907
4908 return S_OK;
4909}
4910
4911HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4912{
4913 aProgress = NULL;
4914 NOREF(aSettingsFilePath);
4915 ReturnComNotImplemented();
4916}
4917
4918HRESULT Machine::saveSettings()
4919{
4920 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4921
4922 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4923 if (FAILED(rc)) return rc;
4924
4925 /* the settings file path may never be null */
4926 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4927
4928 /* save all VM data excluding snapshots */
4929 bool fNeedsGlobalSaveSettings = false;
4930 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4931 mlock.release();
4932
4933 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4934 {
4935 // save the global settings; for that we should hold only the VirtualBox lock
4936 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4937 rc = mParent->i_saveSettings();
4938 }
4939
4940 return rc;
4941}
4942
4943
4944HRESULT Machine::discardSettings()
4945{
4946 /*
4947 * We need to take the machine list lock here as well as the machine one
4948 * or we'll get into trouble should any media stuff require rolling back.
4949 *
4950 * Details:
4951 *
4952 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4953 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4954 * 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]
4955 * 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
4956 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4957 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4958 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4959 * 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
4960 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4961 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4962 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4963 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4964 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4965 * 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]
4966 * 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] (*)
4967 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4968 * 0:005> k
4969 * # Child-SP RetAddr Call Site
4970 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4971 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4972 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4973 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4974 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4975 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4976 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4977 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4978 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4979 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4980 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4981 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4982 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4983 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4984 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4985 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4986 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4987 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4988 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4989 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4990 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4991 *
4992 */
4993 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4995
4996 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4997 if (FAILED(rc)) return rc;
4998
4999 /*
5000 * during this rollback, the session will be notified if data has
5001 * been actually changed
5002 */
5003 i_rollback(true /* aNotify */);
5004
5005 return S_OK;
5006}
5007
5008/** @note Locks objects! */
5009HRESULT Machine::unregister(AutoCaller &autoCaller,
5010 CleanupMode_T aCleanupMode,
5011 std::vector<ComPtr<IMedium> > &aMedia)
5012{
5013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5014
5015 Guid id(i_getId());
5016
5017 if (mData->mSession.mState != SessionState_Unlocked)
5018 return setError(VBOX_E_INVALID_OBJECT_STATE,
5019 tr("Cannot unregister the machine '%s' while it is locked"),
5020 mUserData->s.strName.c_str());
5021
5022 // wait for state dependents to drop to zero
5023 i_ensureNoStateDependencies();
5024
5025 if (!mData->mAccessible)
5026 {
5027 // inaccessible machines can only be unregistered; uninitialize ourselves
5028 // here because currently there may be no unregistered that are inaccessible
5029 // (this state combination is not supported). Note releasing the caller and
5030 // leaving the lock before calling uninit()
5031 alock.release();
5032 autoCaller.release();
5033
5034 uninit();
5035
5036 mParent->i_unregisterMachine(this, id);
5037 // calls VirtualBox::i_saveSettings()
5038
5039 return S_OK;
5040 }
5041
5042 HRESULT rc = S_OK;
5043 mData->llFilesToDelete.clear();
5044
5045 if (!mSSData->strStateFilePath.isEmpty())
5046 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5047
5048 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
5049 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5050 mData->llFilesToDelete.push_back(strNVRAMFile);
5051
5052 // This list collects the medium objects from all medium attachments
5053 // which we will detach from the machine and its snapshots, in a specific
5054 // order which allows for closing all media without getting "media in use"
5055 // errors, simply by going through the list from the front to the back:
5056 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5057 // and must be closed before the parent media from the snapshots, or closing the parents
5058 // will fail because they still have children);
5059 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5060 // the root ("first") snapshot of the machine.
5061 MediaList llMedia;
5062
5063 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5064 && mMediumAttachments->size()
5065 )
5066 {
5067 // we have media attachments: detach them all and add the Medium objects to our list
5068 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5069 }
5070
5071 if (mData->mFirstSnapshot)
5072 {
5073 // add the media from the medium attachments of the snapshots to llMedia
5074 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5075 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5076 // into the children first
5077
5078 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5079 MachineState_T oldState = mData->mMachineState;
5080 mData->mMachineState = MachineState_DeletingSnapshot;
5081
5082 // make a copy of the first snapshot reference so the refcount does not
5083 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5084 // (would hang due to the AutoCaller voodoo)
5085 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5086
5087 // GO!
5088 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5089
5090 mData->mMachineState = oldState;
5091 }
5092
5093 if (FAILED(rc))
5094 {
5095 i_rollbackMedia();
5096 return rc;
5097 }
5098
5099 // commit all the media changes made above
5100 i_commitMedia();
5101
5102 mData->mRegistered = false;
5103
5104 // machine lock no longer needed
5105 alock.release();
5106
5107 /* Make sure that the settings of the current VM are not saved, because
5108 * they are rather crippled at this point to meet the cleanup expectations
5109 * and there's no point destroying the VM config on disk just because. */
5110 mParent->i_unmarkRegistryModified(id);
5111
5112 // return media to caller
5113 aMedia.resize(llMedia.size());
5114 size_t i = 0;
5115 for (MediaList::const_iterator
5116 it = llMedia.begin();
5117 it != llMedia.end();
5118 ++it, ++i)
5119 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5120
5121 mParent->i_unregisterMachine(this, id);
5122 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5123
5124 return S_OK;
5125}
5126
5127/**
5128 * Task record for deleting a machine config.
5129 */
5130class Machine::DeleteConfigTask
5131 : public Machine::Task
5132{
5133public:
5134 DeleteConfigTask(Machine *m,
5135 Progress *p,
5136 const Utf8Str &t,
5137 const RTCList<ComPtr<IMedium> > &llMediums,
5138 const StringsList &llFilesToDelete)
5139 : Task(m, p, t),
5140 m_llMediums(llMediums),
5141 m_llFilesToDelete(llFilesToDelete)
5142 {}
5143
5144private:
5145 void handler()
5146 {
5147 try
5148 {
5149 m_pMachine->i_deleteConfigHandler(*this);
5150 }
5151 catch (...)
5152 {
5153 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5154 }
5155 }
5156
5157 RTCList<ComPtr<IMedium> > m_llMediums;
5158 StringsList m_llFilesToDelete;
5159
5160 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5161};
5162
5163/**
5164 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5165 * SessionMachine::taskHandler().
5166 *
5167 * @note Locks this object for writing.
5168 *
5169 * @param task
5170 * @return
5171 */
5172void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5173{
5174 LogFlowThisFuncEnter();
5175
5176 AutoCaller autoCaller(this);
5177 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5178 if (FAILED(autoCaller.rc()))
5179 {
5180 /* we might have been uninitialized because the session was accidentally
5181 * closed by the client, so don't assert */
5182 HRESULT rc = setError(E_FAIL,
5183 tr("The session has been accidentally closed"));
5184 task.m_pProgress->i_notifyComplete(rc);
5185 LogFlowThisFuncLeave();
5186 return;
5187 }
5188
5189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5190
5191 HRESULT rc = S_OK;
5192
5193 try
5194 {
5195 ULONG uLogHistoryCount = 3;
5196 ComPtr<ISystemProperties> systemProperties;
5197 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5198 if (FAILED(rc)) throw rc;
5199
5200 if (!systemProperties.isNull())
5201 {
5202 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5203 if (FAILED(rc)) throw rc;
5204 }
5205
5206 MachineState_T oldState = mData->mMachineState;
5207 i_setMachineState(MachineState_SettingUp);
5208 alock.release();
5209 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5210 {
5211 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5212 {
5213 AutoCaller mac(pMedium);
5214 if (FAILED(mac.rc())) throw mac.rc();
5215 Utf8Str strLocation = pMedium->i_getLocationFull();
5216 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5217 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5218 if (FAILED(rc)) throw rc;
5219 }
5220 if (pMedium->i_isMediumFormatFile())
5221 {
5222 ComPtr<IProgress> pProgress2;
5223 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5224 if (FAILED(rc)) throw rc;
5225 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5226 if (FAILED(rc)) throw rc;
5227 }
5228
5229 /* Close the medium, deliberately without checking the return
5230 * code, and without leaving any trace in the error info, as
5231 * a failure here is a very minor issue, which shouldn't happen
5232 * as above we even managed to delete the medium. */
5233 {
5234 ErrorInfoKeeper eik;
5235 pMedium->Close();
5236 }
5237 }
5238 i_setMachineState(oldState);
5239 alock.acquire();
5240
5241 // delete the files pushed on the task list by Machine::Delete()
5242 // (this includes saved states of the machine and snapshots and
5243 // medium storage files from the IMedium list passed in, and the
5244 // machine XML file)
5245 for (StringsList::const_iterator
5246 it = task.m_llFilesToDelete.begin();
5247 it != task.m_llFilesToDelete.end();
5248 ++it)
5249 {
5250 const Utf8Str &strFile = *it;
5251 LogFunc(("Deleting file %s\n", strFile.c_str()));
5252 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5253 if (FAILED(rc)) throw rc;
5254
5255 int vrc = RTFileDelete(strFile.c_str());
5256 if (RT_FAILURE(vrc))
5257 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5258 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5259 }
5260
5261 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5262 if (FAILED(rc)) throw rc;
5263
5264 /* delete the settings only when the file actually exists */
5265 if (mData->pMachineConfigFile->fileExists())
5266 {
5267 /* Delete any backup or uncommitted XML files. Ignore failures.
5268 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5269 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5270 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5271 RTFileDelete(otherXml.c_str());
5272 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5273 RTFileDelete(otherXml.c_str());
5274
5275 /* delete the Logs folder, nothing important should be left
5276 * there (we don't check for errors because the user might have
5277 * some private files there that we don't want to delete) */
5278 Utf8Str logFolder;
5279 getLogFolder(logFolder);
5280 Assert(logFolder.length());
5281 if (RTDirExists(logFolder.c_str()))
5282 {
5283 /* Delete all VBox.log[.N] files from the Logs folder
5284 * (this must be in sync with the rotation logic in
5285 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5286 * files that may have been created by the GUI. */
5287 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5288 logFolder.c_str(), RTPATH_DELIMITER);
5289 RTFileDelete(log.c_str());
5290 log = Utf8StrFmt("%s%cVBox.png",
5291 logFolder.c_str(), RTPATH_DELIMITER);
5292 RTFileDelete(log.c_str());
5293 for (int i = uLogHistoryCount; i > 0; i--)
5294 {
5295 log = Utf8StrFmt("%s%cVBox.log.%d",
5296 logFolder.c_str(), RTPATH_DELIMITER, i);
5297 RTFileDelete(log.c_str());
5298 log = Utf8StrFmt("%s%cVBox.png.%d",
5299 logFolder.c_str(), RTPATH_DELIMITER, i);
5300 RTFileDelete(log.c_str());
5301 }
5302#if defined(RT_OS_WINDOWS)
5303 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5304 RTFileDelete(log.c_str());
5305 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5306 RTFileDelete(log.c_str());
5307#endif
5308
5309 RTDirRemove(logFolder.c_str());
5310 }
5311
5312 /* delete the Snapshots folder, nothing important should be left
5313 * there (we don't check for errors because the user might have
5314 * some private files there that we don't want to delete) */
5315 Utf8Str strFullSnapshotFolder;
5316 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5317 Assert(!strFullSnapshotFolder.isEmpty());
5318 if (RTDirExists(strFullSnapshotFolder.c_str()))
5319 RTDirRemove(strFullSnapshotFolder.c_str());
5320
5321 // delete the directory that contains the settings file, but only
5322 // if it matches the VM name
5323 Utf8Str settingsDir;
5324 if (i_isInOwnDir(&settingsDir))
5325 RTDirRemove(settingsDir.c_str());
5326 }
5327
5328 alock.release();
5329
5330 mParent->i_saveModifiedRegistries();
5331 }
5332 catch (HRESULT aRC) { rc = aRC; }
5333
5334 task.m_pProgress->i_notifyComplete(rc);
5335
5336 LogFlowThisFuncLeave();
5337}
5338
5339HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5340{
5341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5342
5343 HRESULT rc = i_checkStateDependency(MutableStateDep);
5344 if (FAILED(rc)) return rc;
5345
5346 if (mData->mRegistered)
5347 return setError(VBOX_E_INVALID_VM_STATE,
5348 tr("Cannot delete settings of a registered machine"));
5349
5350 // collect files to delete
5351 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5352 // machine config file
5353 if (mData->pMachineConfigFile->fileExists())
5354 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5355 // backup of machine config file
5356 Utf8Str strTmp(mData->m_strConfigFileFull);
5357 strTmp.append("-prev");
5358 if (RTFileExists(strTmp.c_str()))
5359 llFilesToDelete.push_back(strTmp);
5360
5361 RTCList<ComPtr<IMedium> > llMediums;
5362 for (size_t i = 0; i < aMedia.size(); ++i)
5363 {
5364 IMedium *pIMedium(aMedia[i]);
5365 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5366 if (pMedium.isNull())
5367 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5368 SafeArray<BSTR> ids;
5369 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5370 if (FAILED(rc)) return rc;
5371 /* At this point the medium should not have any back references
5372 * anymore. If it has it is attached to another VM and *must* not
5373 * deleted. */
5374 if (ids.size() < 1)
5375 llMediums.append(pMedium);
5376 }
5377
5378 ComObjPtr<Progress> pProgress;
5379 pProgress.createObject();
5380 rc = pProgress->init(i_getVirtualBox(),
5381 static_cast<IMachine*>(this) /* aInitiator */,
5382 tr("Deleting files"),
5383 true /* fCancellable */,
5384 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5385 tr("Collecting file inventory"));
5386 if (FAILED(rc))
5387 return rc;
5388
5389 /* create and start the task on a separate thread (note that it will not
5390 * start working until we release alock) */
5391 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5392 rc = pTask->createThread();
5393 pTask = NULL;
5394 if (FAILED(rc))
5395 return rc;
5396
5397 pProgress.queryInterfaceTo(aProgress.asOutParam());
5398
5399 LogFlowFuncLeave();
5400
5401 return S_OK;
5402}
5403
5404HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5405{
5406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5407
5408 ComObjPtr<Snapshot> pSnapshot;
5409 HRESULT rc;
5410
5411 if (aNameOrId.isEmpty())
5412 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5413 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5414 else
5415 {
5416 Guid uuid(aNameOrId);
5417 if (uuid.isValid())
5418 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5419 else
5420 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5421 }
5422 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5423
5424 return rc;
5425}
5426
5427HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5428 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5429{
5430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5431
5432 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5433 if (FAILED(rc)) return rc;
5434
5435 ComObjPtr<SharedFolder> sharedFolder;
5436 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5437 if (SUCCEEDED(rc))
5438 return setError(VBOX_E_OBJECT_IN_USE,
5439 tr("Shared folder named '%s' already exists"),
5440 aName.c_str());
5441
5442 sharedFolder.createObject();
5443 rc = sharedFolder->init(i_getMachine(),
5444 aName,
5445 aHostPath,
5446 !!aWritable,
5447 !!aAutomount,
5448 aAutoMountPoint,
5449 true /* fFailOnError */);
5450 if (FAILED(rc)) return rc;
5451
5452 i_setModified(IsModified_SharedFolders);
5453 mHWData.backup();
5454 mHWData->mSharedFolders.push_back(sharedFolder);
5455
5456 /* inform the direct session if any */
5457 alock.release();
5458 i_onSharedFolderChange();
5459
5460 return S_OK;
5461}
5462
5463HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5464{
5465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5466
5467 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5468 if (FAILED(rc)) return rc;
5469
5470 ComObjPtr<SharedFolder> sharedFolder;
5471 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5472 if (FAILED(rc)) return rc;
5473
5474 i_setModified(IsModified_SharedFolders);
5475 mHWData.backup();
5476 mHWData->mSharedFolders.remove(sharedFolder);
5477
5478 /* inform the direct session if any */
5479 alock.release();
5480 i_onSharedFolderChange();
5481
5482 return S_OK;
5483}
5484
5485HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5486{
5487 /* start with No */
5488 *aCanShow = FALSE;
5489
5490 ComPtr<IInternalSessionControl> directControl;
5491 {
5492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 if (mData->mSession.mState != SessionState_Locked)
5495 return setError(VBOX_E_INVALID_VM_STATE,
5496 tr("Machine is not locked for session (session state: %s)"),
5497 Global::stringifySessionState(mData->mSession.mState));
5498
5499 if (mData->mSession.mLockType == LockType_VM)
5500 directControl = mData->mSession.mDirectControl;
5501 }
5502
5503 /* ignore calls made after #OnSessionEnd() is called */
5504 if (!directControl)
5505 return S_OK;
5506
5507 LONG64 dummy;
5508 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5509}
5510
5511HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5512{
5513 ComPtr<IInternalSessionControl> directControl;
5514 {
5515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5516
5517 if (mData->mSession.mState != SessionState_Locked)
5518 return setError(E_FAIL,
5519 tr("Machine is not locked for session (session state: %s)"),
5520 Global::stringifySessionState(mData->mSession.mState));
5521
5522 if (mData->mSession.mLockType == LockType_VM)
5523 directControl = mData->mSession.mDirectControl;
5524 }
5525
5526 /* ignore calls made after #OnSessionEnd() is called */
5527 if (!directControl)
5528 return S_OK;
5529
5530 BOOL dummy;
5531 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5532}
5533
5534#ifdef VBOX_WITH_GUEST_PROPS
5535/**
5536 * Look up a guest property in VBoxSVC's internal structures.
5537 */
5538HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5539 com::Utf8Str &aValue,
5540 LONG64 *aTimestamp,
5541 com::Utf8Str &aFlags) const
5542{
5543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5544
5545 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5546 if (it != mHWData->mGuestProperties.end())
5547 {
5548 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5549 aValue = it->second.strValue;
5550 *aTimestamp = it->second.mTimestamp;
5551 GuestPropWriteFlags(it->second.mFlags, szFlags);
5552 aFlags = Utf8Str(szFlags);
5553 }
5554
5555 return S_OK;
5556}
5557
5558/**
5559 * Query the VM that a guest property belongs to for the property.
5560 * @returns E_ACCESSDENIED if the VM process is not available or not
5561 * currently handling queries and the lookup should then be done in
5562 * VBoxSVC.
5563 */
5564HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5565 com::Utf8Str &aValue,
5566 LONG64 *aTimestamp,
5567 com::Utf8Str &aFlags) const
5568{
5569 HRESULT rc = S_OK;
5570 Bstr bstrValue;
5571 Bstr bstrFlags;
5572
5573 ComPtr<IInternalSessionControl> directControl;
5574 {
5575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5576 if (mData->mSession.mLockType == LockType_VM)
5577 directControl = mData->mSession.mDirectControl;
5578 }
5579
5580 /* ignore calls made after #OnSessionEnd() is called */
5581 if (!directControl)
5582 rc = E_ACCESSDENIED;
5583 else
5584 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5585 0 /* accessMode */,
5586 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5587
5588 aValue = bstrValue;
5589 aFlags = bstrFlags;
5590
5591 return rc;
5592}
5593#endif // VBOX_WITH_GUEST_PROPS
5594
5595HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5596 com::Utf8Str &aValue,
5597 LONG64 *aTimestamp,
5598 com::Utf8Str &aFlags)
5599{
5600#ifndef VBOX_WITH_GUEST_PROPS
5601 ReturnComNotImplemented();
5602#else // VBOX_WITH_GUEST_PROPS
5603
5604 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5605
5606 if (rc == E_ACCESSDENIED)
5607 /* The VM is not running or the service is not (yet) accessible */
5608 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5609 return rc;
5610#endif // VBOX_WITH_GUEST_PROPS
5611}
5612
5613HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5614{
5615 LONG64 dummyTimestamp;
5616 com::Utf8Str dummyFlags;
5617 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5618 return rc;
5619
5620}
5621HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5622{
5623 com::Utf8Str dummyFlags;
5624 com::Utf8Str dummyValue;
5625 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5626 return rc;
5627}
5628
5629#ifdef VBOX_WITH_GUEST_PROPS
5630/**
5631 * Set a guest property in VBoxSVC's internal structures.
5632 */
5633HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5634 const com::Utf8Str &aFlags, bool fDelete)
5635{
5636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5637 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5638 if (FAILED(rc)) return rc;
5639
5640 try
5641 {
5642 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5643 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5644 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5645
5646 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5647 if (it == mHWData->mGuestProperties.end())
5648 {
5649 if (!fDelete)
5650 {
5651 i_setModified(IsModified_MachineData);
5652 mHWData.backupEx();
5653
5654 RTTIMESPEC time;
5655 HWData::GuestProperty prop;
5656 prop.strValue = Bstr(aValue).raw();
5657 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5658 prop.mFlags = fFlags;
5659 mHWData->mGuestProperties[aName] = prop;
5660 }
5661 }
5662 else
5663 {
5664 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5665 {
5666 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5667 }
5668 else
5669 {
5670 i_setModified(IsModified_MachineData);
5671 mHWData.backupEx();
5672
5673 /* The backupEx() operation invalidates our iterator,
5674 * so get a new one. */
5675 it = mHWData->mGuestProperties.find(aName);
5676 Assert(it != mHWData->mGuestProperties.end());
5677
5678 if (!fDelete)
5679 {
5680 RTTIMESPEC time;
5681 it->second.strValue = aValue;
5682 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5683 it->second.mFlags = fFlags;
5684 }
5685 else
5686 mHWData->mGuestProperties.erase(it);
5687 }
5688 }
5689
5690 if (SUCCEEDED(rc))
5691 {
5692 alock.release();
5693
5694 mParent->i_onGuestPropertyChange(mData->mUuid,
5695 Bstr(aName).raw(),
5696 Bstr(aValue).raw(),
5697 Bstr(aFlags).raw());
5698 }
5699 }
5700 catch (std::bad_alloc &)
5701 {
5702 rc = E_OUTOFMEMORY;
5703 }
5704
5705 return rc;
5706}
5707
5708/**
5709 * Set a property on the VM that that property belongs to.
5710 * @returns E_ACCESSDENIED if the VM process is not available or not
5711 * currently handling queries and the setting should then be done in
5712 * VBoxSVC.
5713 */
5714HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5715 const com::Utf8Str &aFlags, bool fDelete)
5716{
5717 HRESULT rc;
5718
5719 try
5720 {
5721 ComPtr<IInternalSessionControl> directControl;
5722 {
5723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5724 if (mData->mSession.mLockType == LockType_VM)
5725 directControl = mData->mSession.mDirectControl;
5726 }
5727
5728 Bstr dummy1; /* will not be changed (setter) */
5729 Bstr dummy2; /* will not be changed (setter) */
5730 LONG64 dummy64;
5731 if (!directControl)
5732 rc = E_ACCESSDENIED;
5733 else
5734 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5735 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5736 fDelete ? 2 : 1 /* accessMode */,
5737 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5738 }
5739 catch (std::bad_alloc &)
5740 {
5741 rc = E_OUTOFMEMORY;
5742 }
5743
5744 return rc;
5745}
5746#endif // VBOX_WITH_GUEST_PROPS
5747
5748HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5749 const com::Utf8Str &aFlags)
5750{
5751#ifndef VBOX_WITH_GUEST_PROPS
5752 ReturnComNotImplemented();
5753#else // VBOX_WITH_GUEST_PROPS
5754 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5755 if (rc == E_ACCESSDENIED)
5756 /* The VM is not running or the service is not (yet) accessible */
5757 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5758 return rc;
5759#endif // VBOX_WITH_GUEST_PROPS
5760}
5761
5762HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5763{
5764 return setGuestProperty(aProperty, aValue, "");
5765}
5766
5767HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5768{
5769#ifndef VBOX_WITH_GUEST_PROPS
5770 ReturnComNotImplemented();
5771#else // VBOX_WITH_GUEST_PROPS
5772 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5773 if (rc == E_ACCESSDENIED)
5774 /* The VM is not running or the service is not (yet) accessible */
5775 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5776 return rc;
5777#endif // VBOX_WITH_GUEST_PROPS
5778}
5779
5780#ifdef VBOX_WITH_GUEST_PROPS
5781/**
5782 * Enumerate the guest properties in VBoxSVC's internal structures.
5783 */
5784HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5785 std::vector<com::Utf8Str> &aNames,
5786 std::vector<com::Utf8Str> &aValues,
5787 std::vector<LONG64> &aTimestamps,
5788 std::vector<com::Utf8Str> &aFlags)
5789{
5790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5791 Utf8Str strPatterns(aPatterns);
5792
5793 /*
5794 * Look for matching patterns and build up a list.
5795 */
5796 HWData::GuestPropertyMap propMap;
5797 for (HWData::GuestPropertyMap::const_iterator
5798 it = mHWData->mGuestProperties.begin();
5799 it != mHWData->mGuestProperties.end();
5800 ++it)
5801 {
5802 if ( strPatterns.isEmpty()
5803 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5804 RTSTR_MAX,
5805 it->first.c_str(),
5806 RTSTR_MAX,
5807 NULL)
5808 )
5809 propMap.insert(*it);
5810 }
5811
5812 alock.release();
5813
5814 /*
5815 * And build up the arrays for returning the property information.
5816 */
5817 size_t cEntries = propMap.size();
5818
5819 aNames.resize(cEntries);
5820 aValues.resize(cEntries);
5821 aTimestamps.resize(cEntries);
5822 aFlags.resize(cEntries);
5823
5824 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5825 size_t i = 0;
5826 for (HWData::GuestPropertyMap::const_iterator
5827 it = propMap.begin();
5828 it != propMap.end();
5829 ++it, ++i)
5830 {
5831 aNames[i] = it->first;
5832 aValues[i] = it->second.strValue;
5833 aTimestamps[i] = it->second.mTimestamp;
5834 GuestPropWriteFlags(it->second.mFlags, szFlags);
5835 aFlags[i] = Utf8Str(szFlags);
5836 }
5837
5838 return S_OK;
5839}
5840
5841/**
5842 * Enumerate the properties managed by a VM.
5843 * @returns E_ACCESSDENIED if the VM process is not available or not
5844 * currently handling queries and the setting should then be done in
5845 * VBoxSVC.
5846 */
5847HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5848 std::vector<com::Utf8Str> &aNames,
5849 std::vector<com::Utf8Str> &aValues,
5850 std::vector<LONG64> &aTimestamps,
5851 std::vector<com::Utf8Str> &aFlags)
5852{
5853 HRESULT rc;
5854 ComPtr<IInternalSessionControl> directControl;
5855 {
5856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5857 if (mData->mSession.mLockType == LockType_VM)
5858 directControl = mData->mSession.mDirectControl;
5859 }
5860
5861 com::SafeArray<BSTR> bNames;
5862 com::SafeArray<BSTR> bValues;
5863 com::SafeArray<LONG64> bTimestamps;
5864 com::SafeArray<BSTR> bFlags;
5865
5866 if (!directControl)
5867 rc = E_ACCESSDENIED;
5868 else
5869 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5870 ComSafeArrayAsOutParam(bNames),
5871 ComSafeArrayAsOutParam(bValues),
5872 ComSafeArrayAsOutParam(bTimestamps),
5873 ComSafeArrayAsOutParam(bFlags));
5874 size_t i;
5875 aNames.resize(bNames.size());
5876 for (i = 0; i < bNames.size(); ++i)
5877 aNames[i] = Utf8Str(bNames[i]);
5878 aValues.resize(bValues.size());
5879 for (i = 0; i < bValues.size(); ++i)
5880 aValues[i] = Utf8Str(bValues[i]);
5881 aTimestamps.resize(bTimestamps.size());
5882 for (i = 0; i < bTimestamps.size(); ++i)
5883 aTimestamps[i] = bTimestamps[i];
5884 aFlags.resize(bFlags.size());
5885 for (i = 0; i < bFlags.size(); ++i)
5886 aFlags[i] = Utf8Str(bFlags[i]);
5887
5888 return rc;
5889}
5890#endif // VBOX_WITH_GUEST_PROPS
5891HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5892 std::vector<com::Utf8Str> &aNames,
5893 std::vector<com::Utf8Str> &aValues,
5894 std::vector<LONG64> &aTimestamps,
5895 std::vector<com::Utf8Str> &aFlags)
5896{
5897#ifndef VBOX_WITH_GUEST_PROPS
5898 ReturnComNotImplemented();
5899#else // VBOX_WITH_GUEST_PROPS
5900
5901 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5902
5903 if (rc == E_ACCESSDENIED)
5904 /* The VM is not running or the service is not (yet) accessible */
5905 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5906 return rc;
5907#endif // VBOX_WITH_GUEST_PROPS
5908}
5909
5910HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5911 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5912{
5913 MediumAttachmentList atts;
5914
5915 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5916 if (FAILED(rc)) return rc;
5917
5918 aMediumAttachments.resize(atts.size());
5919 size_t i = 0;
5920 for (MediumAttachmentList::const_iterator
5921 it = atts.begin();
5922 it != atts.end();
5923 ++it, ++i)
5924 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5925
5926 return S_OK;
5927}
5928
5929HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5930 LONG aControllerPort,
5931 LONG aDevice,
5932 ComPtr<IMediumAttachment> &aAttachment)
5933{
5934 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5935 aName.c_str(), aControllerPort, aDevice));
5936
5937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5938
5939 aAttachment = NULL;
5940
5941 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5942 aName,
5943 aControllerPort,
5944 aDevice);
5945 if (pAttach.isNull())
5946 return setError(VBOX_E_OBJECT_NOT_FOUND,
5947 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5948 aDevice, aControllerPort, aName.c_str());
5949
5950 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5951
5952 return S_OK;
5953}
5954
5955
5956HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5957 StorageBus_T aConnectionType,
5958 ComPtr<IStorageController> &aController)
5959{
5960 if ( (aConnectionType <= StorageBus_Null)
5961 || (aConnectionType > StorageBus_VirtioSCSI))
5962 return setError(E_INVALIDARG,
5963 tr("Invalid connection type: %d"),
5964 aConnectionType);
5965
5966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5967
5968 HRESULT rc = i_checkStateDependency(MutableStateDep);
5969 if (FAILED(rc)) return rc;
5970
5971 /* try to find one with the name first. */
5972 ComObjPtr<StorageController> ctrl;
5973
5974 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5975 if (SUCCEEDED(rc))
5976 return setError(VBOX_E_OBJECT_IN_USE,
5977 tr("Storage controller named '%s' already exists"),
5978 aName.c_str());
5979
5980 ctrl.createObject();
5981
5982 /* get a new instance number for the storage controller */
5983 ULONG ulInstance = 0;
5984 bool fBootable = true;
5985 for (StorageControllerList::const_iterator
5986 it = mStorageControllers->begin();
5987 it != mStorageControllers->end();
5988 ++it)
5989 {
5990 if ((*it)->i_getStorageBus() == aConnectionType)
5991 {
5992 ULONG ulCurInst = (*it)->i_getInstance();
5993
5994 if (ulCurInst >= ulInstance)
5995 ulInstance = ulCurInst + 1;
5996
5997 /* Only one controller of each type can be marked as bootable. */
5998 if ((*it)->i_getBootable())
5999 fBootable = false;
6000 }
6001 }
6002
6003 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6004 if (FAILED(rc)) return rc;
6005
6006 i_setModified(IsModified_Storage);
6007 mStorageControllers.backup();
6008 mStorageControllers->push_back(ctrl);
6009
6010 ctrl.queryInterfaceTo(aController.asOutParam());
6011
6012 /* inform the direct session if any */
6013 alock.release();
6014 i_onStorageControllerChange(i_getId(), aName);
6015
6016 return S_OK;
6017}
6018
6019HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6020 ComPtr<IStorageController> &aStorageController)
6021{
6022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6023
6024 ComObjPtr<StorageController> ctrl;
6025
6026 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6027 if (SUCCEEDED(rc))
6028 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6029
6030 return rc;
6031}
6032
6033HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6034 ULONG aInstance,
6035 ComPtr<IStorageController> &aStorageController)
6036{
6037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 for (StorageControllerList::const_iterator
6040 it = mStorageControllers->begin();
6041 it != mStorageControllers->end();
6042 ++it)
6043 {
6044 if ( (*it)->i_getStorageBus() == aConnectionType
6045 && (*it)->i_getInstance() == aInstance)
6046 {
6047 (*it).queryInterfaceTo(aStorageController.asOutParam());
6048 return S_OK;
6049 }
6050 }
6051
6052 return setError(VBOX_E_OBJECT_NOT_FOUND,
6053 tr("Could not find a storage controller with instance number '%lu'"),
6054 aInstance);
6055}
6056
6057HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6058{
6059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6060
6061 HRESULT rc = i_checkStateDependency(MutableStateDep);
6062 if (FAILED(rc)) return rc;
6063
6064 ComObjPtr<StorageController> ctrl;
6065
6066 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6067 if (SUCCEEDED(rc))
6068 {
6069 /* Ensure that only one controller of each type is marked as bootable. */
6070 if (aBootable == TRUE)
6071 {
6072 for (StorageControllerList::const_iterator
6073 it = mStorageControllers->begin();
6074 it != mStorageControllers->end();
6075 ++it)
6076 {
6077 ComObjPtr<StorageController> aCtrl = (*it);
6078
6079 if ( (aCtrl->i_getName() != aName)
6080 && aCtrl->i_getBootable() == TRUE
6081 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6082 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6083 {
6084 aCtrl->i_setBootable(FALSE);
6085 break;
6086 }
6087 }
6088 }
6089
6090 if (SUCCEEDED(rc))
6091 {
6092 ctrl->i_setBootable(aBootable);
6093 i_setModified(IsModified_Storage);
6094 }
6095 }
6096
6097 if (SUCCEEDED(rc))
6098 {
6099 /* inform the direct session if any */
6100 alock.release();
6101 i_onStorageControllerChange(i_getId(), aName);
6102 }
6103
6104 return rc;
6105}
6106
6107HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6108{
6109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6110
6111 HRESULT rc = i_checkStateDependency(MutableStateDep);
6112 if (FAILED(rc)) return rc;
6113
6114 ComObjPtr<StorageController> ctrl;
6115 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6116 if (FAILED(rc)) return rc;
6117
6118 MediumAttachmentList llDetachedAttachments;
6119 {
6120 /* find all attached devices to the appropriate storage controller and detach them all */
6121 // make a temporary list because detachDevice invalidates iterators into
6122 // mMediumAttachments
6123 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6124
6125 for (MediumAttachmentList::const_iterator
6126 it = llAttachments2.begin();
6127 it != llAttachments2.end();
6128 ++it)
6129 {
6130 MediumAttachment *pAttachTemp = *it;
6131
6132 AutoCaller localAutoCaller(pAttachTemp);
6133 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6134
6135 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6136
6137 if (pAttachTemp->i_getControllerName() == aName)
6138 {
6139 llDetachedAttachments.push_back(pAttachTemp);
6140 rc = i_detachDevice(pAttachTemp, alock, NULL);
6141 if (FAILED(rc)) return rc;
6142 }
6143 }
6144 }
6145
6146 /* send event about detached devices before removing parent controller */
6147 for (MediumAttachmentList::const_iterator
6148 it = llDetachedAttachments.begin();
6149 it != llDetachedAttachments.end();
6150 ++it)
6151 {
6152 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6153 }
6154
6155 /* We can remove it now. */
6156 i_setModified(IsModified_Storage);
6157 mStorageControllers.backup();
6158
6159 ctrl->i_unshare();
6160
6161 mStorageControllers->remove(ctrl);
6162
6163 /* inform the direct session if any */
6164 alock.release();
6165 i_onStorageControllerChange(i_getId(), aName);
6166
6167 return S_OK;
6168}
6169
6170HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6171 ComPtr<IUSBController> &aController)
6172{
6173 if ( (aType <= USBControllerType_Null)
6174 || (aType >= USBControllerType_Last))
6175 return setError(E_INVALIDARG,
6176 tr("Invalid USB controller type: %d"),
6177 aType);
6178
6179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6180
6181 HRESULT rc = i_checkStateDependency(MutableStateDep);
6182 if (FAILED(rc)) return rc;
6183
6184 /* try to find one with the same type first. */
6185 ComObjPtr<USBController> ctrl;
6186
6187 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6188 if (SUCCEEDED(rc))
6189 return setError(VBOX_E_OBJECT_IN_USE,
6190 tr("USB controller named '%s' already exists"),
6191 aName.c_str());
6192
6193 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6194 ULONG maxInstances;
6195 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6196 if (FAILED(rc))
6197 return rc;
6198
6199 ULONG cInstances = i_getUSBControllerCountByType(aType);
6200 if (cInstances >= maxInstances)
6201 return setError(E_INVALIDARG,
6202 tr("Too many USB controllers of this type"));
6203
6204 ctrl.createObject();
6205
6206 rc = ctrl->init(this, aName, aType);
6207 if (FAILED(rc)) return rc;
6208
6209 i_setModified(IsModified_USB);
6210 mUSBControllers.backup();
6211 mUSBControllers->push_back(ctrl);
6212
6213 ctrl.queryInterfaceTo(aController.asOutParam());
6214
6215 /* inform the direct session if any */
6216 alock.release();
6217 i_onUSBControllerChange();
6218
6219 return S_OK;
6220}
6221
6222HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6223{
6224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 ComObjPtr<USBController> ctrl;
6227
6228 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6229 if (SUCCEEDED(rc))
6230 ctrl.queryInterfaceTo(aController.asOutParam());
6231
6232 return rc;
6233}
6234
6235HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6236 ULONG *aControllers)
6237{
6238 if ( (aType <= USBControllerType_Null)
6239 || (aType >= USBControllerType_Last))
6240 return setError(E_INVALIDARG,
6241 tr("Invalid USB controller type: %d"),
6242 aType);
6243
6244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 ComObjPtr<USBController> ctrl;
6247
6248 *aControllers = i_getUSBControllerCountByType(aType);
6249
6250 return S_OK;
6251}
6252
6253HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6254{
6255
6256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6257
6258 HRESULT rc = i_checkStateDependency(MutableStateDep);
6259 if (FAILED(rc)) return rc;
6260
6261 ComObjPtr<USBController> ctrl;
6262 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6263 if (FAILED(rc)) return rc;
6264
6265 i_setModified(IsModified_USB);
6266 mUSBControllers.backup();
6267
6268 ctrl->i_unshare();
6269
6270 mUSBControllers->remove(ctrl);
6271
6272 /* inform the direct session if any */
6273 alock.release();
6274 i_onUSBControllerChange();
6275
6276 return S_OK;
6277}
6278
6279HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6280 ULONG *aOriginX,
6281 ULONG *aOriginY,
6282 ULONG *aWidth,
6283 ULONG *aHeight,
6284 BOOL *aEnabled)
6285{
6286 uint32_t u32OriginX= 0;
6287 uint32_t u32OriginY= 0;
6288 uint32_t u32Width = 0;
6289 uint32_t u32Height = 0;
6290 uint16_t u16Flags = 0;
6291
6292 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6293 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6294 if (RT_FAILURE(vrc))
6295 {
6296#ifdef RT_OS_WINDOWS
6297 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6298 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6299 * So just assign fEnable to TRUE again.
6300 * The right fix would be to change GUI API wrappers to make sure that parameters
6301 * are changed only if API succeeds.
6302 */
6303 *aEnabled = TRUE;
6304#endif
6305 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6306 tr("Saved guest size is not available (%Rrc)"),
6307 vrc);
6308 }
6309
6310 *aOriginX = u32OriginX;
6311 *aOriginY = u32OriginY;
6312 *aWidth = u32Width;
6313 *aHeight = u32Height;
6314 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6315
6316 return S_OK;
6317}
6318
6319HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6320 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6321{
6322 if (aScreenId != 0)
6323 return E_NOTIMPL;
6324
6325 if ( aBitmapFormat != BitmapFormat_BGR0
6326 && aBitmapFormat != BitmapFormat_BGRA
6327 && aBitmapFormat != BitmapFormat_RGBA
6328 && aBitmapFormat != BitmapFormat_PNG)
6329 return setError(E_NOTIMPL,
6330 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6331
6332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6333
6334 uint8_t *pu8Data = NULL;
6335 uint32_t cbData = 0;
6336 uint32_t u32Width = 0;
6337 uint32_t u32Height = 0;
6338
6339 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6340
6341 if (RT_FAILURE(vrc))
6342 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6343 tr("Saved thumbnail data is not available (%Rrc)"),
6344 vrc);
6345
6346 HRESULT hr = S_OK;
6347
6348 *aWidth = u32Width;
6349 *aHeight = u32Height;
6350
6351 if (cbData > 0)
6352 {
6353 /* Convert pixels to the format expected by the API caller. */
6354 if (aBitmapFormat == BitmapFormat_BGR0)
6355 {
6356 /* [0] B, [1] G, [2] R, [3] 0. */
6357 aData.resize(cbData);
6358 memcpy(&aData.front(), pu8Data, cbData);
6359 }
6360 else if (aBitmapFormat == BitmapFormat_BGRA)
6361 {
6362 /* [0] B, [1] G, [2] R, [3] A. */
6363 aData.resize(cbData);
6364 for (uint32_t i = 0; i < cbData; i += 4)
6365 {
6366 aData[i] = pu8Data[i];
6367 aData[i + 1] = pu8Data[i + 1];
6368 aData[i + 2] = pu8Data[i + 2];
6369 aData[i + 3] = 0xff;
6370 }
6371 }
6372 else if (aBitmapFormat == BitmapFormat_RGBA)
6373 {
6374 /* [0] R, [1] G, [2] B, [3] A. */
6375 aData.resize(cbData);
6376 for (uint32_t i = 0; i < cbData; i += 4)
6377 {
6378 aData[i] = pu8Data[i + 2];
6379 aData[i + 1] = pu8Data[i + 1];
6380 aData[i + 2] = pu8Data[i];
6381 aData[i + 3] = 0xff;
6382 }
6383 }
6384 else if (aBitmapFormat == BitmapFormat_PNG)
6385 {
6386 uint8_t *pu8PNG = NULL;
6387 uint32_t cbPNG = 0;
6388 uint32_t cxPNG = 0;
6389 uint32_t cyPNG = 0;
6390
6391 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6392
6393 if (RT_SUCCESS(vrc))
6394 {
6395 aData.resize(cbPNG);
6396 if (cbPNG)
6397 memcpy(&aData.front(), pu8PNG, cbPNG);
6398 }
6399 else
6400 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6401 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6402 vrc);
6403
6404 RTMemFree(pu8PNG);
6405 }
6406 }
6407
6408 freeSavedDisplayScreenshot(pu8Data);
6409
6410 return hr;
6411}
6412
6413HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6414 ULONG *aWidth,
6415 ULONG *aHeight,
6416 std::vector<BitmapFormat_T> &aBitmapFormats)
6417{
6418 if (aScreenId != 0)
6419 return E_NOTIMPL;
6420
6421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6422
6423 uint8_t *pu8Data = NULL;
6424 uint32_t cbData = 0;
6425 uint32_t u32Width = 0;
6426 uint32_t u32Height = 0;
6427
6428 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6429
6430 if (RT_FAILURE(vrc))
6431 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6432 tr("Saved screenshot data is not available (%Rrc)"),
6433 vrc);
6434
6435 *aWidth = u32Width;
6436 *aHeight = u32Height;
6437 aBitmapFormats.resize(1);
6438 aBitmapFormats[0] = BitmapFormat_PNG;
6439
6440 freeSavedDisplayScreenshot(pu8Data);
6441
6442 return S_OK;
6443}
6444
6445HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6446 BitmapFormat_T aBitmapFormat,
6447 ULONG *aWidth,
6448 ULONG *aHeight,
6449 std::vector<BYTE> &aData)
6450{
6451 if (aScreenId != 0)
6452 return E_NOTIMPL;
6453
6454 if (aBitmapFormat != BitmapFormat_PNG)
6455 return E_NOTIMPL;
6456
6457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 uint8_t *pu8Data = NULL;
6460 uint32_t cbData = 0;
6461 uint32_t u32Width = 0;
6462 uint32_t u32Height = 0;
6463
6464 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6465
6466 if (RT_FAILURE(vrc))
6467 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6468 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6469 vrc);
6470
6471 *aWidth = u32Width;
6472 *aHeight = u32Height;
6473
6474 aData.resize(cbData);
6475 if (cbData)
6476 memcpy(&aData.front(), pu8Data, cbData);
6477
6478 freeSavedDisplayScreenshot(pu8Data);
6479
6480 return S_OK;
6481}
6482
6483HRESULT Machine::hotPlugCPU(ULONG aCpu)
6484{
6485 HRESULT rc = S_OK;
6486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6487
6488 if (!mHWData->mCPUHotPlugEnabled)
6489 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6490
6491 if (aCpu >= mHWData->mCPUCount)
6492 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6493
6494 if (mHWData->mCPUAttached[aCpu])
6495 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6496
6497 alock.release();
6498 rc = i_onCPUChange(aCpu, false);
6499 alock.acquire();
6500 if (FAILED(rc)) return rc;
6501
6502 i_setModified(IsModified_MachineData);
6503 mHWData.backup();
6504 mHWData->mCPUAttached[aCpu] = true;
6505
6506 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6507 if (Global::IsOnline(mData->mMachineState))
6508 i_saveSettings(NULL);
6509
6510 return S_OK;
6511}
6512
6513HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6514{
6515 HRESULT rc = S_OK;
6516
6517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6518
6519 if (!mHWData->mCPUHotPlugEnabled)
6520 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6521
6522 if (aCpu >= SchemaDefs::MaxCPUCount)
6523 return setError(E_INVALIDARG,
6524 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6525 SchemaDefs::MaxCPUCount);
6526
6527 if (!mHWData->mCPUAttached[aCpu])
6528 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6529
6530 /* CPU 0 can't be detached */
6531 if (aCpu == 0)
6532 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6533
6534 alock.release();
6535 rc = i_onCPUChange(aCpu, true);
6536 alock.acquire();
6537 if (FAILED(rc)) return rc;
6538
6539 i_setModified(IsModified_MachineData);
6540 mHWData.backup();
6541 mHWData->mCPUAttached[aCpu] = false;
6542
6543 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6544 if (Global::IsOnline(mData->mMachineState))
6545 i_saveSettings(NULL);
6546
6547 return S_OK;
6548}
6549
6550HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6551{
6552 *aAttached = false;
6553
6554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 /* If hotplug is enabled the CPU is always enabled. */
6557 if (!mHWData->mCPUHotPlugEnabled)
6558 {
6559 if (aCpu < mHWData->mCPUCount)
6560 *aAttached = true;
6561 }
6562 else
6563 {
6564 if (aCpu < SchemaDefs::MaxCPUCount)
6565 *aAttached = mHWData->mCPUAttached[aCpu];
6566 }
6567
6568 return S_OK;
6569}
6570
6571HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6572{
6573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 Utf8Str log = i_getLogFilename(aIdx);
6576 if (!RTFileExists(log.c_str()))
6577 log.setNull();
6578 aFilename = log;
6579
6580 return S_OK;
6581}
6582
6583HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6584{
6585 if (aSize < 0)
6586 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6587
6588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6589
6590 HRESULT rc = S_OK;
6591 Utf8Str log = i_getLogFilename(aIdx);
6592
6593 /* do not unnecessarily hold the lock while doing something which does
6594 * not need the lock and potentially takes a long time. */
6595 alock.release();
6596
6597 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6598 * keeps the SOAP reply size under 1M for the webservice (we're using
6599 * base64 encoded strings for binary data for years now, avoiding the
6600 * expansion of each byte array element to approx. 25 bytes of XML. */
6601 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6602 aData.resize(cbData);
6603
6604 RTFILE LogFile;
6605 int vrc = RTFileOpen(&LogFile, log.c_str(),
6606 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6607 if (RT_SUCCESS(vrc))
6608 {
6609 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6610 if (RT_SUCCESS(vrc))
6611 aData.resize(cbData);
6612 else
6613 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6614 tr("Could not read log file '%s' (%Rrc)"),
6615 log.c_str(), vrc);
6616 RTFileClose(LogFile);
6617 }
6618 else
6619 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6620 tr("Could not open log file '%s' (%Rrc)"),
6621 log.c_str(), vrc);
6622
6623 if (FAILED(rc))
6624 aData.resize(0);
6625
6626 return rc;
6627}
6628
6629
6630/**
6631 * Currently this method doesn't attach device to the running VM,
6632 * just makes sure it's plugged on next VM start.
6633 */
6634HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6635{
6636 // lock scope
6637 {
6638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6639
6640 HRESULT rc = i_checkStateDependency(MutableStateDep);
6641 if (FAILED(rc)) return rc;
6642
6643 ChipsetType_T aChipset = ChipsetType_PIIX3;
6644 COMGETTER(ChipsetType)(&aChipset);
6645
6646 if (aChipset != ChipsetType_ICH9)
6647 {
6648 return setError(E_INVALIDARG,
6649 tr("Host PCI attachment only supported with ICH9 chipset"));
6650 }
6651
6652 // check if device with this host PCI address already attached
6653 for (HWData::PCIDeviceAssignmentList::const_iterator
6654 it = mHWData->mPCIDeviceAssignments.begin();
6655 it != mHWData->mPCIDeviceAssignments.end();
6656 ++it)
6657 {
6658 LONG iHostAddress = -1;
6659 ComPtr<PCIDeviceAttachment> pAttach;
6660 pAttach = *it;
6661 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6662 if (iHostAddress == aHostAddress)
6663 return setError(E_INVALIDARG,
6664 tr("Device with host PCI address already attached to this VM"));
6665 }
6666
6667 ComObjPtr<PCIDeviceAttachment> pda;
6668 char name[32];
6669
6670 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6671 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6672 pda.createObject();
6673 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6674 i_setModified(IsModified_MachineData);
6675 mHWData.backup();
6676 mHWData->mPCIDeviceAssignments.push_back(pda);
6677 }
6678
6679 return S_OK;
6680}
6681
6682/**
6683 * Currently this method doesn't detach device from the running VM,
6684 * just makes sure it's not plugged on next VM start.
6685 */
6686HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6687{
6688 ComObjPtr<PCIDeviceAttachment> pAttach;
6689 bool fRemoved = false;
6690 HRESULT rc;
6691
6692 // lock scope
6693 {
6694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6695
6696 rc = i_checkStateDependency(MutableStateDep);
6697 if (FAILED(rc)) return rc;
6698
6699 for (HWData::PCIDeviceAssignmentList::const_iterator
6700 it = mHWData->mPCIDeviceAssignments.begin();
6701 it != mHWData->mPCIDeviceAssignments.end();
6702 ++it)
6703 {
6704 LONG iHostAddress = -1;
6705 pAttach = *it;
6706 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6707 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6708 {
6709 i_setModified(IsModified_MachineData);
6710 mHWData.backup();
6711 mHWData->mPCIDeviceAssignments.remove(pAttach);
6712 fRemoved = true;
6713 break;
6714 }
6715 }
6716 }
6717
6718
6719 /* Fire event outside of the lock */
6720 if (fRemoved)
6721 {
6722 Assert(!pAttach.isNull());
6723 ComPtr<IEventSource> es;
6724 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6725 Assert(SUCCEEDED(rc));
6726 Bstr mid;
6727 rc = this->COMGETTER(Id)(mid.asOutParam());
6728 Assert(SUCCEEDED(rc));
6729 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6730 }
6731
6732 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6733 tr("No host PCI device %08x attached"),
6734 aHostAddress
6735 );
6736}
6737
6738HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6739{
6740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6741
6742 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6743 size_t i = 0;
6744 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6745 it = mHWData->mPCIDeviceAssignments.begin();
6746 it != mHWData->mPCIDeviceAssignments.end();
6747 ++it, ++i)
6748 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6749
6750 return S_OK;
6751}
6752
6753HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6754{
6755 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6756
6757 return S_OK;
6758}
6759
6760HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6761{
6762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6763
6764 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6765
6766 return S_OK;
6767}
6768
6769HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6770{
6771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6772 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6773 if (SUCCEEDED(hrc))
6774 {
6775 hrc = mHWData.backupEx();
6776 if (SUCCEEDED(hrc))
6777 {
6778 i_setModified(IsModified_MachineData);
6779 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6780 }
6781 }
6782 return hrc;
6783}
6784
6785HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6786{
6787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6788 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6789 return S_OK;
6790}
6791
6792HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6793{
6794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6795 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6796 if (SUCCEEDED(hrc))
6797 {
6798 hrc = mHWData.backupEx();
6799 if (SUCCEEDED(hrc))
6800 {
6801 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6802 if (SUCCEEDED(hrc))
6803 i_setModified(IsModified_MachineData);
6804 }
6805 }
6806 return hrc;
6807}
6808
6809HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6810{
6811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6812
6813 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6814
6815 return S_OK;
6816}
6817
6818HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6819{
6820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6821 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6822 if (SUCCEEDED(hrc))
6823 {
6824 hrc = mHWData.backupEx();
6825 if (SUCCEEDED(hrc))
6826 {
6827 i_setModified(IsModified_MachineData);
6828 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6829 }
6830 }
6831 return hrc;
6832}
6833
6834HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6835{
6836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6839
6840 return S_OK;
6841}
6842
6843HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6844{
6845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6846
6847 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6848 if ( SUCCEEDED(hrc)
6849 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6850 {
6851 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6852 int vrc;
6853
6854 if (aAutostartEnabled)
6855 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6856 else
6857 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6858
6859 if (RT_SUCCESS(vrc))
6860 {
6861 hrc = mHWData.backupEx();
6862 if (SUCCEEDED(hrc))
6863 {
6864 i_setModified(IsModified_MachineData);
6865 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6866 }
6867 }
6868 else if (vrc == VERR_NOT_SUPPORTED)
6869 hrc = setError(VBOX_E_NOT_SUPPORTED,
6870 tr("The VM autostart feature is not supported on this platform"));
6871 else if (vrc == VERR_PATH_NOT_FOUND)
6872 hrc = setError(E_FAIL,
6873 tr("The path to the autostart database is not set"));
6874 else
6875 hrc = setError(E_UNEXPECTED,
6876 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6877 aAutostartEnabled ? "Adding" : "Removing",
6878 mUserData->s.strName.c_str(), vrc);
6879 }
6880 return hrc;
6881}
6882
6883HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6884{
6885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6886
6887 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6888
6889 return S_OK;
6890}
6891
6892HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6893{
6894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6895 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6896 if (SUCCEEDED(hrc))
6897 {
6898 hrc = mHWData.backupEx();
6899 if (SUCCEEDED(hrc))
6900 {
6901 i_setModified(IsModified_MachineData);
6902 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6903 }
6904 }
6905 return hrc;
6906}
6907
6908HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6909{
6910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6911
6912 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6913
6914 return S_OK;
6915}
6916
6917HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6918{
6919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6920 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6921 if ( SUCCEEDED(hrc)
6922 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6923 {
6924 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6925 int vrc;
6926
6927 if (aAutostopType != AutostopType_Disabled)
6928 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6929 else
6930 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6931
6932 if (RT_SUCCESS(vrc))
6933 {
6934 hrc = mHWData.backupEx();
6935 if (SUCCEEDED(hrc))
6936 {
6937 i_setModified(IsModified_MachineData);
6938 mHWData->mAutostart.enmAutostopType = aAutostopType;
6939 }
6940 }
6941 else if (vrc == VERR_NOT_SUPPORTED)
6942 hrc = setError(VBOX_E_NOT_SUPPORTED,
6943 tr("The VM autostop feature is not supported on this platform"));
6944 else if (vrc == VERR_PATH_NOT_FOUND)
6945 hrc = setError(E_FAIL,
6946 tr("The path to the autostart database is not set"));
6947 else
6948 hrc = setError(E_UNEXPECTED,
6949 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6950 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6951 mUserData->s.strName.c_str(), vrc);
6952 }
6953 return hrc;
6954}
6955
6956HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6957{
6958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6959
6960 aDefaultFrontend = mHWData->mDefaultFrontend;
6961
6962 return S_OK;
6963}
6964
6965HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6966{
6967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6968 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6969 if (SUCCEEDED(hrc))
6970 {
6971 hrc = mHWData.backupEx();
6972 if (SUCCEEDED(hrc))
6973 {
6974 i_setModified(IsModified_MachineData);
6975 mHWData->mDefaultFrontend = aDefaultFrontend;
6976 }
6977 }
6978 return hrc;
6979}
6980
6981HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6982{
6983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6984 size_t cbIcon = mUserData->s.ovIcon.size();
6985 aIcon.resize(cbIcon);
6986 if (cbIcon)
6987 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6988 return S_OK;
6989}
6990
6991HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6992{
6993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6994 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6995 if (SUCCEEDED(hrc))
6996 {
6997 i_setModified(IsModified_MachineData);
6998 mUserData.backup();
6999 size_t cbIcon = aIcon.size();
7000 mUserData->s.ovIcon.resize(cbIcon);
7001 if (cbIcon)
7002 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7003 }
7004 return hrc;
7005}
7006
7007HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7008{
7009#ifdef VBOX_WITH_USB
7010 *aUSBProxyAvailable = true;
7011#else
7012 *aUSBProxyAvailable = false;
7013#endif
7014 return S_OK;
7015}
7016
7017HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7018{
7019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7020
7021 *aVMProcessPriority = mUserData->s.enmVMPriority;
7022
7023 return S_OK;
7024}
7025
7026HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7027{
7028 RT_NOREF(aVMProcessPriority);
7029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7030 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7031 if (SUCCEEDED(hrc))
7032 {
7033 hrc = mUserData.backupEx();
7034 if (SUCCEEDED(hrc))
7035 {
7036 i_setModified(IsModified_MachineData);
7037 mUserData->s.enmVMPriority = aVMProcessPriority;
7038 }
7039 }
7040 alock.release();
7041 if (SUCCEEDED(hrc))
7042 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7043 return hrc;
7044}
7045
7046HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7047 ComPtr<IProgress> &aProgress)
7048{
7049 ComObjPtr<Progress> pP;
7050 Progress *ppP = pP;
7051 IProgress *iP = static_cast<IProgress *>(ppP);
7052 IProgress **pProgress = &iP;
7053
7054 IMachine *pTarget = aTarget;
7055
7056 /* Convert the options. */
7057 RTCList<CloneOptions_T> optList;
7058 if (aOptions.size())
7059 for (size_t i = 0; i < aOptions.size(); ++i)
7060 optList.append(aOptions[i]);
7061
7062 if (optList.contains(CloneOptions_Link))
7063 {
7064 if (!i_isSnapshotMachine())
7065 return setError(E_INVALIDARG,
7066 tr("Linked clone can only be created from a snapshot"));
7067 if (aMode != CloneMode_MachineState)
7068 return setError(E_INVALIDARG,
7069 tr("Linked clone can only be created for a single machine state"));
7070 }
7071 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7072
7073 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7074
7075 HRESULT rc = pWorker->start(pProgress);
7076
7077 pP = static_cast<Progress *>(*pProgress);
7078 pP.queryInterfaceTo(aProgress.asOutParam());
7079
7080 return rc;
7081
7082}
7083
7084HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7085 const com::Utf8Str &aType,
7086 ComPtr<IProgress> &aProgress)
7087{
7088 LogFlowThisFuncEnter();
7089
7090 ComObjPtr<Progress> ptrProgress;
7091 HRESULT hrc = ptrProgress.createObject();
7092 if (SUCCEEDED(hrc))
7093 {
7094 /* Initialize our worker task */
7095 MachineMoveVM *pTask = NULL;
7096 try
7097 {
7098 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
7099 }
7100 catch (std::bad_alloc &)
7101 {
7102 return E_OUTOFMEMORY;
7103 }
7104
7105 hrc = pTask->init();//no exceptions are thrown
7106
7107 if (SUCCEEDED(hrc))
7108 {
7109 hrc = pTask->createThread();
7110 pTask = NULL; /* Consumed by createThread(). */
7111 if (SUCCEEDED(hrc))
7112 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7113 else
7114 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7115 }
7116 else
7117 delete pTask;
7118 }
7119
7120 LogFlowThisFuncLeave();
7121 return hrc;
7122
7123}
7124
7125HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7126{
7127 NOREF(aProgress);
7128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7129
7130 // This check should always fail.
7131 HRESULT rc = i_checkStateDependency(MutableStateDep);
7132 if (FAILED(rc)) return rc;
7133
7134 AssertFailedReturn(E_NOTIMPL);
7135}
7136
7137HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7138{
7139 NOREF(aSavedStateFile);
7140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7141
7142 // This check should always fail.
7143 HRESULT rc = i_checkStateDependency(MutableStateDep);
7144 if (FAILED(rc)) return rc;
7145
7146 AssertFailedReturn(E_NOTIMPL);
7147}
7148
7149HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7150{
7151 NOREF(aFRemoveFile);
7152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7153
7154 // This check should always fail.
7155 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7156 if (FAILED(rc)) return rc;
7157
7158 AssertFailedReturn(E_NOTIMPL);
7159}
7160
7161// public methods for internal purposes
7162/////////////////////////////////////////////////////////////////////////////
7163
7164/**
7165 * Adds the given IsModified_* flag to the dirty flags of the machine.
7166 * This must be called either during i_loadSettings or under the machine write lock.
7167 * @param fl Flag
7168 * @param fAllowStateModification If state modifications are allowed.
7169 */
7170void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7171{
7172 mData->flModifications |= fl;
7173 if (fAllowStateModification && i_isStateModificationAllowed())
7174 mData->mCurrentStateModified = true;
7175}
7176
7177/**
7178 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7179 * care of the write locking.
7180 *
7181 * @param fModification The flag to add.
7182 * @param fAllowStateModification If state modifications are allowed.
7183 */
7184void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7185{
7186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7187 i_setModified(fModification, fAllowStateModification);
7188}
7189
7190/**
7191 * Saves the registry entry of this machine to the given configuration node.
7192 *
7193 * @param data Machine registry data.
7194 *
7195 * @note locks this object for reading.
7196 */
7197HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7198{
7199 AutoLimitedCaller autoCaller(this);
7200 AssertComRCReturnRC(autoCaller.rc());
7201
7202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7203
7204 data.uuid = mData->mUuid;
7205 data.strSettingsFile = mData->m_strConfigFile;
7206
7207 return S_OK;
7208}
7209
7210/**
7211 * Calculates the absolute path of the given path taking the directory of the
7212 * machine settings file as the current directory.
7213 *
7214 * @param strPath Path to calculate the absolute path for.
7215 * @param aResult Where to put the result (used only on success, can be the
7216 * same Utf8Str instance as passed in @a aPath).
7217 * @return IPRT result.
7218 *
7219 * @note Locks this object for reading.
7220 */
7221int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7222{
7223 AutoCaller autoCaller(this);
7224 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7225
7226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7227
7228 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7229
7230 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7231
7232 strSettingsDir.stripFilename();
7233 char szFolder[RTPATH_MAX];
7234 size_t cbFolder = sizeof(szFolder);
7235 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7236 if (RT_SUCCESS(vrc))
7237 aResult = szFolder;
7238
7239 return vrc;
7240}
7241
7242/**
7243 * Copies strSource to strTarget, making it relative to the machine folder
7244 * if it is a subdirectory thereof, or simply copying it otherwise.
7245 *
7246 * @param strSource Path to evaluate and copy.
7247 * @param strTarget Buffer to receive target path.
7248 *
7249 * @note Locks this object for reading.
7250 */
7251void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7252 Utf8Str &strTarget)
7253{
7254 AutoCaller autoCaller(this);
7255 AssertComRCReturn(autoCaller.rc(), (void)0);
7256
7257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7258
7259 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7260 // use strTarget as a temporary buffer to hold the machine settings dir
7261 strTarget = mData->m_strConfigFileFull;
7262 strTarget.stripFilename();
7263 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7264 {
7265 // is relative: then append what's left
7266 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7267 // for empty paths (only possible for subdirs) use "." to avoid
7268 // triggering default settings for not present config attributes.
7269 if (strTarget.isEmpty())
7270 strTarget = ".";
7271 }
7272 else
7273 // is not relative: then overwrite
7274 strTarget = strSource;
7275}
7276
7277/**
7278 * Returns the full path to the machine's log folder in the
7279 * \a aLogFolder argument.
7280 */
7281void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7282{
7283 AutoCaller autoCaller(this);
7284 AssertComRCReturnVoid(autoCaller.rc());
7285
7286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7287
7288 char szTmp[RTPATH_MAX];
7289 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7290 if (RT_SUCCESS(vrc))
7291 {
7292 if (szTmp[0] && !mUserData.isNull())
7293 {
7294 char szTmp2[RTPATH_MAX];
7295 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7296 if (RT_SUCCESS(vrc))
7297 aLogFolder = Utf8StrFmt("%s%c%s",
7298 szTmp2,
7299 RTPATH_DELIMITER,
7300 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7301 }
7302 else
7303 vrc = VERR_PATH_IS_RELATIVE;
7304 }
7305
7306 if (RT_FAILURE(vrc))
7307 {
7308 // fallback if VBOX_USER_LOGHOME is not set or invalid
7309 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7310 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7311 aLogFolder.append(RTPATH_DELIMITER);
7312 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7313 }
7314}
7315
7316/**
7317 * Returns the full path to the machine's log file for an given index.
7318 */
7319Utf8Str Machine::i_getLogFilename(ULONG idx)
7320{
7321 Utf8Str logFolder;
7322 getLogFolder(logFolder);
7323 Assert(logFolder.length());
7324
7325 Utf8Str log;
7326 if (idx == 0)
7327 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7328#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7329 else if (idx == 1)
7330 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7331 else
7332 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7333#else
7334 else
7335 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7336#endif
7337 return log;
7338}
7339
7340/**
7341 * Returns the full path to the machine's hardened log file.
7342 */
7343Utf8Str Machine::i_getHardeningLogFilename(void)
7344{
7345 Utf8Str strFilename;
7346 getLogFolder(strFilename);
7347 Assert(strFilename.length());
7348 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7349 return strFilename;
7350}
7351
7352/**
7353 * Returns the default NVRAM filename based on the location of the VM config.
7354 * Note that this is a relative path.
7355 */
7356Utf8Str Machine::i_getDefaultNVRAMFilename()
7357{
7358 AutoCaller autoCaller(this);
7359 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7360
7361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7362
7363 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7364 || i_isSnapshotMachine())
7365 return Utf8Str::Empty;
7366
7367 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7368 strNVRAMFilePath.stripPath();
7369 strNVRAMFilePath.stripSuffix();
7370 strNVRAMFilePath += ".nvram";
7371
7372 return strNVRAMFilePath;
7373}
7374
7375/**
7376 * Returns the NVRAM filename for a new snapshot. This intentionally works
7377 * similarly to the saved state file naming. Note that this is usually
7378 * a relative path, unless the snapshot folder is absolute.
7379 */
7380Utf8Str Machine::i_getSnapshotNVRAMFilename()
7381{
7382 AutoCaller autoCaller(this);
7383 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7384
7385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7386
7387 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7388 return Utf8Str::Empty;
7389
7390 RTTIMESPEC ts;
7391 RTTimeNow(&ts);
7392 RTTIME time;
7393 RTTimeExplode(&time, &ts);
7394
7395 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7396 strNVRAMFilePath += RTPATH_DELIMITER;
7397 strNVRAMFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7398 time.i32Year, time.u8Month, time.u8MonthDay,
7399 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7400
7401 return strNVRAMFilePath;
7402}
7403
7404/**
7405 * Composes a unique saved state filename based on the current system time. The filename is
7406 * granular to the second so this will work so long as no more than one snapshot is taken on
7407 * a machine per second.
7408 *
7409 * Before version 4.1, we used this formula for saved state files:
7410 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7411 * which no longer works because saved state files can now be shared between the saved state of the
7412 * "saved" machine and an online snapshot, and the following would cause problems:
7413 * 1) save machine
7414 * 2) create online snapshot from that machine state --> reusing saved state file
7415 * 3) save machine again --> filename would be reused, breaking the online snapshot
7416 *
7417 * So instead we now use a timestamp.
7418 *
7419 * @param strStateFilePath
7420 */
7421
7422void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7423{
7424 AutoCaller autoCaller(this);
7425 AssertComRCReturnVoid(autoCaller.rc());
7426
7427 {
7428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7429 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7430 }
7431
7432 RTTIMESPEC ts;
7433 RTTimeNow(&ts);
7434 RTTIME time;
7435 RTTimeExplode(&time, &ts);
7436
7437 strStateFilePath += RTPATH_DELIMITER;
7438 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7439 time.i32Year, time.u8Month, time.u8MonthDay,
7440 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7441}
7442
7443/**
7444 * Returns whether at least one USB controller is present for the VM.
7445 */
7446bool Machine::i_isUSBControllerPresent()
7447{
7448 AutoCaller autoCaller(this);
7449 AssertComRCReturn(autoCaller.rc(), false);
7450
7451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 return (mUSBControllers->size() > 0);
7454}
7455
7456#ifdef VBOX_WITH_CLOUD_NET
7457HRESULT Machine::i_connectToCloudNetwork(ProgressProxy *aProgress)
7458{
7459 LogFlowThisFuncEnter();
7460 AssertReturn(aProgress, E_FAIL);
7461
7462 HRESULT hrc = E_FAIL;
7463 Bstr name;
7464
7465 LogFlowThisFunc(("Checking if cloud network needs to be connected\n"));
7466 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7467 {
7468 BOOL enabled;
7469 hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
7470 if ( FAILED(hrc)
7471 || !enabled)
7472 continue;
7473
7474 NetworkAttachmentType_T type;
7475 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
7476 if ( SUCCEEDED(hrc)
7477 && type == NetworkAttachmentType_Cloud)
7478 {
7479 if (name.isNotEmpty())
7480 {
7481 LogRel(("VM '%s' uses multiple cloud network attachments. '%ls' will be ignored.\n",
7482 mUserData->s.strName.c_str(), name.raw()));
7483 continue;
7484 }
7485 hrc = mNetworkAdapters[slot]->COMGETTER(CloudNetwork)(name.asOutParam());
7486 if (SUCCEEDED(hrc))
7487 {
7488 LogRel(("VM '%s' uses cloud network '%ls'\n",
7489 mUserData->s.strName.c_str(), name.raw()));
7490 }
7491 }
7492 }
7493 if (name.isNotEmpty())
7494 {
7495 LogFlowThisFunc(("Connecting to cloud network '%ls'...\n", name.raw()));
7496 ComObjPtr<CloudNetwork> network;
7497 hrc = mParent->i_findCloudNetworkByName(name, &network);
7498 if (FAILED(hrc))
7499 {
7500 LogRel(("Could not find cloud network '%ls'.\n", name.raw()));
7501 return hrc;
7502 }
7503 GatewayInfo gateways;
7504 hrc = startGateways(mParent, network, gateways);
7505 if (SUCCEEDED(hrc))
7506 {
7507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7508 mData->mGatewayInfo = gateways;
7509 }
7510 }
7511 else
7512 LogFlowThisFunc(("VM '%s' has no cloud network attachments.\n", mUserData->s.strName.c_str()));
7513
7514 LogFlowThisFuncLeave();
7515 return hrc;
7516}
7517
7518HRESULT Machine::i_disconnectFromCloudNetwork()
7519{
7520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7521 GatewayInfo gateways(mData->mGatewayInfo);
7522 mData->mGatewayInfo.setNull();
7523 alock.release();
7524
7525 HRESULT hrc = stopGateways(mParent, gateways);
7526 return hrc;
7527}
7528#endif /* VBOX_WITH_CLOUD_NET */
7529
7530
7531/**
7532 * @note Locks this object for writing, calls the client process
7533 * (inside the lock).
7534 */
7535HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7536 const Utf8Str &strFrontend,
7537 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7538 ProgressProxy *aProgress)
7539{
7540 LogFlowThisFuncEnter();
7541
7542 AssertReturn(aControl, E_FAIL);
7543 AssertReturn(aProgress, E_FAIL);
7544 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7545
7546 AutoCaller autoCaller(this);
7547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7548
7549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7550
7551 if (!mData->mRegistered)
7552 return setError(E_UNEXPECTED,
7553 tr("The machine '%s' is not registered"),
7554 mUserData->s.strName.c_str());
7555
7556 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7557
7558 /* The process started when launching a VM with separate UI/VM processes is always
7559 * the UI process, i.e. needs special handling as it won't claim the session. */
7560 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7561
7562 if (fSeparate)
7563 {
7564 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7565 return setError(VBOX_E_INVALID_OBJECT_STATE,
7566 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7567 mUserData->s.strName.c_str());
7568 }
7569 else
7570 {
7571 if ( mData->mSession.mState == SessionState_Locked
7572 || mData->mSession.mState == SessionState_Spawning
7573 || mData->mSession.mState == SessionState_Unlocking)
7574 return setError(VBOX_E_INVALID_OBJECT_STATE,
7575 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7576 mUserData->s.strName.c_str());
7577
7578 /* may not be busy */
7579 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7580 }
7581
7582 /* Hardening logging */
7583#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7584 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7585 {
7586 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7587 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7588 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7589 {
7590 Utf8Str strStartupLogDir = strHardeningLogFile;
7591 strStartupLogDir.stripFilename();
7592 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7593 file without stripping the file. */
7594 }
7595 strSupHardeningLogArg.append(strHardeningLogFile);
7596
7597 /* Remove legacy log filename to avoid confusion. */
7598 Utf8Str strOldStartupLogFile;
7599 getLogFolder(strOldStartupLogFile);
7600 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7601 RTFileDelete(strOldStartupLogFile.c_str());
7602 }
7603#else
7604 Utf8Str strSupHardeningLogArg;
7605#endif
7606
7607 Utf8Str strAppOverride;
7608#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7609 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7610#endif
7611
7612 bool fUseVBoxSDS = false;
7613 Utf8Str strCanonicalName;
7614 if (false)
7615 { }
7616#ifdef VBOX_WITH_QTGUI
7617 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7618 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7619 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7620 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7621 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7622 {
7623 strCanonicalName = "GUI/Qt";
7624 fUseVBoxSDS = true;
7625 }
7626#endif
7627#ifdef VBOX_WITH_VBOXSDL
7628 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7629 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7630 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7631 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7632 {
7633 strCanonicalName = "GUI/SDL";
7634 fUseVBoxSDS = true;
7635 }
7636#endif
7637#ifdef VBOX_WITH_HEADLESS
7638 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7639 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7640 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7641 {
7642 strCanonicalName = "headless";
7643 }
7644#endif
7645 else
7646 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7647
7648 Utf8Str idStr = mData->mUuid.toString();
7649 Utf8Str const &strMachineName = mUserData->s.strName;
7650 RTPROCESS pid = NIL_RTPROCESS;
7651
7652#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7653 RT_NOREF(fUseVBoxSDS);
7654#else
7655 DWORD idCallerSession = ~(DWORD)0;
7656 if (fUseVBoxSDS)
7657 {
7658 /*
7659 * The VBoxSDS should be used for process launching the VM with
7660 * GUI only if the caller and the VBoxSDS are in different Windows
7661 * sessions and the caller in the interactive one.
7662 */
7663 fUseVBoxSDS = false;
7664
7665 /* Get windows session of the current process. The process token used
7666 due to several reasons:
7667 1. The token is absent for the current thread except someone set it
7668 for us.
7669 2. Needs to get the id of the session where the process is started.
7670 We only need to do this once, though. */
7671 static DWORD s_idCurrentSession = ~(DWORD)0;
7672 DWORD idCurrentSession = s_idCurrentSession;
7673 if (idCurrentSession == ~(DWORD)0)
7674 {
7675 HANDLE hCurrentProcessToken = NULL;
7676 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7677 {
7678 DWORD cbIgn = 0;
7679 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7680 s_idCurrentSession = idCurrentSession;
7681 else
7682 {
7683 idCurrentSession = ~(DWORD)0;
7684 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7685 }
7686 CloseHandle(hCurrentProcessToken);
7687 }
7688 else
7689 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7690 }
7691
7692 /* get the caller's session */
7693 HRESULT hrc = CoImpersonateClient();
7694 if (SUCCEEDED(hrc))
7695 {
7696 HANDLE hCallerThreadToken;
7697 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7698 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7699 &hCallerThreadToken))
7700 {
7701 SetLastError(NO_ERROR);
7702 DWORD cbIgn = 0;
7703 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7704 {
7705 /* Only need to use SDS if the session ID differs: */
7706 if (idCurrentSession != idCallerSession)
7707 {
7708 fUseVBoxSDS = false;
7709
7710 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7711 DWORD cbTokenGroups = 0;
7712 PTOKEN_GROUPS pTokenGroups = NULL;
7713 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7714 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7715 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7716 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7717 {
7718 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7719 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7720 PSID pInteractiveSid = NULL;
7721 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7722 {
7723 /* Iterate over the groups looking for the interactive SID: */
7724 fUseVBoxSDS = false;
7725 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7726 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7727 {
7728 fUseVBoxSDS = true;
7729 break;
7730 }
7731 FreeSid(pInteractiveSid);
7732 }
7733 }
7734 else
7735 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7736 RTMemTmpFree(pTokenGroups);
7737 }
7738 }
7739 else
7740 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7741 CloseHandle(hCallerThreadToken);
7742 }
7743 else
7744 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7745 CoRevertToSelf();
7746 }
7747 else
7748 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7749 }
7750 if (fUseVBoxSDS)
7751 {
7752 /* connect to VBoxSDS */
7753 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7754 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7755 if (FAILED(rc))
7756 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7757 strMachineName.c_str());
7758
7759 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7760 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7761 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7762 service to access the files. */
7763 rc = CoSetProxyBlanket(pVBoxSDS,
7764 RPC_C_AUTHN_DEFAULT,
7765 RPC_C_AUTHZ_DEFAULT,
7766 COLE_DEFAULT_PRINCIPAL,
7767 RPC_C_AUTHN_LEVEL_DEFAULT,
7768 RPC_C_IMP_LEVEL_IMPERSONATE,
7769 NULL,
7770 EOAC_DEFAULT);
7771 if (FAILED(rc))
7772 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7773
7774 size_t const cEnvVars = aEnvironmentChanges.size();
7775 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7776 for (size_t i = 0; i < cEnvVars; i++)
7777 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7778
7779 ULONG uPid = 0;
7780 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7781 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7782 idCallerSession, &uPid);
7783 if (FAILED(rc))
7784 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7785 pid = (RTPROCESS)uPid;
7786 }
7787 else
7788#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7789 {
7790 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7791 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7792 if (RT_FAILURE(vrc))
7793 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7794 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7795 }
7796
7797 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7798 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7799
7800 if (!fSeparate)
7801 {
7802 /*
7803 * Note that we don't release the lock here before calling the client,
7804 * because it doesn't need to call us back if called with a NULL argument.
7805 * Releasing the lock here is dangerous because we didn't prepare the
7806 * launch data yet, but the client we've just started may happen to be
7807 * too fast and call LockMachine() that will fail (because of PID, etc.),
7808 * so that the Machine will never get out of the Spawning session state.
7809 */
7810
7811 /* inform the session that it will be a remote one */
7812 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7813#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7814 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7815#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7816 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7817#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7818 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7819
7820 if (FAILED(rc))
7821 {
7822 /* restore the session state */
7823 mData->mSession.mState = SessionState_Unlocked;
7824 alock.release();
7825 mParent->i_addProcessToReap(pid);
7826 /* The failure may occur w/o any error info (from RPC), so provide one */
7827 return setError(VBOX_E_VM_ERROR,
7828 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7829 }
7830
7831 /* attach launch data to the machine */
7832 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7833 mData->mSession.mRemoteControls.push_back(aControl);
7834 mData->mSession.mProgress = aProgress;
7835 mData->mSession.mPID = pid;
7836 mData->mSession.mState = SessionState_Spawning;
7837 Assert(strCanonicalName.isNotEmpty());
7838 mData->mSession.mName = strCanonicalName;
7839 }
7840 else
7841 {
7842 /* For separate UI process we declare the launch as completed instantly, as the
7843 * actual headless VM start may or may not come. No point in remembering anything
7844 * yet, as what matters for us is when the headless VM gets started. */
7845 aProgress->i_notifyComplete(S_OK);
7846 }
7847
7848 alock.release();
7849 mParent->i_addProcessToReap(pid);
7850
7851 LogFlowThisFuncLeave();
7852 return S_OK;
7853}
7854
7855/**
7856 * Returns @c true if the given session machine instance has an open direct
7857 * session (and optionally also for direct sessions which are closing) and
7858 * returns the session control machine instance if so.
7859 *
7860 * Note that when the method returns @c false, the arguments remain unchanged.
7861 *
7862 * @param aMachine Session machine object.
7863 * @param aControl Direct session control object (optional).
7864 * @param aRequireVM If true then only allow VM sessions.
7865 * @param aAllowClosing If true then additionally a session which is currently
7866 * being closed will also be allowed.
7867 *
7868 * @note locks this object for reading.
7869 */
7870bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7871 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7872 bool aRequireVM /*= false*/,
7873 bool aAllowClosing /*= false*/)
7874{
7875 AutoLimitedCaller autoCaller(this);
7876 AssertComRCReturn(autoCaller.rc(), false);
7877
7878 /* just return false for inaccessible machines */
7879 if (getObjectState().getState() != ObjectState::Ready)
7880 return false;
7881
7882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7883
7884 if ( ( mData->mSession.mState == SessionState_Locked
7885 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7886 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7887 )
7888 {
7889 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7890
7891 aMachine = mData->mSession.mMachine;
7892
7893 if (aControl != NULL)
7894 *aControl = mData->mSession.mDirectControl;
7895
7896 return true;
7897 }
7898
7899 return false;
7900}
7901
7902/**
7903 * Returns @c true if the given machine has an spawning direct session.
7904 *
7905 * @note locks this object for reading.
7906 */
7907bool Machine::i_isSessionSpawning()
7908{
7909 AutoLimitedCaller autoCaller(this);
7910 AssertComRCReturn(autoCaller.rc(), false);
7911
7912 /* just return false for inaccessible machines */
7913 if (getObjectState().getState() != ObjectState::Ready)
7914 return false;
7915
7916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7917
7918 if (mData->mSession.mState == SessionState_Spawning)
7919 return true;
7920
7921 return false;
7922}
7923
7924/**
7925 * Called from the client watcher thread to check for unexpected client process
7926 * death during Session_Spawning state (e.g. before it successfully opened a
7927 * direct session).
7928 *
7929 * On Win32 and on OS/2, this method is called only when we've got the
7930 * direct client's process termination notification, so it always returns @c
7931 * true.
7932 *
7933 * On other platforms, this method returns @c true if the client process is
7934 * terminated and @c false if it's still alive.
7935 *
7936 * @note Locks this object for writing.
7937 */
7938bool Machine::i_checkForSpawnFailure()
7939{
7940 AutoCaller autoCaller(this);
7941 if (!autoCaller.isOk())
7942 {
7943 /* nothing to do */
7944 LogFlowThisFunc(("Already uninitialized!\n"));
7945 return true;
7946 }
7947
7948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7949
7950 if (mData->mSession.mState != SessionState_Spawning)
7951 {
7952 /* nothing to do */
7953 LogFlowThisFunc(("Not spawning any more!\n"));
7954 return true;
7955 }
7956
7957 HRESULT rc = S_OK;
7958
7959 /* PID not yet initialized, skip check. */
7960 if (mData->mSession.mPID == NIL_RTPROCESS)
7961 return false;
7962
7963 RTPROCSTATUS status;
7964 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7965
7966 if (vrc != VERR_PROCESS_RUNNING)
7967 {
7968 Utf8Str strExtraInfo;
7969
7970#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7971 /* If the startup logfile exists and is of non-zero length, tell the
7972 user to look there for more details to encourage them to attach it
7973 when reporting startup issues. */
7974 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7975 uint64_t cbStartupLogFile = 0;
7976 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7977 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7978 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7979#endif
7980
7981 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7982 rc = setError(E_FAIL,
7983 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7984 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7985 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7986 rc = setError(E_FAIL,
7987 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7988 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7989 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7990 rc = setError(E_FAIL,
7991 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7992 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7993 else
7994 rc = setErrorBoth(E_FAIL, vrc,
7995 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7996 i_getName().c_str(), vrc, strExtraInfo.c_str());
7997 }
7998
7999 if (FAILED(rc))
8000 {
8001 /* Close the remote session, remove the remote control from the list
8002 * and reset session state to Closed (@note keep the code in sync with
8003 * the relevant part in LockMachine()). */
8004
8005 Assert(mData->mSession.mRemoteControls.size() == 1);
8006 if (mData->mSession.mRemoteControls.size() == 1)
8007 {
8008 ErrorInfoKeeper eik;
8009 mData->mSession.mRemoteControls.front()->Uninitialize();
8010 }
8011
8012 mData->mSession.mRemoteControls.clear();
8013 mData->mSession.mState = SessionState_Unlocked;
8014
8015 /* finalize the progress after setting the state */
8016 if (!mData->mSession.mProgress.isNull())
8017 {
8018 mData->mSession.mProgress->notifyComplete(rc);
8019 mData->mSession.mProgress.setNull();
8020 }
8021
8022 mData->mSession.mPID = NIL_RTPROCESS;
8023
8024 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8025 return true;
8026 }
8027
8028 return false;
8029}
8030
8031/**
8032 * Checks whether the machine can be registered. If so, commits and saves
8033 * all settings.
8034 *
8035 * @note Must be called from mParent's write lock. Locks this object and
8036 * children for writing.
8037 */
8038HRESULT Machine::i_prepareRegister()
8039{
8040 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8041
8042 AutoLimitedCaller autoCaller(this);
8043 AssertComRCReturnRC(autoCaller.rc());
8044
8045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8046
8047 /* wait for state dependents to drop to zero */
8048 i_ensureNoStateDependencies();
8049
8050 if (!mData->mAccessible)
8051 return setError(VBOX_E_INVALID_OBJECT_STATE,
8052 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8053 mUserData->s.strName.c_str(),
8054 mData->mUuid.toString().c_str());
8055
8056 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8057
8058 if (mData->mRegistered)
8059 return setError(VBOX_E_INVALID_OBJECT_STATE,
8060 tr("The machine '%s' with UUID {%s} is already registered"),
8061 mUserData->s.strName.c_str(),
8062 mData->mUuid.toString().c_str());
8063
8064 HRESULT rc = S_OK;
8065
8066 // Ensure the settings are saved. If we are going to be registered and
8067 // no config file exists yet, create it by calling i_saveSettings() too.
8068 if ( (mData->flModifications)
8069 || (!mData->pMachineConfigFile->fileExists())
8070 )
8071 {
8072 rc = i_saveSettings(NULL);
8073 // no need to check whether VirtualBox.xml needs saving too since
8074 // we can't have a machine XML file rename pending
8075 if (FAILED(rc)) return rc;
8076 }
8077
8078 /* more config checking goes here */
8079
8080 if (SUCCEEDED(rc))
8081 {
8082 /* we may have had implicit modifications we want to fix on success */
8083 i_commit();
8084
8085 mData->mRegistered = true;
8086 }
8087 else
8088 {
8089 /* we may have had implicit modifications we want to cancel on failure*/
8090 i_rollback(false /* aNotify */);
8091 }
8092
8093 return rc;
8094}
8095
8096/**
8097 * Increases the number of objects dependent on the machine state or on the
8098 * registered state. Guarantees that these two states will not change at least
8099 * until #i_releaseStateDependency() is called.
8100 *
8101 * Depending on the @a aDepType value, additional state checks may be made.
8102 * These checks will set extended error info on failure. See
8103 * #i_checkStateDependency() for more info.
8104 *
8105 * If this method returns a failure, the dependency is not added and the caller
8106 * is not allowed to rely on any particular machine state or registration state
8107 * value and may return the failed result code to the upper level.
8108 *
8109 * @param aDepType Dependency type to add.
8110 * @param aState Current machine state (NULL if not interested).
8111 * @param aRegistered Current registered state (NULL if not interested).
8112 *
8113 * @note Locks this object for writing.
8114 */
8115HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8116 MachineState_T *aState /* = NULL */,
8117 BOOL *aRegistered /* = NULL */)
8118{
8119 AutoCaller autoCaller(this);
8120 AssertComRCReturnRC(autoCaller.rc());
8121
8122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8123
8124 HRESULT rc = i_checkStateDependency(aDepType);
8125 if (FAILED(rc)) return rc;
8126
8127 {
8128 if (mData->mMachineStateChangePending != 0)
8129 {
8130 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8131 * drop to zero so don't add more. It may make sense to wait a bit
8132 * and retry before reporting an error (since the pending state
8133 * transition should be really quick) but let's just assert for
8134 * now to see if it ever happens on practice. */
8135
8136 AssertFailed();
8137
8138 return setError(E_ACCESSDENIED,
8139 tr("Machine state change is in progress. Please retry the operation later."));
8140 }
8141
8142 ++mData->mMachineStateDeps;
8143 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8144 }
8145
8146 if (aState)
8147 *aState = mData->mMachineState;
8148 if (aRegistered)
8149 *aRegistered = mData->mRegistered;
8150
8151 return S_OK;
8152}
8153
8154/**
8155 * Decreases the number of objects dependent on the machine state.
8156 * Must always complete the #i_addStateDependency() call after the state
8157 * dependency is no more necessary.
8158 */
8159void Machine::i_releaseStateDependency()
8160{
8161 AutoCaller autoCaller(this);
8162 AssertComRCReturnVoid(autoCaller.rc());
8163
8164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8165
8166 /* releaseStateDependency() w/o addStateDependency()? */
8167 AssertReturnVoid(mData->mMachineStateDeps != 0);
8168 -- mData->mMachineStateDeps;
8169
8170 if (mData->mMachineStateDeps == 0)
8171 {
8172 /* inform i_ensureNoStateDependencies() that there are no more deps */
8173 if (mData->mMachineStateChangePending != 0)
8174 {
8175 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8176 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8177 }
8178 }
8179}
8180
8181Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8182{
8183 /* start with nothing found */
8184 Utf8Str strResult("");
8185
8186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8187
8188 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8189 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8190 // found:
8191 strResult = it->second; // source is a Utf8Str
8192
8193 return strResult;
8194}
8195
8196// protected methods
8197/////////////////////////////////////////////////////////////////////////////
8198
8199/**
8200 * Performs machine state checks based on the @a aDepType value. If a check
8201 * fails, this method will set extended error info, otherwise it will return
8202 * S_OK. It is supposed, that on failure, the caller will immediately return
8203 * the return value of this method to the upper level.
8204 *
8205 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8206 *
8207 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8208 * current state of this machine object allows to change settings of the
8209 * machine (i.e. the machine is not registered, or registered but not running
8210 * and not saved). It is useful to call this method from Machine setters
8211 * before performing any change.
8212 *
8213 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8214 * as for MutableStateDep except that if the machine is saved, S_OK is also
8215 * returned. This is useful in setters which allow changing machine
8216 * properties when it is in the saved state.
8217 *
8218 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8219 * if the current state of this machine object allows to change runtime
8220 * changeable settings of the machine (i.e. the machine is not registered, or
8221 * registered but either running or not running and not saved). It is useful
8222 * to call this method from Machine setters before performing any changes to
8223 * runtime changeable settings.
8224 *
8225 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8226 * the same as for MutableOrRunningStateDep except that if the machine is
8227 * saved, S_OK is also returned. This is useful in setters which allow
8228 * changing runtime and saved state changeable machine properties.
8229 *
8230 * @param aDepType Dependency type to check.
8231 *
8232 * @note Non Machine based classes should use #i_addStateDependency() and
8233 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8234 * template.
8235 *
8236 * @note This method must be called from under this object's read or write
8237 * lock.
8238 */
8239HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8240{
8241 switch (aDepType)
8242 {
8243 case AnyStateDep:
8244 {
8245 break;
8246 }
8247 case MutableStateDep:
8248 {
8249 if ( mData->mRegistered
8250 && ( !i_isSessionMachine()
8251 || ( mData->mMachineState != MachineState_Aborted
8252 && mData->mMachineState != MachineState_Teleported
8253 && mData->mMachineState != MachineState_PoweredOff
8254 )
8255 )
8256 )
8257 return setError(VBOX_E_INVALID_VM_STATE,
8258 tr("The machine is not mutable (state is %s)"),
8259 Global::stringifyMachineState(mData->mMachineState));
8260 break;
8261 }
8262 case MutableOrSavedStateDep:
8263 {
8264 if ( mData->mRegistered
8265 && ( !i_isSessionMachine()
8266 || ( mData->mMachineState != MachineState_Aborted
8267 && mData->mMachineState != MachineState_Teleported
8268 && mData->mMachineState != MachineState_Saved
8269 && mData->mMachineState != MachineState_PoweredOff
8270 )
8271 )
8272 )
8273 return setError(VBOX_E_INVALID_VM_STATE,
8274 tr("The machine is not mutable or saved (state is %s)"),
8275 Global::stringifyMachineState(mData->mMachineState));
8276 break;
8277 }
8278 case MutableOrRunningStateDep:
8279 {
8280 if ( mData->mRegistered
8281 && ( !i_isSessionMachine()
8282 || ( mData->mMachineState != MachineState_Aborted
8283 && mData->mMachineState != MachineState_Teleported
8284 && mData->mMachineState != MachineState_PoweredOff
8285 && !Global::IsOnline(mData->mMachineState)
8286 )
8287 )
8288 )
8289 return setError(VBOX_E_INVALID_VM_STATE,
8290 tr("The machine is not mutable or running (state is %s)"),
8291 Global::stringifyMachineState(mData->mMachineState));
8292 break;
8293 }
8294 case MutableOrSavedOrRunningStateDep:
8295 {
8296 if ( mData->mRegistered
8297 && ( !i_isSessionMachine()
8298 || ( mData->mMachineState != MachineState_Aborted
8299 && mData->mMachineState != MachineState_Teleported
8300 && mData->mMachineState != MachineState_Saved
8301 && mData->mMachineState != MachineState_PoweredOff
8302 && !Global::IsOnline(mData->mMachineState)
8303 )
8304 )
8305 )
8306 return setError(VBOX_E_INVALID_VM_STATE,
8307 tr("The machine is not mutable, saved or running (state is %s)"),
8308 Global::stringifyMachineState(mData->mMachineState));
8309 break;
8310 }
8311 }
8312
8313 return S_OK;
8314}
8315
8316/**
8317 * Helper to initialize all associated child objects and allocate data
8318 * structures.
8319 *
8320 * This method must be called as a part of the object's initialization procedure
8321 * (usually done in the #init() method).
8322 *
8323 * @note Must be called only from #init() or from #i_registeredInit().
8324 */
8325HRESULT Machine::initDataAndChildObjects()
8326{
8327 AutoCaller autoCaller(this);
8328 AssertComRCReturnRC(autoCaller.rc());
8329 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8330 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8331
8332 AssertReturn(!mData->mAccessible, E_FAIL);
8333
8334 /* allocate data structures */
8335 mSSData.allocate();
8336 mUserData.allocate();
8337 mHWData.allocate();
8338 mMediumAttachments.allocate();
8339 mStorageControllers.allocate();
8340 mUSBControllers.allocate();
8341
8342 /* initialize mOSTypeId */
8343 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8344
8345/** @todo r=bird: init() methods never fails, right? Why don't we make them
8346 * return void then! */
8347
8348 /* create associated BIOS settings object */
8349 unconst(mBIOSSettings).createObject();
8350 mBIOSSettings->init(this);
8351
8352 /* create associated record settings object */
8353 unconst(mRecordingSettings).createObject();
8354 mRecordingSettings->init(this);
8355
8356 /* create an associated VRDE object (default is disabled) */
8357 unconst(mVRDEServer).createObject();
8358 mVRDEServer->init(this);
8359
8360 /* create associated serial port objects */
8361 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8362 {
8363 unconst(mSerialPorts[slot]).createObject();
8364 mSerialPorts[slot]->init(this, slot);
8365 }
8366
8367 /* create associated parallel port objects */
8368 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8369 {
8370 unconst(mParallelPorts[slot]).createObject();
8371 mParallelPorts[slot]->init(this, slot);
8372 }
8373
8374 /* create the audio adapter object (always present, default is disabled) */
8375 unconst(mAudioAdapter).createObject();
8376 mAudioAdapter->init(this);
8377
8378 /* create the USB device filters object (always present) */
8379 unconst(mUSBDeviceFilters).createObject();
8380 mUSBDeviceFilters->init(this);
8381
8382 /* create associated network adapter objects */
8383 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8384 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8385 {
8386 unconst(mNetworkAdapters[slot]).createObject();
8387 mNetworkAdapters[slot]->init(this, slot);
8388 }
8389
8390 /* create the bandwidth control */
8391 unconst(mBandwidthControl).createObject();
8392 mBandwidthControl->init(this);
8393
8394 return S_OK;
8395}
8396
8397/**
8398 * Helper to uninitialize all associated child objects and to free all data
8399 * structures.
8400 *
8401 * This method must be called as a part of the object's uninitialization
8402 * procedure (usually done in the #uninit() method).
8403 *
8404 * @note Must be called only from #uninit() or from #i_registeredInit().
8405 */
8406void Machine::uninitDataAndChildObjects()
8407{
8408 AutoCaller autoCaller(this);
8409 AssertComRCReturnVoid(autoCaller.rc());
8410 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8411 || getObjectState().getState() == ObjectState::Limited);
8412
8413 /* tell all our other child objects we've been uninitialized */
8414 if (mBandwidthControl)
8415 {
8416 mBandwidthControl->uninit();
8417 unconst(mBandwidthControl).setNull();
8418 }
8419
8420 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8421 {
8422 if (mNetworkAdapters[slot])
8423 {
8424 mNetworkAdapters[slot]->uninit();
8425 unconst(mNetworkAdapters[slot]).setNull();
8426 }
8427 }
8428
8429 if (mUSBDeviceFilters)
8430 {
8431 mUSBDeviceFilters->uninit();
8432 unconst(mUSBDeviceFilters).setNull();
8433 }
8434
8435 if (mAudioAdapter)
8436 {
8437 mAudioAdapter->uninit();
8438 unconst(mAudioAdapter).setNull();
8439 }
8440
8441 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8442 {
8443 if (mParallelPorts[slot])
8444 {
8445 mParallelPorts[slot]->uninit();
8446 unconst(mParallelPorts[slot]).setNull();
8447 }
8448 }
8449
8450 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8451 {
8452 if (mSerialPorts[slot])
8453 {
8454 mSerialPorts[slot]->uninit();
8455 unconst(mSerialPorts[slot]).setNull();
8456 }
8457 }
8458
8459 if (mVRDEServer)
8460 {
8461 mVRDEServer->uninit();
8462 unconst(mVRDEServer).setNull();
8463 }
8464
8465 if (mBIOSSettings)
8466 {
8467 mBIOSSettings->uninit();
8468 unconst(mBIOSSettings).setNull();
8469 }
8470
8471 if (mRecordingSettings)
8472 {
8473 mRecordingSettings->uninit();
8474 unconst(mRecordingSettings).setNull();
8475 }
8476
8477 /* Deassociate media (only when a real Machine or a SnapshotMachine
8478 * instance is uninitialized; SessionMachine instances refer to real
8479 * Machine media). This is necessary for a clean re-initialization of
8480 * the VM after successfully re-checking the accessibility state. Note
8481 * that in case of normal Machine or SnapshotMachine uninitialization (as
8482 * a result of unregistering or deleting the snapshot), outdated media
8483 * attachments will already be uninitialized and deleted, so this
8484 * code will not affect them. */
8485 if ( !mMediumAttachments.isNull()
8486 && !i_isSessionMachine()
8487 )
8488 {
8489 for (MediumAttachmentList::const_iterator
8490 it = mMediumAttachments->begin();
8491 it != mMediumAttachments->end();
8492 ++it)
8493 {
8494 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8495 if (pMedium.isNull())
8496 continue;
8497 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8498 AssertComRC(rc);
8499 }
8500 }
8501
8502 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8503 {
8504 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8505 if (mData->mFirstSnapshot)
8506 {
8507 // snapshots tree is protected by machine write lock; strictly
8508 // this isn't necessary here since we're deleting the entire
8509 // machine, but otherwise we assert in Snapshot::uninit()
8510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8511 mData->mFirstSnapshot->uninit();
8512 mData->mFirstSnapshot.setNull();
8513 }
8514
8515 mData->mCurrentSnapshot.setNull();
8516 }
8517
8518 /* free data structures (the essential mData structure is not freed here
8519 * since it may be still in use) */
8520 mMediumAttachments.free();
8521 mStorageControllers.free();
8522 mUSBControllers.free();
8523 mHWData.free();
8524 mUserData.free();
8525 mSSData.free();
8526}
8527
8528/**
8529 * Returns a pointer to the Machine object for this machine that acts like a
8530 * parent for complex machine data objects such as shared folders, etc.
8531 *
8532 * For primary Machine objects and for SnapshotMachine objects, returns this
8533 * object's pointer itself. For SessionMachine objects, returns the peer
8534 * (primary) machine pointer.
8535 */
8536Machine *Machine::i_getMachine()
8537{
8538 if (i_isSessionMachine())
8539 return (Machine*)mPeer;
8540 return this;
8541}
8542
8543/**
8544 * Makes sure that there are no machine state dependents. If necessary, waits
8545 * for the number of dependents to drop to zero.
8546 *
8547 * Make sure this method is called from under this object's write lock to
8548 * guarantee that no new dependents may be added when this method returns
8549 * control to the caller.
8550 *
8551 * @note Locks this object for writing. The lock will be released while waiting
8552 * (if necessary).
8553 *
8554 * @warning To be used only in methods that change the machine state!
8555 */
8556void Machine::i_ensureNoStateDependencies()
8557{
8558 AssertReturnVoid(isWriteLockOnCurrentThread());
8559
8560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8561
8562 /* Wait for all state dependents if necessary */
8563 if (mData->mMachineStateDeps != 0)
8564 {
8565 /* lazy semaphore creation */
8566 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8567 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8568
8569 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8570 mData->mMachineStateDeps));
8571
8572 ++mData->mMachineStateChangePending;
8573
8574 /* reset the semaphore before waiting, the last dependent will signal
8575 * it */
8576 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8577
8578 alock.release();
8579
8580 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8581
8582 alock.acquire();
8583
8584 -- mData->mMachineStateChangePending;
8585 }
8586}
8587
8588/**
8589 * Changes the machine state and informs callbacks.
8590 *
8591 * This method is not intended to fail so it either returns S_OK or asserts (and
8592 * returns a failure).
8593 *
8594 * @note Locks this object for writing.
8595 */
8596HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8597{
8598 LogFlowThisFuncEnter();
8599 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8600 Assert(aMachineState != MachineState_Null);
8601
8602 AutoCaller autoCaller(this);
8603 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8604
8605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8606
8607 /* wait for state dependents to drop to zero */
8608 i_ensureNoStateDependencies();
8609
8610 MachineState_T const enmOldState = mData->mMachineState;
8611 if (enmOldState != aMachineState)
8612 {
8613 mData->mMachineState = aMachineState;
8614 RTTimeNow(&mData->mLastStateChange);
8615
8616#ifdef VBOX_WITH_DTRACE_R3_MAIN
8617 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8618#endif
8619 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8620 }
8621
8622 LogFlowThisFuncLeave();
8623 return S_OK;
8624}
8625
8626/**
8627 * Searches for a shared folder with the given logical name
8628 * in the collection of shared folders.
8629 *
8630 * @param aName logical name of the shared folder
8631 * @param aSharedFolder where to return the found object
8632 * @param aSetError whether to set the error info if the folder is
8633 * not found
8634 * @return
8635 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8636 *
8637 * @note
8638 * must be called from under the object's lock!
8639 */
8640HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8641 ComObjPtr<SharedFolder> &aSharedFolder,
8642 bool aSetError /* = false */)
8643{
8644 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8645 for (HWData::SharedFolderList::const_iterator
8646 it = mHWData->mSharedFolders.begin();
8647 it != mHWData->mSharedFolders.end();
8648 ++it)
8649 {
8650 SharedFolder *pSF = *it;
8651 AutoCaller autoCaller(pSF);
8652 if (pSF->i_getName() == aName)
8653 {
8654 aSharedFolder = pSF;
8655 rc = S_OK;
8656 break;
8657 }
8658 }
8659
8660 if (aSetError && FAILED(rc))
8661 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8662
8663 return rc;
8664}
8665
8666/**
8667 * Initializes all machine instance data from the given settings structures
8668 * from XML. The exception is the machine UUID which needs special handling
8669 * depending on the caller's use case, so the caller needs to set that herself.
8670 *
8671 * This gets called in several contexts during machine initialization:
8672 *
8673 * -- When machine XML exists on disk already and needs to be loaded into memory,
8674 * for example, from #i_registeredInit() to load all registered machines on
8675 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8676 * attached to the machine should be part of some media registry already.
8677 *
8678 * -- During OVF import, when a machine config has been constructed from an
8679 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8680 * ensure that the media listed as attachments in the config (which have
8681 * been imported from the OVF) receive the correct registry ID.
8682 *
8683 * -- During VM cloning.
8684 *
8685 * @param config Machine settings from XML.
8686 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8687 * for each attached medium in the config.
8688 * @return
8689 */
8690HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8691 const Guid *puuidRegistry)
8692{
8693 // copy name, description, OS type, teleporter, UTC etc.
8694 mUserData->s = config.machineUserData;
8695
8696 // look up the object by Id to check it is valid
8697 ComObjPtr<GuestOSType> pGuestOSType;
8698 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8699 if (!pGuestOSType.isNull())
8700 mUserData->s.strOsType = pGuestOSType->i_id();
8701
8702 // stateFile (optional)
8703 if (config.strStateFile.isEmpty())
8704 mSSData->strStateFilePath.setNull();
8705 else
8706 {
8707 Utf8Str stateFilePathFull(config.strStateFile);
8708 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8709 if (RT_FAILURE(vrc))
8710 return setErrorBoth(E_FAIL, vrc,
8711 tr("Invalid saved state file path '%s' (%Rrc)"),
8712 config.strStateFile.c_str(),
8713 vrc);
8714 mSSData->strStateFilePath = stateFilePathFull;
8715 }
8716
8717 // snapshot folder needs special processing so set it again
8718 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8719 if (FAILED(rc)) return rc;
8720
8721 /* Copy the extra data items (config may or may not be the same as
8722 * mData->pMachineConfigFile) if necessary. When loading the XML files
8723 * from disk they are the same, but not for OVF import. */
8724 if (mData->pMachineConfigFile != &config)
8725 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8726
8727 /* currentStateModified (optional, default is true) */
8728 mData->mCurrentStateModified = config.fCurrentStateModified;
8729
8730 mData->mLastStateChange = config.timeLastStateChange;
8731
8732 /*
8733 * note: all mUserData members must be assigned prior this point because
8734 * we need to commit changes in order to let mUserData be shared by all
8735 * snapshot machine instances.
8736 */
8737 mUserData.commitCopy();
8738
8739 // machine registry, if present (must be loaded before snapshots)
8740 if (config.canHaveOwnMediaRegistry())
8741 {
8742 // determine machine folder
8743 Utf8Str strMachineFolder = i_getSettingsFileFull();
8744 strMachineFolder.stripFilename();
8745 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8746 config.mediaRegistry,
8747 strMachineFolder);
8748 if (FAILED(rc)) return rc;
8749 }
8750
8751 /* Snapshot node (optional) */
8752 size_t cRootSnapshots;
8753 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8754 {
8755 // there must be only one root snapshot
8756 Assert(cRootSnapshots == 1);
8757
8758 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8759
8760 rc = i_loadSnapshot(snap,
8761 config.uuidCurrentSnapshot,
8762 NULL); // no parent == first snapshot
8763 if (FAILED(rc)) return rc;
8764 }
8765
8766 // hardware data
8767 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8768 if (FAILED(rc)) return rc;
8769
8770 /*
8771 * NOTE: the assignment below must be the last thing to do,
8772 * otherwise it will be not possible to change the settings
8773 * somewhere in the code above because all setters will be
8774 * blocked by i_checkStateDependency(MutableStateDep).
8775 */
8776
8777 /* set the machine state to Aborted or Saved when appropriate */
8778 if (config.fAborted)
8779 {
8780 mSSData->strStateFilePath.setNull();
8781
8782 /* no need to use i_setMachineState() during init() */
8783 mData->mMachineState = MachineState_Aborted;
8784 }
8785 else if (!mSSData->strStateFilePath.isEmpty())
8786 {
8787 /* no need to use i_setMachineState() during init() */
8788 mData->mMachineState = MachineState_Saved;
8789 }
8790
8791 // after loading settings, we are no longer different from the XML on disk
8792 mData->flModifications = 0;
8793
8794 return S_OK;
8795}
8796
8797/**
8798 * Recursively loads all snapshots starting from the given.
8799 *
8800 * @param data snapshot settings.
8801 * @param aCurSnapshotId Current snapshot ID from the settings file.
8802 * @param aParentSnapshot Parent snapshot.
8803 */
8804HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8805 const Guid &aCurSnapshotId,
8806 Snapshot *aParentSnapshot)
8807{
8808 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8809 AssertReturn(!i_isSessionMachine(), E_FAIL);
8810
8811 HRESULT rc = S_OK;
8812
8813 Utf8Str strStateFile;
8814 if (!data.strStateFile.isEmpty())
8815 {
8816 /* optional */
8817 strStateFile = data.strStateFile;
8818 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8819 if (RT_FAILURE(vrc))
8820 return setErrorBoth(E_FAIL, vrc,
8821 tr("Invalid saved state file path '%s' (%Rrc)"),
8822 strStateFile.c_str(),
8823 vrc);
8824 }
8825
8826 /* create a snapshot machine object */
8827 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8828 pSnapshotMachine.createObject();
8829 rc = pSnapshotMachine->initFromSettings(this,
8830 data.hardware,
8831 &data.debugging,
8832 &data.autostart,
8833 data.uuid.ref(),
8834 strStateFile);
8835 if (FAILED(rc)) return rc;
8836
8837 /* create a snapshot object */
8838 ComObjPtr<Snapshot> pSnapshot;
8839 pSnapshot.createObject();
8840 /* initialize the snapshot */
8841 rc = pSnapshot->init(mParent, // VirtualBox object
8842 data.uuid,
8843 data.strName,
8844 data.strDescription,
8845 data.timestamp,
8846 pSnapshotMachine,
8847 aParentSnapshot);
8848 if (FAILED(rc)) return rc;
8849
8850 /* memorize the first snapshot if necessary */
8851 if (!mData->mFirstSnapshot)
8852 mData->mFirstSnapshot = pSnapshot;
8853
8854 /* memorize the current snapshot when appropriate */
8855 if ( !mData->mCurrentSnapshot
8856 && pSnapshot->i_getId() == aCurSnapshotId
8857 )
8858 mData->mCurrentSnapshot = pSnapshot;
8859
8860 // now create the children
8861 for (settings::SnapshotsList::const_iterator
8862 it = data.llChildSnapshots.begin();
8863 it != data.llChildSnapshots.end();
8864 ++it)
8865 {
8866 const settings::Snapshot &childData = *it;
8867 // recurse
8868 rc = i_loadSnapshot(childData,
8869 aCurSnapshotId,
8870 pSnapshot); // parent = the one we created above
8871 if (FAILED(rc)) return rc;
8872 }
8873
8874 return rc;
8875}
8876
8877/**
8878 * Loads settings into mHWData.
8879 *
8880 * @param puuidRegistry Registry ID.
8881 * @param puuidSnapshot Snapshot ID
8882 * @param data Reference to the hardware settings.
8883 * @param pDbg Pointer to the debugging settings.
8884 * @param pAutostart Pointer to the autostart settings.
8885 */
8886HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8887 const Guid *puuidSnapshot,
8888 const settings::Hardware &data,
8889 const settings::Debugging *pDbg,
8890 const settings::Autostart *pAutostart)
8891{
8892 AssertReturn(!i_isSessionMachine(), E_FAIL);
8893
8894 HRESULT rc = S_OK;
8895
8896 try
8897 {
8898 ComObjPtr<GuestOSType> pGuestOSType;
8899 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8900
8901 /* The hardware version attribute (optional). */
8902 mHWData->mHWVersion = data.strVersion;
8903 mHWData->mHardwareUUID = data.uuid;
8904
8905 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8906 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8907 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8908 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8909 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8910 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8911 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8912 mHWData->mPAEEnabled = data.fPAE;
8913 mHWData->mLongMode = data.enmLongMode;
8914 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8915 mHWData->mAPIC = data.fAPIC;
8916 mHWData->mX2APIC = data.fX2APIC;
8917 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8918 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8919 mHWData->mSpecCtrl = data.fSpecCtrl;
8920 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8921 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8922 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8923 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8924 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8925 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8926 mHWData->mCPUCount = data.cCPUs;
8927 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8928 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8929 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8930 mHWData->mCpuProfile = data.strCpuProfile;
8931
8932 // cpu
8933 if (mHWData->mCPUHotPlugEnabled)
8934 {
8935 for (settings::CpuList::const_iterator
8936 it = data.llCpus.begin();
8937 it != data.llCpus.end();
8938 ++it)
8939 {
8940 const settings::Cpu &cpu = *it;
8941
8942 mHWData->mCPUAttached[cpu.ulId] = true;
8943 }
8944 }
8945
8946 // cpuid leafs
8947 for (settings::CpuIdLeafsList::const_iterator
8948 it = data.llCpuIdLeafs.begin();
8949 it != data.llCpuIdLeafs.end();
8950 ++it)
8951 {
8952 const settings::CpuIdLeaf &rLeaf= *it;
8953 if ( rLeaf.idx < UINT32_C(0x20)
8954 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8955 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8956 mHWData->mCpuIdLeafList.push_back(rLeaf);
8957 /* else: just ignore */
8958 }
8959
8960 mHWData->mMemorySize = data.ulMemorySizeMB;
8961 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8962
8963 // boot order
8964 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8965 {
8966 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8967 if (it == data.mapBootOrder.end())
8968 mHWData->mBootOrder[i] = DeviceType_Null;
8969 else
8970 mHWData->mBootOrder[i] = it->second;
8971 }
8972
8973 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8974 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8975 mHWData->mMonitorCount = data.cMonitors;
8976 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8977 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8978 mHWData->mFirmwareType = data.firmwareType;
8979 mHWData->mPointingHIDType = data.pointingHIDType;
8980 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8981 mHWData->mChipsetType = data.chipsetType;
8982 mHWData->mParavirtProvider = data.paravirtProvider;
8983 mHWData->mParavirtDebug = data.strParavirtDebug;
8984 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8985 mHWData->mHPETEnabled = data.fHPETEnabled;
8986
8987 /* VRDEServer */
8988 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8989 if (FAILED(rc)) return rc;
8990
8991 /* BIOS */
8992 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8993 if (FAILED(rc)) return rc;
8994
8995 /* Recording settings */
8996 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8997 if (FAILED(rc)) return rc;
8998
8999 // Bandwidth control (must come before network adapters)
9000 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9001 if (FAILED(rc)) return rc;
9002
9003 /* USB controllers */
9004 for (settings::USBControllerList::const_iterator
9005 it = data.usbSettings.llUSBControllers.begin();
9006 it != data.usbSettings.llUSBControllers.end();
9007 ++it)
9008 {
9009 const settings::USBController &settingsCtrl = *it;
9010 ComObjPtr<USBController> newCtrl;
9011
9012 newCtrl.createObject();
9013 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9014 mUSBControllers->push_back(newCtrl);
9015 }
9016
9017 /* USB device filters */
9018 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9019 if (FAILED(rc)) return rc;
9020
9021 // network adapters (establish array size first and apply defaults, to
9022 // ensure reading the same settings as we saved, since the list skips
9023 // adapters having defaults)
9024 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9025 size_t oldCount = mNetworkAdapters.size();
9026 if (newCount > oldCount)
9027 {
9028 mNetworkAdapters.resize(newCount);
9029 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9030 {
9031 unconst(mNetworkAdapters[slot]).createObject();
9032 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9033 }
9034 }
9035 else if (newCount < oldCount)
9036 mNetworkAdapters.resize(newCount);
9037 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9038 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9039 for (settings::NetworkAdaptersList::const_iterator
9040 it = data.llNetworkAdapters.begin();
9041 it != data.llNetworkAdapters.end();
9042 ++it)
9043 {
9044 const settings::NetworkAdapter &nic = *it;
9045
9046 /* slot uniqueness is guaranteed by XML Schema */
9047 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9048 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9049 if (FAILED(rc)) return rc;
9050 }
9051
9052 // serial ports (establish defaults first, to ensure reading the same
9053 // settings as we saved, since the list skips ports having defaults)
9054 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9055 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9056 for (settings::SerialPortsList::const_iterator
9057 it = data.llSerialPorts.begin();
9058 it != data.llSerialPorts.end();
9059 ++it)
9060 {
9061 const settings::SerialPort &s = *it;
9062
9063 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9064 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9065 if (FAILED(rc)) return rc;
9066 }
9067
9068 // parallel ports (establish defaults first, to ensure reading the same
9069 // settings as we saved, since the list skips ports having defaults)
9070 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9071 mParallelPorts[i]->i_applyDefaults();
9072 for (settings::ParallelPortsList::const_iterator
9073 it = data.llParallelPorts.begin();
9074 it != data.llParallelPorts.end();
9075 ++it)
9076 {
9077 const settings::ParallelPort &p = *it;
9078
9079 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9080 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9081 if (FAILED(rc)) return rc;
9082 }
9083
9084 /* AudioAdapter */
9085 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9086 if (FAILED(rc)) return rc;
9087
9088 /* storage controllers */
9089 rc = i_loadStorageControllers(data.storage,
9090 puuidRegistry,
9091 puuidSnapshot);
9092 if (FAILED(rc)) return rc;
9093
9094 /* Shared folders */
9095 for (settings::SharedFoldersList::const_iterator
9096 it = data.llSharedFolders.begin();
9097 it != data.llSharedFolders.end();
9098 ++it)
9099 {
9100 const settings::SharedFolder &sf = *it;
9101
9102 ComObjPtr<SharedFolder> sharedFolder;
9103 /* Check for double entries. Not allowed! */
9104 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9105 if (SUCCEEDED(rc))
9106 return setError(VBOX_E_OBJECT_IN_USE,
9107 tr("Shared folder named '%s' already exists"),
9108 sf.strName.c_str());
9109
9110 /* Create the new shared folder. Don't break on error. This will be
9111 * reported when the machine starts. */
9112 sharedFolder.createObject();
9113 rc = sharedFolder->init(i_getMachine(),
9114 sf.strName,
9115 sf.strHostPath,
9116 RT_BOOL(sf.fWritable),
9117 RT_BOOL(sf.fAutoMount),
9118 sf.strAutoMountPoint,
9119 false /* fFailOnError */);
9120 if (FAILED(rc)) return rc;
9121 mHWData->mSharedFolders.push_back(sharedFolder);
9122 }
9123
9124 // Clipboard
9125 mHWData->mClipboardMode = data.clipboardMode;
9126 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9127
9128 // drag'n'drop
9129 mHWData->mDnDMode = data.dndMode;
9130
9131 // guest settings
9132 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9133
9134 // IO settings
9135 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9136 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9137
9138 // Host PCI devices
9139 for (settings::HostPCIDeviceAttachmentList::const_iterator
9140 it = data.pciAttachments.begin();
9141 it != data.pciAttachments.end();
9142 ++it)
9143 {
9144 const settings::HostPCIDeviceAttachment &hpda = *it;
9145 ComObjPtr<PCIDeviceAttachment> pda;
9146
9147 pda.createObject();
9148 pda->i_loadSettings(this, hpda);
9149 mHWData->mPCIDeviceAssignments.push_back(pda);
9150 }
9151
9152 /*
9153 * (The following isn't really real hardware, but it lives in HWData
9154 * for reasons of convenience.)
9155 */
9156
9157#ifdef VBOX_WITH_GUEST_PROPS
9158 /* Guest properties (optional) */
9159
9160 /* Only load transient guest properties for configs which have saved
9161 * state, because there shouldn't be any for powered off VMs. The same
9162 * logic applies for snapshots, as offline snapshots shouldn't have
9163 * any such properties. They confuse the code in various places.
9164 * Note: can't rely on the machine state, as it isn't set yet. */
9165 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9166 /* apologies for the hacky unconst() usage, but this needs hacking
9167 * actually inconsistent settings into consistency, otherwise there
9168 * will be some corner cases where the inconsistency survives
9169 * surprisingly long without getting fixed, especially for snapshots
9170 * as there are no config changes. */
9171 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9172 for (settings::GuestPropertiesList::iterator
9173 it = llGuestProperties.begin();
9174 it != llGuestProperties.end();
9175 /*nothing*/)
9176 {
9177 const settings::GuestProperty &prop = *it;
9178 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9179 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9180 if ( fSkipTransientGuestProperties
9181 && ( fFlags & GUEST_PROP_F_TRANSIENT
9182 || fFlags & GUEST_PROP_F_TRANSRESET))
9183 {
9184 it = llGuestProperties.erase(it);
9185 continue;
9186 }
9187 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9188 mHWData->mGuestProperties[prop.strName] = property;
9189 ++it;
9190 }
9191#endif /* VBOX_WITH_GUEST_PROPS defined */
9192
9193 rc = i_loadDebugging(pDbg);
9194 if (FAILED(rc))
9195 return rc;
9196
9197 mHWData->mAutostart = *pAutostart;
9198
9199 /* default frontend */
9200 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9201 }
9202 catch (std::bad_alloc &)
9203 {
9204 return E_OUTOFMEMORY;
9205 }
9206
9207 AssertComRC(rc);
9208 return rc;
9209}
9210
9211/**
9212 * Called from i_loadHardware() to load the debugging settings of the
9213 * machine.
9214 *
9215 * @param pDbg Pointer to the settings.
9216 */
9217HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9218{
9219 mHWData->mDebugging = *pDbg;
9220 /* no more processing currently required, this will probably change. */
9221 return S_OK;
9222}
9223
9224/**
9225 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9226 *
9227 * @param data storage settings.
9228 * @param puuidRegistry media registry ID to set media to or NULL;
9229 * see Machine::i_loadMachineDataFromSettings()
9230 * @param puuidSnapshot snapshot ID
9231 * @return
9232 */
9233HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9234 const Guid *puuidRegistry,
9235 const Guid *puuidSnapshot)
9236{
9237 AssertReturn(!i_isSessionMachine(), E_FAIL);
9238
9239 HRESULT rc = S_OK;
9240
9241 for (settings::StorageControllersList::const_iterator
9242 it = data.llStorageControllers.begin();
9243 it != data.llStorageControllers.end();
9244 ++it)
9245 {
9246 const settings::StorageController &ctlData = *it;
9247
9248 ComObjPtr<StorageController> pCtl;
9249 /* Try to find one with the name first. */
9250 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9251 if (SUCCEEDED(rc))
9252 return setError(VBOX_E_OBJECT_IN_USE,
9253 tr("Storage controller named '%s' already exists"),
9254 ctlData.strName.c_str());
9255
9256 pCtl.createObject();
9257 rc = pCtl->init(this,
9258 ctlData.strName,
9259 ctlData.storageBus,
9260 ctlData.ulInstance,
9261 ctlData.fBootable);
9262 if (FAILED(rc)) return rc;
9263
9264 mStorageControllers->push_back(pCtl);
9265
9266 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9267 if (FAILED(rc)) return rc;
9268
9269 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9270 if (FAILED(rc)) return rc;
9271
9272 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9273 if (FAILED(rc)) return rc;
9274
9275 /* Load the attached devices now. */
9276 rc = i_loadStorageDevices(pCtl,
9277 ctlData,
9278 puuidRegistry,
9279 puuidSnapshot);
9280 if (FAILED(rc)) return rc;
9281 }
9282
9283 return S_OK;
9284}
9285
9286/**
9287 * Called from i_loadStorageControllers for a controller's devices.
9288 *
9289 * @param aStorageController
9290 * @param data
9291 * @param puuidRegistry media registry ID to set media to or NULL; see
9292 * Machine::i_loadMachineDataFromSettings()
9293 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9294 * @return
9295 */
9296HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9297 const settings::StorageController &data,
9298 const Guid *puuidRegistry,
9299 const Guid *puuidSnapshot)
9300{
9301 HRESULT rc = S_OK;
9302
9303 /* paranoia: detect duplicate attachments */
9304 for (settings::AttachedDevicesList::const_iterator
9305 it = data.llAttachedDevices.begin();
9306 it != data.llAttachedDevices.end();
9307 ++it)
9308 {
9309 const settings::AttachedDevice &ad = *it;
9310
9311 for (settings::AttachedDevicesList::const_iterator it2 = it;
9312 it2 != data.llAttachedDevices.end();
9313 ++it2)
9314 {
9315 if (it == it2)
9316 continue;
9317
9318 const settings::AttachedDevice &ad2 = *it2;
9319
9320 if ( ad.lPort == ad2.lPort
9321 && ad.lDevice == ad2.lDevice)
9322 {
9323 return setError(E_FAIL,
9324 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9325 aStorageController->i_getName().c_str(),
9326 ad.lPort,
9327 ad.lDevice,
9328 mUserData->s.strName.c_str());
9329 }
9330 }
9331 }
9332
9333 for (settings::AttachedDevicesList::const_iterator
9334 it = data.llAttachedDevices.begin();
9335 it != data.llAttachedDevices.end();
9336 ++it)
9337 {
9338 const settings::AttachedDevice &dev = *it;
9339 ComObjPtr<Medium> medium;
9340
9341 switch (dev.deviceType)
9342 {
9343 case DeviceType_Floppy:
9344 case DeviceType_DVD:
9345 if (dev.strHostDriveSrc.isNotEmpty())
9346 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9347 false /* fRefresh */, medium);
9348 else
9349 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9350 dev.uuid,
9351 false /* fRefresh */,
9352 false /* aSetError */,
9353 medium);
9354 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9355 // This is not an error. The host drive or UUID might have vanished, so just go
9356 // ahead without this removeable medium attachment
9357 rc = S_OK;
9358 break;
9359
9360 case DeviceType_HardDisk:
9361 {
9362 /* find a hard disk by UUID */
9363 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9364 if (FAILED(rc))
9365 {
9366 if (i_isSnapshotMachine())
9367 {
9368 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9369 // so the user knows that the bad disk is in a snapshot somewhere
9370 com::ErrorInfo info;
9371 return setError(E_FAIL,
9372 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9373 puuidSnapshot->raw(),
9374 info.getText().raw());
9375 }
9376 else
9377 return rc;
9378 }
9379
9380 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9381
9382 if (medium->i_getType() == MediumType_Immutable)
9383 {
9384 if (i_isSnapshotMachine())
9385 return setError(E_FAIL,
9386 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9387 "of the virtual machine '%s' ('%s')"),
9388 medium->i_getLocationFull().c_str(),
9389 dev.uuid.raw(),
9390 puuidSnapshot->raw(),
9391 mUserData->s.strName.c_str(),
9392 mData->m_strConfigFileFull.c_str());
9393
9394 return setError(E_FAIL,
9395 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9396 medium->i_getLocationFull().c_str(),
9397 dev.uuid.raw(),
9398 mUserData->s.strName.c_str(),
9399 mData->m_strConfigFileFull.c_str());
9400 }
9401
9402 if (medium->i_getType() == MediumType_MultiAttach)
9403 {
9404 if (i_isSnapshotMachine())
9405 return setError(E_FAIL,
9406 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9407 "of the virtual machine '%s' ('%s')"),
9408 medium->i_getLocationFull().c_str(),
9409 dev.uuid.raw(),
9410 puuidSnapshot->raw(),
9411 mUserData->s.strName.c_str(),
9412 mData->m_strConfigFileFull.c_str());
9413
9414 return setError(E_FAIL,
9415 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9416 medium->i_getLocationFull().c_str(),
9417 dev.uuid.raw(),
9418 mUserData->s.strName.c_str(),
9419 mData->m_strConfigFileFull.c_str());
9420 }
9421
9422 if ( !i_isSnapshotMachine()
9423 && medium->i_getChildren().size() != 0
9424 )
9425 return setError(E_FAIL,
9426 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9427 "because it has %d differencing child hard disks"),
9428 medium->i_getLocationFull().c_str(),
9429 dev.uuid.raw(),
9430 mUserData->s.strName.c_str(),
9431 mData->m_strConfigFileFull.c_str(),
9432 medium->i_getChildren().size());
9433
9434 if (i_findAttachment(*mMediumAttachments.data(),
9435 medium))
9436 return setError(E_FAIL,
9437 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9438 medium->i_getLocationFull().c_str(),
9439 dev.uuid.raw(),
9440 mUserData->s.strName.c_str(),
9441 mData->m_strConfigFileFull.c_str());
9442
9443 break;
9444 }
9445
9446 default:
9447 return setError(E_FAIL,
9448 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9449 medium->i_getLocationFull().c_str(),
9450 mUserData->s.strName.c_str(),
9451 mData->m_strConfigFileFull.c_str());
9452 }
9453
9454 if (FAILED(rc))
9455 break;
9456
9457 /* Bandwidth groups are loaded at this point. */
9458 ComObjPtr<BandwidthGroup> pBwGroup;
9459
9460 if (!dev.strBwGroup.isEmpty())
9461 {
9462 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9463 if (FAILED(rc))
9464 return setError(E_FAIL,
9465 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9466 medium->i_getLocationFull().c_str(),
9467 dev.strBwGroup.c_str(),
9468 mUserData->s.strName.c_str(),
9469 mData->m_strConfigFileFull.c_str());
9470 pBwGroup->i_reference();
9471 }
9472
9473 const Utf8Str controllerName = aStorageController->i_getName();
9474 ComObjPtr<MediumAttachment> pAttachment;
9475 pAttachment.createObject();
9476 rc = pAttachment->init(this,
9477 medium,
9478 controllerName,
9479 dev.lPort,
9480 dev.lDevice,
9481 dev.deviceType,
9482 false,
9483 dev.fPassThrough,
9484 dev.fTempEject,
9485 dev.fNonRotational,
9486 dev.fDiscard,
9487 dev.fHotPluggable,
9488 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9489 if (FAILED(rc)) break;
9490
9491 /* associate the medium with this machine and snapshot */
9492 if (!medium.isNull())
9493 {
9494 AutoCaller medCaller(medium);
9495 if (FAILED(medCaller.rc())) return medCaller.rc();
9496 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9497
9498 if (i_isSnapshotMachine())
9499 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9500 else
9501 rc = medium->i_addBackReference(mData->mUuid);
9502 /* If the medium->addBackReference fails it sets an appropriate
9503 * error message, so no need to do any guesswork here. */
9504
9505 if (puuidRegistry)
9506 // caller wants registry ID to be set on all attached media (OVF import case)
9507 medium->i_addRegistry(*puuidRegistry);
9508 }
9509
9510 if (FAILED(rc))
9511 break;
9512
9513 /* back up mMediumAttachments to let registeredInit() properly rollback
9514 * on failure (= limited accessibility) */
9515 i_setModified(IsModified_Storage);
9516 mMediumAttachments.backup();
9517 mMediumAttachments->push_back(pAttachment);
9518 }
9519
9520 return rc;
9521}
9522
9523/**
9524 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9525 *
9526 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9527 * @param aSnapshot where to return the found snapshot
9528 * @param aSetError true to set extended error info on failure
9529 */
9530HRESULT Machine::i_findSnapshotById(const Guid &aId,
9531 ComObjPtr<Snapshot> &aSnapshot,
9532 bool aSetError /* = false */)
9533{
9534 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9535
9536 if (!mData->mFirstSnapshot)
9537 {
9538 if (aSetError)
9539 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9540 return E_FAIL;
9541 }
9542
9543 if (aId.isZero())
9544 aSnapshot = mData->mFirstSnapshot;
9545 else
9546 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9547
9548 if (!aSnapshot)
9549 {
9550 if (aSetError)
9551 return setError(E_FAIL,
9552 tr("Could not find a snapshot with UUID {%s}"),
9553 aId.toString().c_str());
9554 return E_FAIL;
9555 }
9556
9557 return S_OK;
9558}
9559
9560/**
9561 * Returns the snapshot with the given name or fails of no such snapshot.
9562 *
9563 * @param strName snapshot name to find
9564 * @param aSnapshot where to return the found snapshot
9565 * @param aSetError true to set extended error info on failure
9566 */
9567HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9568 ComObjPtr<Snapshot> &aSnapshot,
9569 bool aSetError /* = false */)
9570{
9571 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9572
9573 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9574
9575 if (!mData->mFirstSnapshot)
9576 {
9577 if (aSetError)
9578 return setError(VBOX_E_OBJECT_NOT_FOUND,
9579 tr("This machine does not have any snapshots"));
9580 return VBOX_E_OBJECT_NOT_FOUND;
9581 }
9582
9583 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9584
9585 if (!aSnapshot)
9586 {
9587 if (aSetError)
9588 return setError(VBOX_E_OBJECT_NOT_FOUND,
9589 tr("Could not find a snapshot named '%s'"), strName.c_str());
9590 return VBOX_E_OBJECT_NOT_FOUND;
9591 }
9592
9593 return S_OK;
9594}
9595
9596/**
9597 * Returns a storage controller object with the given name.
9598 *
9599 * @param aName storage controller name to find
9600 * @param aStorageController where to return the found storage controller
9601 * @param aSetError true to set extended error info on failure
9602 */
9603HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9604 ComObjPtr<StorageController> &aStorageController,
9605 bool aSetError /* = false */)
9606{
9607 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9608
9609 for (StorageControllerList::const_iterator
9610 it = mStorageControllers->begin();
9611 it != mStorageControllers->end();
9612 ++it)
9613 {
9614 if ((*it)->i_getName() == aName)
9615 {
9616 aStorageController = (*it);
9617 return S_OK;
9618 }
9619 }
9620
9621 if (aSetError)
9622 return setError(VBOX_E_OBJECT_NOT_FOUND,
9623 tr("Could not find a storage controller named '%s'"),
9624 aName.c_str());
9625 return VBOX_E_OBJECT_NOT_FOUND;
9626}
9627
9628/**
9629 * Returns a USB controller object with the given name.
9630 *
9631 * @param aName USB controller name to find
9632 * @param aUSBController where to return the found USB controller
9633 * @param aSetError true to set extended error info on failure
9634 */
9635HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9636 ComObjPtr<USBController> &aUSBController,
9637 bool aSetError /* = false */)
9638{
9639 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9640
9641 for (USBControllerList::const_iterator
9642 it = mUSBControllers->begin();
9643 it != mUSBControllers->end();
9644 ++it)
9645 {
9646 if ((*it)->i_getName() == aName)
9647 {
9648 aUSBController = (*it);
9649 return S_OK;
9650 }
9651 }
9652
9653 if (aSetError)
9654 return setError(VBOX_E_OBJECT_NOT_FOUND,
9655 tr("Could not find a storage controller named '%s'"),
9656 aName.c_str());
9657 return VBOX_E_OBJECT_NOT_FOUND;
9658}
9659
9660/**
9661 * Returns the number of USB controller instance of the given type.
9662 *
9663 * @param enmType USB controller type.
9664 */
9665ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9666{
9667 ULONG cCtrls = 0;
9668
9669 for (USBControllerList::const_iterator
9670 it = mUSBControllers->begin();
9671 it != mUSBControllers->end();
9672 ++it)
9673 {
9674 if ((*it)->i_getControllerType() == enmType)
9675 cCtrls++;
9676 }
9677
9678 return cCtrls;
9679}
9680
9681HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9682 MediumAttachmentList &atts)
9683{
9684 AutoCaller autoCaller(this);
9685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9686
9687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9688
9689 for (MediumAttachmentList::const_iterator
9690 it = mMediumAttachments->begin();
9691 it != mMediumAttachments->end();
9692 ++it)
9693 {
9694 const ComObjPtr<MediumAttachment> &pAtt = *it;
9695 // should never happen, but deal with NULL pointers in the list.
9696 AssertContinue(!pAtt.isNull());
9697
9698 // getControllerName() needs caller+read lock
9699 AutoCaller autoAttCaller(pAtt);
9700 if (FAILED(autoAttCaller.rc()))
9701 {
9702 atts.clear();
9703 return autoAttCaller.rc();
9704 }
9705 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9706
9707 if (pAtt->i_getControllerName() == aName)
9708 atts.push_back(pAtt);
9709 }
9710
9711 return S_OK;
9712}
9713
9714
9715/**
9716 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9717 * file if the machine name was changed and about creating a new settings file
9718 * if this is a new machine.
9719 *
9720 * @note Must be never called directly but only from #saveSettings().
9721 */
9722HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9723{
9724 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9725
9726 HRESULT rc = S_OK;
9727
9728 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9729
9730 /// @todo need to handle primary group change, too
9731
9732 /* attempt to rename the settings file if machine name is changed */
9733 if ( mUserData->s.fNameSync
9734 && mUserData.isBackedUp()
9735 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9736 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9737 )
9738 {
9739 bool dirRenamed = false;
9740 bool fileRenamed = false;
9741
9742 Utf8Str configFile, newConfigFile;
9743 Utf8Str configFilePrev, newConfigFilePrev;
9744 Utf8Str NVRAMFile, newNVRAMFile;
9745 Utf8Str configDir, newConfigDir;
9746
9747 do
9748 {
9749 int vrc = VINF_SUCCESS;
9750
9751 Utf8Str name = mUserData.backedUpData()->s.strName;
9752 Utf8Str newName = mUserData->s.strName;
9753 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9754 if (group == "/")
9755 group.setNull();
9756 Utf8Str newGroup = mUserData->s.llGroups.front();
9757 if (newGroup == "/")
9758 newGroup.setNull();
9759
9760 configFile = mData->m_strConfigFileFull;
9761
9762 /* first, rename the directory if it matches the group and machine name */
9763 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9764 group.c_str(), RTPATH_DELIMITER, name.c_str());
9765 /** @todo hack, make somehow use of ComposeMachineFilename */
9766 if (mUserData->s.fDirectoryIncludesUUID)
9767 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9768 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9769 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9770 /** @todo hack, make somehow use of ComposeMachineFilename */
9771 if (mUserData->s.fDirectoryIncludesUUID)
9772 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9773 configDir = configFile;
9774 configDir.stripFilename();
9775 newConfigDir = configDir;
9776 if ( configDir.length() >= groupPlusName.length()
9777 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9778 groupPlusName.c_str()))
9779 {
9780 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9781 Utf8Str newConfigBaseDir(newConfigDir);
9782 newConfigDir.append(newGroupPlusName);
9783 /* consistency: use \ if appropriate on the platform */
9784 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9785 /* new dir and old dir cannot be equal here because of 'if'
9786 * above and because name != newName */
9787 Assert(configDir != newConfigDir);
9788 if (!fSettingsFileIsNew)
9789 {
9790 /* perform real rename only if the machine is not new */
9791 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9792 if ( vrc == VERR_FILE_NOT_FOUND
9793 || vrc == VERR_PATH_NOT_FOUND)
9794 {
9795 /* create the parent directory, then retry renaming */
9796 Utf8Str parent(newConfigDir);
9797 parent.stripFilename();
9798 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9799 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9800 }
9801 if (RT_FAILURE(vrc))
9802 {
9803 rc = setErrorBoth(E_FAIL, vrc,
9804 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9805 configDir.c_str(),
9806 newConfigDir.c_str(),
9807 vrc);
9808 break;
9809 }
9810 /* delete subdirectories which are no longer needed */
9811 Utf8Str dir(configDir);
9812 dir.stripFilename();
9813 while (dir != newConfigBaseDir && dir != ".")
9814 {
9815 vrc = RTDirRemove(dir.c_str());
9816 if (RT_FAILURE(vrc))
9817 break;
9818 dir.stripFilename();
9819 }
9820 dirRenamed = true;
9821 }
9822 }
9823
9824 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9825 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9826
9827 /* then try to rename the settings file itself */
9828 if (newConfigFile != configFile)
9829 {
9830 /* get the path to old settings file in renamed directory */
9831 configFile = Utf8StrFmt("%s%c%s",
9832 newConfigDir.c_str(),
9833 RTPATH_DELIMITER,
9834 RTPathFilename(configFile.c_str()));
9835 if (!fSettingsFileIsNew)
9836 {
9837 /* perform real rename only if the machine is not new */
9838 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9839 if (RT_FAILURE(vrc))
9840 {
9841 rc = setErrorBoth(E_FAIL, vrc,
9842 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9843 configFile.c_str(),
9844 newConfigFile.c_str(),
9845 vrc);
9846 break;
9847 }
9848 fileRenamed = true;
9849 configFilePrev = configFile;
9850 configFilePrev += "-prev";
9851 newConfigFilePrev = newConfigFile;
9852 newConfigFilePrev += "-prev";
9853 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9854 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9855 if (NVRAMFile.isNotEmpty())
9856 {
9857 // in the NVRAM file path, replace the old directory with the new directory
9858 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9859 {
9860 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9861 NVRAMFile = newConfigDir + strNVRAMFile;
9862 }
9863 newNVRAMFile = newConfigFile;
9864 newNVRAMFile.stripSuffix();
9865 newNVRAMFile += ".nvram";
9866 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9867 }
9868 }
9869 }
9870
9871 // update m_strConfigFileFull amd mConfigFile
9872 mData->m_strConfigFileFull = newConfigFile;
9873 // compute the relative path too
9874 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9875
9876 // store the old and new so that VirtualBox::i_saveSettings() can update
9877 // the media registry
9878 if ( mData->mRegistered
9879 && (configDir != newConfigDir || configFile != newConfigFile))
9880 {
9881 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9882
9883 if (pfNeedsGlobalSaveSettings)
9884 *pfNeedsGlobalSaveSettings = true;
9885 }
9886
9887 // in the saved state file path, replace the old directory with the new directory
9888 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9889 {
9890 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9891 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9892 }
9893 if (newNVRAMFile.isNotEmpty())
9894 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9895
9896 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9897 if (mData->mFirstSnapshot)
9898 {
9899 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9900 newConfigDir.c_str());
9901 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9902 newConfigDir.c_str());
9903 }
9904 }
9905 while (0);
9906
9907 if (FAILED(rc))
9908 {
9909 /* silently try to rename everything back */
9910 if (fileRenamed)
9911 {
9912 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9913 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9914 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9915 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9916 }
9917 if (dirRenamed)
9918 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9919 }
9920
9921 if (FAILED(rc)) return rc;
9922 }
9923
9924 if (fSettingsFileIsNew)
9925 {
9926 /* create a virgin config file */
9927 int vrc = VINF_SUCCESS;
9928
9929 /* ensure the settings directory exists */
9930 Utf8Str path(mData->m_strConfigFileFull);
9931 path.stripFilename();
9932 if (!RTDirExists(path.c_str()))
9933 {
9934 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9935 if (RT_FAILURE(vrc))
9936 {
9937 return setErrorBoth(E_FAIL, vrc,
9938 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9939 path.c_str(),
9940 vrc);
9941 }
9942 }
9943
9944 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9945 path = Utf8Str(mData->m_strConfigFileFull);
9946 RTFILE f = NIL_RTFILE;
9947 vrc = RTFileOpen(&f, path.c_str(),
9948 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9949 if (RT_FAILURE(vrc))
9950 return setErrorBoth(E_FAIL, vrc,
9951 tr("Could not create the settings file '%s' (%Rrc)"),
9952 path.c_str(),
9953 vrc);
9954 RTFileClose(f);
9955 }
9956
9957 return rc;
9958}
9959
9960/**
9961 * Saves and commits machine data, user data and hardware data.
9962 *
9963 * Note that on failure, the data remains uncommitted.
9964 *
9965 * @a aFlags may combine the following flags:
9966 *
9967 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9968 * Used when saving settings after an operation that makes them 100%
9969 * correspond to the settings from the current snapshot.
9970 * - SaveS_Force: settings will be saved without doing a deep compare of the
9971 * settings structures. This is used when this is called because snapshots
9972 * have changed to avoid the overhead of the deep compare.
9973 *
9974 * @note Must be called from under this object's write lock. Locks children for
9975 * writing.
9976 *
9977 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9978 * initialized to false and that will be set to true by this function if
9979 * the caller must invoke VirtualBox::i_saveSettings() because the global
9980 * settings have changed. This will happen if a machine rename has been
9981 * saved and the global machine and media registries will therefore need
9982 * updating.
9983 * @param aFlags Flags.
9984 */
9985HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9986 int aFlags /*= 0*/)
9987{
9988 LogFlowThisFuncEnter();
9989
9990 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9991
9992 /* make sure child objects are unable to modify the settings while we are
9993 * saving them */
9994 i_ensureNoStateDependencies();
9995
9996 AssertReturn(!i_isSnapshotMachine(),
9997 E_FAIL);
9998
9999 if (!mData->mAccessible)
10000 return setError(VBOX_E_INVALID_VM_STATE,
10001 tr("The machine is not accessible, so cannot save settings"));
10002
10003 HRESULT rc = S_OK;
10004 bool fNeedsWrite = false;
10005
10006 /* First, prepare to save settings. It will care about renaming the
10007 * settings directory and file if the machine name was changed and about
10008 * creating a new settings file if this is a new machine. */
10009 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10010 if (FAILED(rc)) return rc;
10011
10012 // keep a pointer to the current settings structures
10013 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10014 settings::MachineConfigFile *pNewConfig = NULL;
10015
10016 try
10017 {
10018 // make a fresh one to have everyone write stuff into
10019 pNewConfig = new settings::MachineConfigFile(NULL);
10020 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10021
10022 // now go and copy all the settings data from COM to the settings structures
10023 // (this calls i_saveSettings() on all the COM objects in the machine)
10024 i_copyMachineDataToSettings(*pNewConfig);
10025
10026 if (aFlags & SaveS_ResetCurStateModified)
10027 {
10028 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10029 mData->mCurrentStateModified = FALSE;
10030 fNeedsWrite = true; // always, no need to compare
10031 }
10032 else if (aFlags & SaveS_Force)
10033 {
10034 fNeedsWrite = true; // always, no need to compare
10035 }
10036 else
10037 {
10038 if (!mData->mCurrentStateModified)
10039 {
10040 // do a deep compare of the settings that we just saved with the settings
10041 // previously stored in the config file; this invokes MachineConfigFile::operator==
10042 // which does a deep compare of all the settings, which is expensive but less expensive
10043 // than writing out XML in vain
10044 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10045
10046 // could still be modified if any settings changed
10047 mData->mCurrentStateModified = fAnySettingsChanged;
10048
10049 fNeedsWrite = fAnySettingsChanged;
10050 }
10051 else
10052 fNeedsWrite = true;
10053 }
10054
10055 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10056
10057 if (fNeedsWrite)
10058 // now spit it all out!
10059 pNewConfig->write(mData->m_strConfigFileFull);
10060
10061 mData->pMachineConfigFile = pNewConfig;
10062 delete pOldConfig;
10063 i_commit();
10064
10065 // after saving settings, we are no longer different from the XML on disk
10066 mData->flModifications = 0;
10067 }
10068 catch (HRESULT err)
10069 {
10070 // we assume that error info is set by the thrower
10071 rc = err;
10072
10073 // restore old config
10074 delete pNewConfig;
10075 mData->pMachineConfigFile = pOldConfig;
10076 }
10077 catch (...)
10078 {
10079 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10080 }
10081
10082 if (fNeedsWrite)
10083 {
10084 /* Fire the data change event, even on failure (since we've already
10085 * committed all data). This is done only for SessionMachines because
10086 * mutable Machine instances are always not registered (i.e. private
10087 * to the client process that creates them) and thus don't need to
10088 * inform callbacks. */
10089 if (i_isSessionMachine())
10090 mParent->i_onMachineDataChange(mData->mUuid);
10091 }
10092
10093 LogFlowThisFunc(("rc=%08X\n", rc));
10094 LogFlowThisFuncLeave();
10095 return rc;
10096}
10097
10098/**
10099 * Implementation for saving the machine settings into the given
10100 * settings::MachineConfigFile instance. This copies machine extradata
10101 * from the previous machine config file in the instance data, if any.
10102 *
10103 * This gets called from two locations:
10104 *
10105 * -- Machine::i_saveSettings(), during the regular XML writing;
10106 *
10107 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10108 * exported to OVF and we write the VirtualBox proprietary XML
10109 * into a <vbox:Machine> tag.
10110 *
10111 * This routine fills all the fields in there, including snapshots, *except*
10112 * for the following:
10113 *
10114 * -- fCurrentStateModified. There is some special logic associated with that.
10115 *
10116 * The caller can then call MachineConfigFile::write() or do something else
10117 * with it.
10118 *
10119 * Caller must hold the machine lock!
10120 *
10121 * This throws XML errors and HRESULT, so the caller must have a catch block!
10122 */
10123void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10124{
10125 // deep copy extradata, being extra careful with self assignment (the STL
10126 // map assignment on Mac OS X clang based Xcode isn't checking)
10127 if (&config != mData->pMachineConfigFile)
10128 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10129
10130 config.uuid = mData->mUuid;
10131
10132 // copy name, description, OS type, teleport, UTC etc.
10133 config.machineUserData = mUserData->s;
10134
10135 if ( mData->mMachineState == MachineState_Saved
10136 || mData->mMachineState == MachineState_Restoring
10137 // when doing certain snapshot operations we may or may not have
10138 // a saved state in the current state, so keep everything as is
10139 || ( ( mData->mMachineState == MachineState_Snapshotting
10140 || mData->mMachineState == MachineState_DeletingSnapshot
10141 || mData->mMachineState == MachineState_RestoringSnapshot)
10142 && (!mSSData->strStateFilePath.isEmpty())
10143 )
10144 )
10145 {
10146 Assert(!mSSData->strStateFilePath.isEmpty());
10147 /* try to make the file name relative to the settings file dir */
10148 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10149 }
10150 else
10151 {
10152 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10153 config.strStateFile.setNull();
10154 }
10155
10156 if (mData->mCurrentSnapshot)
10157 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10158 else
10159 config.uuidCurrentSnapshot.clear();
10160
10161 config.timeLastStateChange = mData->mLastStateChange;
10162 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10163 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10164
10165 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10166 if (FAILED(rc)) throw rc;
10167
10168 // save machine's media registry if this is VirtualBox 4.0 or later
10169 if (config.canHaveOwnMediaRegistry())
10170 {
10171 // determine machine folder
10172 Utf8Str strMachineFolder = i_getSettingsFileFull();
10173 strMachineFolder.stripFilename();
10174 mParent->i_saveMediaRegistry(config.mediaRegistry,
10175 i_getId(), // only media with registry ID == machine UUID
10176 strMachineFolder);
10177 // this throws HRESULT
10178 }
10179
10180 // save snapshots
10181 rc = i_saveAllSnapshots(config);
10182 if (FAILED(rc)) throw rc;
10183}
10184
10185/**
10186 * Saves all snapshots of the machine into the given machine config file. Called
10187 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10188 * @param config
10189 * @return
10190 */
10191HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10192{
10193 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10194
10195 HRESULT rc = S_OK;
10196
10197 try
10198 {
10199 config.llFirstSnapshot.clear();
10200
10201 if (mData->mFirstSnapshot)
10202 {
10203 // the settings use a list for "the first snapshot"
10204 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10205
10206 // get reference to the snapshot on the list and work on that
10207 // element straight in the list to avoid excessive copying later
10208 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10209 if (FAILED(rc)) throw rc;
10210 }
10211
10212// if (mType == IsSessionMachine)
10213// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10214
10215 }
10216 catch (HRESULT err)
10217 {
10218 /* we assume that error info is set by the thrower */
10219 rc = err;
10220 }
10221 catch (...)
10222 {
10223 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10224 }
10225
10226 return rc;
10227}
10228
10229/**
10230 * Saves the VM hardware configuration. It is assumed that the
10231 * given node is empty.
10232 *
10233 * @param data Reference to the settings object for the hardware config.
10234 * @param pDbg Pointer to the settings object for the debugging config
10235 * which happens to live in mHWData.
10236 * @param pAutostart Pointer to the settings object for the autostart config
10237 * which happens to live in mHWData.
10238 */
10239HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10240 settings::Autostart *pAutostart)
10241{
10242 HRESULT rc = S_OK;
10243
10244 try
10245 {
10246 /* The hardware version attribute (optional).
10247 Automatically upgrade from 1 to current default hardware version
10248 when there is no saved state. (ugly!) */
10249 if ( mHWData->mHWVersion == "1"
10250 && mSSData->strStateFilePath.isEmpty()
10251 )
10252 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10253
10254 data.strVersion = mHWData->mHWVersion;
10255 data.uuid = mHWData->mHardwareUUID;
10256
10257 // CPU
10258 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10259 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10260 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10261 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10262 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10263 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10264 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10265 data.fPAE = !!mHWData->mPAEEnabled;
10266 data.enmLongMode = mHWData->mLongMode;
10267 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10268 data.fAPIC = !!mHWData->mAPIC;
10269 data.fX2APIC = !!mHWData->mX2APIC;
10270 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10271 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10272 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10273 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10274 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10275 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10276 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10277 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10278 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10279 data.cCPUs = mHWData->mCPUCount;
10280 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10281 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10282 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10283 data.strCpuProfile = mHWData->mCpuProfile;
10284
10285 data.llCpus.clear();
10286 if (data.fCpuHotPlug)
10287 {
10288 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10289 {
10290 if (mHWData->mCPUAttached[idx])
10291 {
10292 settings::Cpu cpu;
10293 cpu.ulId = idx;
10294 data.llCpus.push_back(cpu);
10295 }
10296 }
10297 }
10298
10299 /* Standard and Extended CPUID leafs. */
10300 data.llCpuIdLeafs.clear();
10301 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10302
10303 // memory
10304 data.ulMemorySizeMB = mHWData->mMemorySize;
10305 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10306
10307 // firmware
10308 data.firmwareType = mHWData->mFirmwareType;
10309
10310 // HID
10311 data.pointingHIDType = mHWData->mPointingHIDType;
10312 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10313
10314 // chipset
10315 data.chipsetType = mHWData->mChipsetType;
10316
10317 // paravirt
10318 data.paravirtProvider = mHWData->mParavirtProvider;
10319 data.strParavirtDebug = mHWData->mParavirtDebug;
10320
10321 // emulated USB card reader
10322 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10323
10324 // HPET
10325 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10326
10327 // boot order
10328 data.mapBootOrder.clear();
10329 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10330 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10331
10332 // display
10333 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10334 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10335 data.cMonitors = mHWData->mMonitorCount;
10336 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10337 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10338
10339 /* VRDEServer settings (optional) */
10340 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10341 if (FAILED(rc)) throw rc;
10342
10343 /* BIOS settings (required) */
10344 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10345 if (FAILED(rc)) throw rc;
10346
10347 /* Recording settings (required) */
10348 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10349 if (FAILED(rc)) throw rc;
10350
10351 /* USB Controller (required) */
10352 data.usbSettings.llUSBControllers.clear();
10353 for (USBControllerList::const_iterator
10354 it = mUSBControllers->begin();
10355 it != mUSBControllers->end();
10356 ++it)
10357 {
10358 ComObjPtr<USBController> ctrl = *it;
10359 settings::USBController settingsCtrl;
10360
10361 settingsCtrl.strName = ctrl->i_getName();
10362 settingsCtrl.enmType = ctrl->i_getControllerType();
10363
10364 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10365 }
10366
10367 /* USB device filters (required) */
10368 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10369 if (FAILED(rc)) throw rc;
10370
10371 /* Network adapters (required) */
10372 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10373 data.llNetworkAdapters.clear();
10374 /* Write out only the nominal number of network adapters for this
10375 * chipset type. Since Machine::commit() hasn't been called there
10376 * may be extra NIC settings in the vector. */
10377 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10378 {
10379 settings::NetworkAdapter nic;
10380 nic.ulSlot = (uint32_t)slot;
10381 /* paranoia check... must not be NULL, but must not crash either. */
10382 if (mNetworkAdapters[slot])
10383 {
10384 if (mNetworkAdapters[slot]->i_hasDefaults())
10385 continue;
10386
10387 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10388 if (FAILED(rc)) throw rc;
10389
10390 data.llNetworkAdapters.push_back(nic);
10391 }
10392 }
10393
10394 /* Serial ports */
10395 data.llSerialPorts.clear();
10396 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10397 {
10398 if (mSerialPorts[slot]->i_hasDefaults())
10399 continue;
10400
10401 settings::SerialPort s;
10402 s.ulSlot = slot;
10403 rc = mSerialPorts[slot]->i_saveSettings(s);
10404 if (FAILED(rc)) return rc;
10405
10406 data.llSerialPorts.push_back(s);
10407 }
10408
10409 /* Parallel ports */
10410 data.llParallelPorts.clear();
10411 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10412 {
10413 if (mParallelPorts[slot]->i_hasDefaults())
10414 continue;
10415
10416 settings::ParallelPort p;
10417 p.ulSlot = slot;
10418 rc = mParallelPorts[slot]->i_saveSettings(p);
10419 if (FAILED(rc)) return rc;
10420
10421 data.llParallelPorts.push_back(p);
10422 }
10423
10424 /* Audio adapter */
10425 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10426 if (FAILED(rc)) return rc;
10427
10428 rc = i_saveStorageControllers(data.storage);
10429 if (FAILED(rc)) return rc;
10430
10431 /* Shared folders */
10432 data.llSharedFolders.clear();
10433 for (HWData::SharedFolderList::const_iterator
10434 it = mHWData->mSharedFolders.begin();
10435 it != mHWData->mSharedFolders.end();
10436 ++it)
10437 {
10438 SharedFolder *pSF = *it;
10439 AutoCaller sfCaller(pSF);
10440 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10441 settings::SharedFolder sf;
10442 sf.strName = pSF->i_getName();
10443 sf.strHostPath = pSF->i_getHostPath();
10444 sf.fWritable = !!pSF->i_isWritable();
10445 sf.fAutoMount = !!pSF->i_isAutoMounted();
10446 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10447
10448 data.llSharedFolders.push_back(sf);
10449 }
10450
10451 // clipboard
10452 data.clipboardMode = mHWData->mClipboardMode;
10453 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10454
10455 // drag'n'drop
10456 data.dndMode = mHWData->mDnDMode;
10457
10458 /* Guest */
10459 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10460
10461 // IO settings
10462 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10463 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10464
10465 /* BandwidthControl (required) */
10466 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10467 if (FAILED(rc)) throw rc;
10468
10469 /* Host PCI devices */
10470 data.pciAttachments.clear();
10471 for (HWData::PCIDeviceAssignmentList::const_iterator
10472 it = mHWData->mPCIDeviceAssignments.begin();
10473 it != mHWData->mPCIDeviceAssignments.end();
10474 ++it)
10475 {
10476 ComObjPtr<PCIDeviceAttachment> pda = *it;
10477 settings::HostPCIDeviceAttachment hpda;
10478
10479 rc = pda->i_saveSettings(hpda);
10480 if (FAILED(rc)) throw rc;
10481
10482 data.pciAttachments.push_back(hpda);
10483 }
10484
10485 // guest properties
10486 data.llGuestProperties.clear();
10487#ifdef VBOX_WITH_GUEST_PROPS
10488 for (HWData::GuestPropertyMap::const_iterator
10489 it = mHWData->mGuestProperties.begin();
10490 it != mHWData->mGuestProperties.end();
10491 ++it)
10492 {
10493 HWData::GuestProperty property = it->second;
10494
10495 /* Remove transient guest properties at shutdown unless we
10496 * are saving state. Note that restoring snapshot intentionally
10497 * keeps them, they will be removed if appropriate once the final
10498 * machine state is set (as crashes etc. need to work). */
10499 if ( ( mData->mMachineState == MachineState_PoweredOff
10500 || mData->mMachineState == MachineState_Aborted
10501 || mData->mMachineState == MachineState_Teleported)
10502 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10503 continue;
10504 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10505 prop.strName = it->first;
10506 prop.strValue = property.strValue;
10507 prop.timestamp = property.mTimestamp;
10508 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10509 GuestPropWriteFlags(property.mFlags, szFlags);
10510 prop.strFlags = szFlags;
10511
10512 data.llGuestProperties.push_back(prop);
10513 }
10514
10515 /* I presume this doesn't require a backup(). */
10516 mData->mGuestPropertiesModified = FALSE;
10517#endif /* VBOX_WITH_GUEST_PROPS defined */
10518
10519 *pDbg = mHWData->mDebugging;
10520 *pAutostart = mHWData->mAutostart;
10521
10522 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10523 }
10524 catch (std::bad_alloc &)
10525 {
10526 return E_OUTOFMEMORY;
10527 }
10528
10529 AssertComRC(rc);
10530 return rc;
10531}
10532
10533/**
10534 * Saves the storage controller configuration.
10535 *
10536 * @param data storage settings.
10537 */
10538HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10539{
10540 data.llStorageControllers.clear();
10541
10542 for (StorageControllerList::const_iterator
10543 it = mStorageControllers->begin();
10544 it != mStorageControllers->end();
10545 ++it)
10546 {
10547 HRESULT rc;
10548 ComObjPtr<StorageController> pCtl = *it;
10549
10550 settings::StorageController ctl;
10551 ctl.strName = pCtl->i_getName();
10552 ctl.controllerType = pCtl->i_getControllerType();
10553 ctl.storageBus = pCtl->i_getStorageBus();
10554 ctl.ulInstance = pCtl->i_getInstance();
10555 ctl.fBootable = pCtl->i_getBootable();
10556
10557 /* Save the port count. */
10558 ULONG portCount;
10559 rc = pCtl->COMGETTER(PortCount)(&portCount);
10560 ComAssertComRCRet(rc, rc);
10561 ctl.ulPortCount = portCount;
10562
10563 /* Save fUseHostIOCache */
10564 BOOL fUseHostIOCache;
10565 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10566 ComAssertComRCRet(rc, rc);
10567 ctl.fUseHostIOCache = !!fUseHostIOCache;
10568
10569 /* save the devices now. */
10570 rc = i_saveStorageDevices(pCtl, ctl);
10571 ComAssertComRCRet(rc, rc);
10572
10573 data.llStorageControllers.push_back(ctl);
10574 }
10575
10576 return S_OK;
10577}
10578
10579/**
10580 * Saves the hard disk configuration.
10581 */
10582HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10583 settings::StorageController &data)
10584{
10585 MediumAttachmentList atts;
10586
10587 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10588 if (FAILED(rc)) return rc;
10589
10590 data.llAttachedDevices.clear();
10591 for (MediumAttachmentList::const_iterator
10592 it = atts.begin();
10593 it != atts.end();
10594 ++it)
10595 {
10596 settings::AttachedDevice dev;
10597 IMediumAttachment *iA = *it;
10598 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10599 Medium *pMedium = pAttach->i_getMedium();
10600
10601 dev.deviceType = pAttach->i_getType();
10602 dev.lPort = pAttach->i_getPort();
10603 dev.lDevice = pAttach->i_getDevice();
10604 dev.fPassThrough = pAttach->i_getPassthrough();
10605 dev.fHotPluggable = pAttach->i_getHotPluggable();
10606 if (pMedium)
10607 {
10608 if (pMedium->i_isHostDrive())
10609 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10610 else
10611 dev.uuid = pMedium->i_getId();
10612 dev.fTempEject = pAttach->i_getTempEject();
10613 dev.fNonRotational = pAttach->i_getNonRotational();
10614 dev.fDiscard = pAttach->i_getDiscard();
10615 }
10616
10617 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10618
10619 data.llAttachedDevices.push_back(dev);
10620 }
10621
10622 return S_OK;
10623}
10624
10625/**
10626 * Saves machine state settings as defined by aFlags
10627 * (SaveSTS_* values).
10628 *
10629 * @param aFlags Combination of SaveSTS_* flags.
10630 *
10631 * @note Locks objects for writing.
10632 */
10633HRESULT Machine::i_saveStateSettings(int aFlags)
10634{
10635 if (aFlags == 0)
10636 return S_OK;
10637
10638 AutoCaller autoCaller(this);
10639 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10640
10641 /* This object's write lock is also necessary to serialize file access
10642 * (prevent concurrent reads and writes) */
10643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10644
10645 HRESULT rc = S_OK;
10646
10647 Assert(mData->pMachineConfigFile);
10648
10649 try
10650 {
10651 if (aFlags & SaveSTS_CurStateModified)
10652 mData->pMachineConfigFile->fCurrentStateModified = true;
10653
10654 if (aFlags & SaveSTS_StateFilePath)
10655 {
10656 if (!mSSData->strStateFilePath.isEmpty())
10657 /* try to make the file name relative to the settings file dir */
10658 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10659 else
10660 mData->pMachineConfigFile->strStateFile.setNull();
10661 }
10662
10663 if (aFlags & SaveSTS_StateTimeStamp)
10664 {
10665 Assert( mData->mMachineState != MachineState_Aborted
10666 || mSSData->strStateFilePath.isEmpty());
10667
10668 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10669
10670 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10671/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10672 }
10673
10674 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10675 }
10676 catch (...)
10677 {
10678 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10679 }
10680
10681 return rc;
10682}
10683
10684/**
10685 * Ensures that the given medium is added to a media registry. If this machine
10686 * was created with 4.0 or later, then the machine registry is used. Otherwise
10687 * the global VirtualBox media registry is used.
10688 *
10689 * Caller must NOT hold machine lock, media tree or any medium locks!
10690 *
10691 * @param pMedium
10692 */
10693void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10694{
10695 /* Paranoia checks: do not hold machine or media tree locks. */
10696 AssertReturnVoid(!isWriteLockOnCurrentThread());
10697 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10698
10699 ComObjPtr<Medium> pBase;
10700 {
10701 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10702 pBase = pMedium->i_getBase();
10703 }
10704
10705 /* Paranoia checks: do not hold medium locks. */
10706 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10707 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10708
10709 // decide which medium registry to use now that the medium is attached:
10710 Guid uuid;
10711 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10712 if (fCanHaveOwnMediaRegistry)
10713 // machine XML is VirtualBox 4.0 or higher:
10714 uuid = i_getId(); // machine UUID
10715 else
10716 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10717
10718 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10719 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10720 if (pMedium->i_addRegistry(uuid))
10721 mParent->i_markRegistryModified(uuid);
10722
10723 /* For more complex hard disk structures it can happen that the base
10724 * medium isn't yet associated with any medium registry. Do that now. */
10725 if (pMedium != pBase)
10726 {
10727 /* Tree lock needed by Medium::addRegistry when recursing. */
10728 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10729 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10730 {
10731 treeLock.release();
10732 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10733 treeLock.acquire();
10734 }
10735 if (pBase->i_addRegistryRecursive(uuid))
10736 {
10737 treeLock.release();
10738 mParent->i_markRegistryModified(uuid);
10739 }
10740 }
10741}
10742
10743/**
10744 * Creates differencing hard disks for all normal hard disks attached to this
10745 * machine and a new set of attachments to refer to created disks.
10746 *
10747 * Used when taking a snapshot or when deleting the current state. Gets called
10748 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10749 *
10750 * This method assumes that mMediumAttachments contains the original hard disk
10751 * attachments it needs to create diffs for. On success, these attachments will
10752 * be replaced with the created diffs.
10753 *
10754 * Attachments with non-normal hard disks are left as is.
10755 *
10756 * If @a aOnline is @c false then the original hard disks that require implicit
10757 * diffs will be locked for reading. Otherwise it is assumed that they are
10758 * already locked for writing (when the VM was started). Note that in the latter
10759 * case it is responsibility of the caller to lock the newly created diffs for
10760 * writing if this method succeeds.
10761 *
10762 * @param aProgress Progress object to run (must contain at least as
10763 * many operations left as the number of hard disks
10764 * attached).
10765 * @param aWeight Weight of this operation.
10766 * @param aOnline Whether the VM was online prior to this operation.
10767 *
10768 * @note The progress object is not marked as completed, neither on success nor
10769 * on failure. This is a responsibility of the caller.
10770 *
10771 * @note Locks this object and the media tree for writing.
10772 */
10773HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10774 ULONG aWeight,
10775 bool aOnline)
10776{
10777 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10778
10779 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10780 AssertReturn(!!pProgressControl, E_INVALIDARG);
10781
10782 AutoCaller autoCaller(this);
10783 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10784
10785 AutoMultiWriteLock2 alock(this->lockHandle(),
10786 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10787
10788 /* must be in a protective state because we release the lock below */
10789 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10790 || mData->mMachineState == MachineState_OnlineSnapshotting
10791 || mData->mMachineState == MachineState_LiveSnapshotting
10792 || mData->mMachineState == MachineState_RestoringSnapshot
10793 || mData->mMachineState == MachineState_DeletingSnapshot
10794 , E_FAIL);
10795
10796 HRESULT rc = S_OK;
10797
10798 // use appropriate locked media map (online or offline)
10799 MediumLockListMap lockedMediaOffline;
10800 MediumLockListMap *lockedMediaMap;
10801 if (aOnline)
10802 lockedMediaMap = &mData->mSession.mLockedMedia;
10803 else
10804 lockedMediaMap = &lockedMediaOffline;
10805
10806 try
10807 {
10808 if (!aOnline)
10809 {
10810 /* lock all attached hard disks early to detect "in use"
10811 * situations before creating actual diffs */
10812 for (MediumAttachmentList::const_iterator
10813 it = mMediumAttachments->begin();
10814 it != mMediumAttachments->end();
10815 ++it)
10816 {
10817 MediumAttachment *pAtt = *it;
10818 if (pAtt->i_getType() == DeviceType_HardDisk)
10819 {
10820 Medium *pMedium = pAtt->i_getMedium();
10821 Assert(pMedium);
10822
10823 MediumLockList *pMediumLockList(new MediumLockList());
10824 alock.release();
10825 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10826 NULL /* pToLockWrite */,
10827 false /* fMediumLockWriteAll */,
10828 NULL,
10829 *pMediumLockList);
10830 alock.acquire();
10831 if (FAILED(rc))
10832 {
10833 delete pMediumLockList;
10834 throw rc;
10835 }
10836 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10837 if (FAILED(rc))
10838 {
10839 throw setError(rc,
10840 tr("Collecting locking information for all attached media failed"));
10841 }
10842 }
10843 }
10844
10845 /* Now lock all media. If this fails, nothing is locked. */
10846 alock.release();
10847 rc = lockedMediaMap->Lock();
10848 alock.acquire();
10849 if (FAILED(rc))
10850 {
10851 throw setError(rc,
10852 tr("Locking of attached media failed"));
10853 }
10854 }
10855
10856 /* remember the current list (note that we don't use backup() since
10857 * mMediumAttachments may be already backed up) */
10858 MediumAttachmentList atts = *mMediumAttachments.data();
10859
10860 /* start from scratch */
10861 mMediumAttachments->clear();
10862
10863 /* go through remembered attachments and create diffs for normal hard
10864 * disks and attach them */
10865 for (MediumAttachmentList::const_iterator
10866 it = atts.begin();
10867 it != atts.end();
10868 ++it)
10869 {
10870 MediumAttachment *pAtt = *it;
10871
10872 DeviceType_T devType = pAtt->i_getType();
10873 Medium *pMedium = pAtt->i_getMedium();
10874
10875 if ( devType != DeviceType_HardDisk
10876 || pMedium == NULL
10877 || pMedium->i_getType() != MediumType_Normal)
10878 {
10879 /* copy the attachment as is */
10880
10881 /** @todo the progress object created in SessionMachine::TakeSnaphot
10882 * only expects operations for hard disks. Later other
10883 * device types need to show up in the progress as well. */
10884 if (devType == DeviceType_HardDisk)
10885 {
10886 if (pMedium == NULL)
10887 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10888 aWeight); // weight
10889 else
10890 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10891 pMedium->i_getBase()->i_getName().c_str()).raw(),
10892 aWeight); // weight
10893 }
10894
10895 mMediumAttachments->push_back(pAtt);
10896 continue;
10897 }
10898
10899 /* need a diff */
10900 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10901 pMedium->i_getBase()->i_getName().c_str()).raw(),
10902 aWeight); // weight
10903
10904 Utf8Str strFullSnapshotFolder;
10905 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10906
10907 ComObjPtr<Medium> diff;
10908 diff.createObject();
10909 // store the diff in the same registry as the parent
10910 // (this cannot fail here because we can't create implicit diffs for
10911 // unregistered images)
10912 Guid uuidRegistryParent;
10913 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10914 Assert(fInRegistry); NOREF(fInRegistry);
10915 rc = diff->init(mParent,
10916 pMedium->i_getPreferredDiffFormat(),
10917 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10918 uuidRegistryParent,
10919 DeviceType_HardDisk);
10920 if (FAILED(rc)) throw rc;
10921
10922 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10923 * the push_back? Looks like we're going to release medium with the
10924 * wrong kind of lock (general issue with if we fail anywhere at all)
10925 * and an orphaned VDI in the snapshots folder. */
10926
10927 /* update the appropriate lock list */
10928 MediumLockList *pMediumLockList;
10929 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10930 AssertComRCThrowRC(rc);
10931 if (aOnline)
10932 {
10933 alock.release();
10934 /* The currently attached medium will be read-only, change
10935 * the lock type to read. */
10936 rc = pMediumLockList->Update(pMedium, false);
10937 alock.acquire();
10938 AssertComRCThrowRC(rc);
10939 }
10940
10941 /* release the locks before the potentially lengthy operation */
10942 alock.release();
10943 rc = pMedium->i_createDiffStorage(diff,
10944 pMedium->i_getPreferredDiffVariant(),
10945 pMediumLockList,
10946 NULL /* aProgress */,
10947 true /* aWait */,
10948 false /* aNotify */);
10949 alock.acquire();
10950 if (FAILED(rc)) throw rc;
10951
10952 /* actual lock list update is done in Machine::i_commitMedia */
10953
10954 rc = diff->i_addBackReference(mData->mUuid);
10955 AssertComRCThrowRC(rc);
10956
10957 /* add a new attachment */
10958 ComObjPtr<MediumAttachment> attachment;
10959 attachment.createObject();
10960 rc = attachment->init(this,
10961 diff,
10962 pAtt->i_getControllerName(),
10963 pAtt->i_getPort(),
10964 pAtt->i_getDevice(),
10965 DeviceType_HardDisk,
10966 true /* aImplicit */,
10967 false /* aPassthrough */,
10968 false /* aTempEject */,
10969 pAtt->i_getNonRotational(),
10970 pAtt->i_getDiscard(),
10971 pAtt->i_getHotPluggable(),
10972 pAtt->i_getBandwidthGroup());
10973 if (FAILED(rc)) throw rc;
10974
10975 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10976 AssertComRCThrowRC(rc);
10977 mMediumAttachments->push_back(attachment);
10978 }
10979 }
10980 catch (HRESULT aRC) { rc = aRC; }
10981
10982 /* unlock all hard disks we locked when there is no VM */
10983 if (!aOnline)
10984 {
10985 ErrorInfoKeeper eik;
10986
10987 HRESULT rc1 = lockedMediaMap->Clear();
10988 AssertComRC(rc1);
10989 }
10990
10991 return rc;
10992}
10993
10994/**
10995 * Deletes implicit differencing hard disks created either by
10996 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10997 * mMediumAttachments.
10998 *
10999 * Note that to delete hard disks created by #attachDevice() this method is
11000 * called from #i_rollbackMedia() when the changes are rolled back.
11001 *
11002 * @note Locks this object and the media tree for writing.
11003 */
11004HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11005{
11006 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11007
11008 AutoCaller autoCaller(this);
11009 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11010
11011 AutoMultiWriteLock2 alock(this->lockHandle(),
11012 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11013
11014 /* We absolutely must have backed up state. */
11015 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11016
11017 /* Check if there are any implicitly created diff images. */
11018 bool fImplicitDiffs = false;
11019 for (MediumAttachmentList::const_iterator
11020 it = mMediumAttachments->begin();
11021 it != mMediumAttachments->end();
11022 ++it)
11023 {
11024 const ComObjPtr<MediumAttachment> &pAtt = *it;
11025 if (pAtt->i_isImplicit())
11026 {
11027 fImplicitDiffs = true;
11028 break;
11029 }
11030 }
11031 /* If there is nothing to do, leave early. This saves lots of image locking
11032 * effort. It also avoids a MachineStateChanged event without real reason.
11033 * This is important e.g. when loading a VM config, because there should be
11034 * no events. Otherwise API clients can become thoroughly confused for
11035 * inaccessible VMs (the code for loading VM configs uses this method for
11036 * cleanup if the config makes no sense), as they take such events as an
11037 * indication that the VM is alive, and they would force the VM config to
11038 * be reread, leading to an endless loop. */
11039 if (!fImplicitDiffs)
11040 return S_OK;
11041
11042 HRESULT rc = S_OK;
11043 MachineState_T oldState = mData->mMachineState;
11044
11045 /* will release the lock before the potentially lengthy operation,
11046 * so protect with the special state (unless already protected) */
11047 if ( oldState != MachineState_Snapshotting
11048 && oldState != MachineState_OnlineSnapshotting
11049 && oldState != MachineState_LiveSnapshotting
11050 && oldState != MachineState_RestoringSnapshot
11051 && oldState != MachineState_DeletingSnapshot
11052 && oldState != MachineState_DeletingSnapshotOnline
11053 && oldState != MachineState_DeletingSnapshotPaused
11054 )
11055 i_setMachineState(MachineState_SettingUp);
11056
11057 // use appropriate locked media map (online or offline)
11058 MediumLockListMap lockedMediaOffline;
11059 MediumLockListMap *lockedMediaMap;
11060 if (aOnline)
11061 lockedMediaMap = &mData->mSession.mLockedMedia;
11062 else
11063 lockedMediaMap = &lockedMediaOffline;
11064
11065 try
11066 {
11067 if (!aOnline)
11068 {
11069 /* lock all attached hard disks early to detect "in use"
11070 * situations before deleting actual diffs */
11071 for (MediumAttachmentList::const_iterator
11072 it = mMediumAttachments->begin();
11073 it != mMediumAttachments->end();
11074 ++it)
11075 {
11076 MediumAttachment *pAtt = *it;
11077 if (pAtt->i_getType() == DeviceType_HardDisk)
11078 {
11079 Medium *pMedium = pAtt->i_getMedium();
11080 Assert(pMedium);
11081
11082 MediumLockList *pMediumLockList(new MediumLockList());
11083 alock.release();
11084 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11085 NULL /* pToLockWrite */,
11086 false /* fMediumLockWriteAll */,
11087 NULL,
11088 *pMediumLockList);
11089 alock.acquire();
11090
11091 if (FAILED(rc))
11092 {
11093 delete pMediumLockList;
11094 throw rc;
11095 }
11096
11097 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11098 if (FAILED(rc))
11099 throw rc;
11100 }
11101 }
11102
11103 if (FAILED(rc))
11104 throw rc;
11105 } // end of offline
11106
11107 /* Lock lists are now up to date and include implicitly created media */
11108
11109 /* Go through remembered attachments and delete all implicitly created
11110 * diffs and fix up the attachment information */
11111 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11112 MediumAttachmentList implicitAtts;
11113 for (MediumAttachmentList::const_iterator
11114 it = mMediumAttachments->begin();
11115 it != mMediumAttachments->end();
11116 ++it)
11117 {
11118 ComObjPtr<MediumAttachment> pAtt = *it;
11119 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11120 if (pMedium.isNull())
11121 continue;
11122
11123 // Implicit attachments go on the list for deletion and back references are removed.
11124 if (pAtt->i_isImplicit())
11125 {
11126 /* Deassociate and mark for deletion */
11127 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11128 rc = pMedium->i_removeBackReference(mData->mUuid);
11129 if (FAILED(rc))
11130 throw rc;
11131 implicitAtts.push_back(pAtt);
11132 continue;
11133 }
11134
11135 /* Was this medium attached before? */
11136 if (!i_findAttachment(oldAtts, pMedium))
11137 {
11138 /* no: de-associate */
11139 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11140 rc = pMedium->i_removeBackReference(mData->mUuid);
11141 if (FAILED(rc))
11142 throw rc;
11143 continue;
11144 }
11145 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11146 }
11147
11148 /* If there are implicit attachments to delete, throw away the lock
11149 * map contents (which will unlock all media) since the medium
11150 * attachments will be rolled back. Below we need to completely
11151 * recreate the lock map anyway since it is infinitely complex to
11152 * do this incrementally (would need reconstructing each attachment
11153 * change, which would be extremely hairy). */
11154 if (implicitAtts.size() != 0)
11155 {
11156 ErrorInfoKeeper eik;
11157
11158 HRESULT rc1 = lockedMediaMap->Clear();
11159 AssertComRC(rc1);
11160 }
11161
11162 /* rollback hard disk changes */
11163 mMediumAttachments.rollback();
11164
11165 MultiResult mrc(S_OK);
11166
11167 // Delete unused implicit diffs.
11168 if (implicitAtts.size() != 0)
11169 {
11170 alock.release();
11171
11172 for (MediumAttachmentList::const_iterator
11173 it = implicitAtts.begin();
11174 it != implicitAtts.end();
11175 ++it)
11176 {
11177 // Remove medium associated with this attachment.
11178 ComObjPtr<MediumAttachment> pAtt = *it;
11179 Assert(pAtt);
11180 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11181 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11182 Assert(pMedium);
11183
11184 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11185 // continue on delete failure, just collect error messages
11186 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11187 pMedium->i_getLocationFull().c_str() ));
11188 mrc = rc;
11189 }
11190 // Clear the list of deleted implicit attachments now, while not
11191 // holding the lock, as it will ultimately trigger Medium::uninit()
11192 // calls which assume that the media tree lock isn't held.
11193 implicitAtts.clear();
11194
11195 alock.acquire();
11196
11197 /* if there is a VM recreate media lock map as mentioned above,
11198 * otherwise it is a waste of time and we leave things unlocked */
11199 if (aOnline)
11200 {
11201 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11202 /* must never be NULL, but better safe than sorry */
11203 if (!pMachine.isNull())
11204 {
11205 alock.release();
11206 rc = mData->mSession.mMachine->i_lockMedia();
11207 alock.acquire();
11208 if (FAILED(rc))
11209 throw rc;
11210 }
11211 }
11212 }
11213 }
11214 catch (HRESULT aRC) {rc = aRC;}
11215
11216 if (mData->mMachineState == MachineState_SettingUp)
11217 i_setMachineState(oldState);
11218
11219 /* unlock all hard disks we locked when there is no VM */
11220 if (!aOnline)
11221 {
11222 ErrorInfoKeeper eik;
11223
11224 HRESULT rc1 = lockedMediaMap->Clear();
11225 AssertComRC(rc1);
11226 }
11227
11228 return rc;
11229}
11230
11231
11232/**
11233 * Looks through the given list of media attachments for one with the given parameters
11234 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11235 * can be searched as well if needed.
11236 *
11237 * @param ll
11238 * @param aControllerName
11239 * @param aControllerPort
11240 * @param aDevice
11241 * @return
11242 */
11243MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11244 const Utf8Str &aControllerName,
11245 LONG aControllerPort,
11246 LONG aDevice)
11247{
11248 for (MediumAttachmentList::const_iterator
11249 it = ll.begin();
11250 it != ll.end();
11251 ++it)
11252 {
11253 MediumAttachment *pAttach = *it;
11254 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11255 return pAttach;
11256 }
11257
11258 return NULL;
11259}
11260
11261/**
11262 * Looks through the given list of media attachments for one with the given parameters
11263 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11264 * can be searched as well if needed.
11265 *
11266 * @param ll
11267 * @param pMedium
11268 * @return
11269 */
11270MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11271 ComObjPtr<Medium> pMedium)
11272{
11273 for (MediumAttachmentList::const_iterator
11274 it = ll.begin();
11275 it != ll.end();
11276 ++it)
11277 {
11278 MediumAttachment *pAttach = *it;
11279 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11280 if (pMediumThis == pMedium)
11281 return pAttach;
11282 }
11283
11284 return NULL;
11285}
11286
11287/**
11288 * Looks through the given list of media attachments for one with the given parameters
11289 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11290 * can be searched as well if needed.
11291 *
11292 * @param ll
11293 * @param id
11294 * @return
11295 */
11296MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11297 Guid &id)
11298{
11299 for (MediumAttachmentList::const_iterator
11300 it = ll.begin();
11301 it != ll.end();
11302 ++it)
11303 {
11304 MediumAttachment *pAttach = *it;
11305 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11306 if (pMediumThis->i_getId() == id)
11307 return pAttach;
11308 }
11309
11310 return NULL;
11311}
11312
11313/**
11314 * Main implementation for Machine::DetachDevice. This also gets called
11315 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11316 *
11317 * @param pAttach Medium attachment to detach.
11318 * @param writeLock Machine write lock which the caller must have locked once.
11319 * This may be released temporarily in here.
11320 * @param pSnapshot If NULL, then the detachment is for the current machine.
11321 * Otherwise this is for a SnapshotMachine, and this must be
11322 * its snapshot.
11323 * @return
11324 */
11325HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11326 AutoWriteLock &writeLock,
11327 Snapshot *pSnapshot)
11328{
11329 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11330 DeviceType_T mediumType = pAttach->i_getType();
11331
11332 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11333
11334 if (pAttach->i_isImplicit())
11335 {
11336 /* attempt to implicitly delete the implicitly created diff */
11337
11338 /// @todo move the implicit flag from MediumAttachment to Medium
11339 /// and forbid any hard disk operation when it is implicit. Or maybe
11340 /// a special media state for it to make it even more simple.
11341
11342 Assert(mMediumAttachments.isBackedUp());
11343
11344 /* will release the lock before the potentially lengthy operation, so
11345 * protect with the special state */
11346 MachineState_T oldState = mData->mMachineState;
11347 i_setMachineState(MachineState_SettingUp);
11348
11349 writeLock.release();
11350
11351 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11352 true /*aWait*/,
11353 false /*aNotify*/);
11354
11355 writeLock.acquire();
11356
11357 i_setMachineState(oldState);
11358
11359 if (FAILED(rc)) return rc;
11360 }
11361
11362 i_setModified(IsModified_Storage);
11363 mMediumAttachments.backup();
11364 mMediumAttachments->remove(pAttach);
11365
11366 if (!oldmedium.isNull())
11367 {
11368 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11369 if (pSnapshot)
11370 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11371 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11372 else if (mediumType != DeviceType_HardDisk)
11373 oldmedium->i_removeBackReference(mData->mUuid);
11374 }
11375
11376 return S_OK;
11377}
11378
11379/**
11380 * Goes thru all media of the given list and
11381 *
11382 * 1) calls i_detachDevice() on each of them for this machine and
11383 * 2) adds all Medium objects found in the process to the given list,
11384 * depending on cleanupMode.
11385 *
11386 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11387 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11388 * media to the list.
11389 *
11390 * This gets called from Machine::Unregister, both for the actual Machine and
11391 * the SnapshotMachine objects that might be found in the snapshots.
11392 *
11393 * Requires caller and locking. The machine lock must be passed in because it
11394 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11395 *
11396 * @param writeLock Machine lock from top-level caller; this gets passed to
11397 * i_detachDevice.
11398 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11399 * object if called for a SnapshotMachine.
11400 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11401 * added to llMedia; if Full, then all media get added;
11402 * otherwise no media get added.
11403 * @param llMedia Caller's list to receive Medium objects which got detached so
11404 * caller can close() them, depending on cleanupMode.
11405 * @return
11406 */
11407HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11408 Snapshot *pSnapshot,
11409 CleanupMode_T cleanupMode,
11410 MediaList &llMedia)
11411{
11412 Assert(isWriteLockOnCurrentThread());
11413
11414 HRESULT rc;
11415
11416 // make a temporary list because i_detachDevice invalidates iterators into
11417 // mMediumAttachments
11418 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11419
11420 for (MediumAttachmentList::iterator
11421 it = llAttachments2.begin();
11422 it != llAttachments2.end();
11423 ++it)
11424 {
11425 ComObjPtr<MediumAttachment> &pAttach = *it;
11426 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11427
11428 if (!pMedium.isNull())
11429 {
11430 AutoCaller mac(pMedium);
11431 if (FAILED(mac.rc())) return mac.rc();
11432 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11433 DeviceType_T devType = pMedium->i_getDeviceType();
11434 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11435 && devType == DeviceType_HardDisk)
11436 || (cleanupMode == CleanupMode_Full)
11437 )
11438 {
11439 llMedia.push_back(pMedium);
11440 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11441 /* Not allowed to keep this lock as below we need the parent
11442 * medium lock, and the lock order is parent to child. */
11443 lock.release();
11444 /*
11445 * Search for medias which are not attached to any machine, but
11446 * in the chain to an attached disk. Mediums are only consided
11447 * if they are:
11448 * - have only one child
11449 * - no references to any machines
11450 * - are of normal medium type
11451 */
11452 while (!pParent.isNull())
11453 {
11454 AutoCaller mac1(pParent);
11455 if (FAILED(mac1.rc())) return mac1.rc();
11456 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11457 if (pParent->i_getChildren().size() == 1)
11458 {
11459 if ( pParent->i_getMachineBackRefCount() == 0
11460 && pParent->i_getType() == MediumType_Normal
11461 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11462 llMedia.push_back(pParent);
11463 }
11464 else
11465 break;
11466 pParent = pParent->i_getParent();
11467 }
11468 }
11469 }
11470
11471 // real machine: then we need to use the proper method
11472 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11473
11474 if (FAILED(rc))
11475 return rc;
11476 }
11477
11478 return S_OK;
11479}
11480
11481/**
11482 * Perform deferred hard disk detachments.
11483 *
11484 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11485 * changed (not backed up).
11486 *
11487 * If @a aOnline is @c true then this method will also unlock the old hard
11488 * disks for which the new implicit diffs were created and will lock these new
11489 * diffs for writing.
11490 *
11491 * @param aOnline Whether the VM was online prior to this operation.
11492 *
11493 * @note Locks this object for writing!
11494 */
11495void Machine::i_commitMedia(bool aOnline /*= false*/)
11496{
11497 AutoCaller autoCaller(this);
11498 AssertComRCReturnVoid(autoCaller.rc());
11499
11500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11501
11502 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11503
11504 HRESULT rc = S_OK;
11505
11506 /* no attach/detach operations -- nothing to do */
11507 if (!mMediumAttachments.isBackedUp())
11508 return;
11509
11510 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11511 bool fMediaNeedsLocking = false;
11512
11513 /* enumerate new attachments */
11514 for (MediumAttachmentList::const_iterator
11515 it = mMediumAttachments->begin();
11516 it != mMediumAttachments->end();
11517 ++it)
11518 {
11519 MediumAttachment *pAttach = *it;
11520
11521 pAttach->i_commit();
11522
11523 Medium *pMedium = pAttach->i_getMedium();
11524 bool fImplicit = pAttach->i_isImplicit();
11525
11526 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11527 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11528 fImplicit));
11529
11530 /** @todo convert all this Machine-based voodoo to MediumAttachment
11531 * based commit logic. */
11532 if (fImplicit)
11533 {
11534 /* convert implicit attachment to normal */
11535 pAttach->i_setImplicit(false);
11536
11537 if ( aOnline
11538 && pMedium
11539 && pAttach->i_getType() == DeviceType_HardDisk
11540 )
11541 {
11542 /* update the appropriate lock list */
11543 MediumLockList *pMediumLockList;
11544 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11545 AssertComRC(rc);
11546 if (pMediumLockList)
11547 {
11548 /* unlock if there's a need to change the locking */
11549 if (!fMediaNeedsLocking)
11550 {
11551 rc = mData->mSession.mLockedMedia.Unlock();
11552 AssertComRC(rc);
11553 fMediaNeedsLocking = true;
11554 }
11555 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11556 AssertComRC(rc);
11557 rc = pMediumLockList->Append(pMedium, true);
11558 AssertComRC(rc);
11559 }
11560 }
11561
11562 continue;
11563 }
11564
11565 if (pMedium)
11566 {
11567 /* was this medium attached before? */
11568 for (MediumAttachmentList::iterator
11569 oldIt = oldAtts.begin();
11570 oldIt != oldAtts.end();
11571 ++oldIt)
11572 {
11573 MediumAttachment *pOldAttach = *oldIt;
11574 if (pOldAttach->i_getMedium() == pMedium)
11575 {
11576 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11577
11578 /* yes: remove from old to avoid de-association */
11579 oldAtts.erase(oldIt);
11580 break;
11581 }
11582 }
11583 }
11584 }
11585
11586 /* enumerate remaining old attachments and de-associate from the
11587 * current machine state */
11588 for (MediumAttachmentList::const_iterator
11589 it = oldAtts.begin();
11590 it != oldAtts.end();
11591 ++it)
11592 {
11593 MediumAttachment *pAttach = *it;
11594 Medium *pMedium = pAttach->i_getMedium();
11595
11596 /* Detach only hard disks, since DVD/floppy media is detached
11597 * instantly in MountMedium. */
11598 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11599 {
11600 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11601
11602 /* now de-associate from the current machine state */
11603 rc = pMedium->i_removeBackReference(mData->mUuid);
11604 AssertComRC(rc);
11605
11606 if (aOnline)
11607 {
11608 /* unlock since medium is not used anymore */
11609 MediumLockList *pMediumLockList;
11610 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11611 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11612 {
11613 /* this happens for online snapshots, there the attachment
11614 * is changing, but only to a diff image created under
11615 * the old one, so there is no separate lock list */
11616 Assert(!pMediumLockList);
11617 }
11618 else
11619 {
11620 AssertComRC(rc);
11621 if (pMediumLockList)
11622 {
11623 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11624 AssertComRC(rc);
11625 }
11626 }
11627 }
11628 }
11629 }
11630
11631 /* take media locks again so that the locking state is consistent */
11632 if (fMediaNeedsLocking)
11633 {
11634 Assert(aOnline);
11635 rc = mData->mSession.mLockedMedia.Lock();
11636 AssertComRC(rc);
11637 }
11638
11639 /* commit the hard disk changes */
11640 mMediumAttachments.commit();
11641
11642 if (i_isSessionMachine())
11643 {
11644 /*
11645 * Update the parent machine to point to the new owner.
11646 * This is necessary because the stored parent will point to the
11647 * session machine otherwise and cause crashes or errors later
11648 * when the session machine gets invalid.
11649 */
11650 /** @todo Change the MediumAttachment class to behave like any other
11651 * class in this regard by creating peer MediumAttachment
11652 * objects for session machines and share the data with the peer
11653 * machine.
11654 */
11655 for (MediumAttachmentList::const_iterator
11656 it = mMediumAttachments->begin();
11657 it != mMediumAttachments->end();
11658 ++it)
11659 (*it)->i_updateParentMachine(mPeer);
11660
11661 /* attach new data to the primary machine and reshare it */
11662 mPeer->mMediumAttachments.attach(mMediumAttachments);
11663 }
11664
11665 return;
11666}
11667
11668/**
11669 * Perform deferred deletion of implicitly created diffs.
11670 *
11671 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11672 * changed (not backed up).
11673 *
11674 * @note Locks this object for writing!
11675 */
11676void Machine::i_rollbackMedia()
11677{
11678 AutoCaller autoCaller(this);
11679 AssertComRCReturnVoid(autoCaller.rc());
11680
11681 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11682 LogFlowThisFunc(("Entering rollbackMedia\n"));
11683
11684 HRESULT rc = S_OK;
11685
11686 /* no attach/detach operations -- nothing to do */
11687 if (!mMediumAttachments.isBackedUp())
11688 return;
11689
11690 /* enumerate new attachments */
11691 for (MediumAttachmentList::const_iterator
11692 it = mMediumAttachments->begin();
11693 it != mMediumAttachments->end();
11694 ++it)
11695 {
11696 MediumAttachment *pAttach = *it;
11697 /* Fix up the backrefs for DVD/floppy media. */
11698 if (pAttach->i_getType() != DeviceType_HardDisk)
11699 {
11700 Medium *pMedium = pAttach->i_getMedium();
11701 if (pMedium)
11702 {
11703 rc = pMedium->i_removeBackReference(mData->mUuid);
11704 AssertComRC(rc);
11705 }
11706 }
11707
11708 (*it)->i_rollback();
11709
11710 pAttach = *it;
11711 /* Fix up the backrefs for DVD/floppy media. */
11712 if (pAttach->i_getType() != DeviceType_HardDisk)
11713 {
11714 Medium *pMedium = pAttach->i_getMedium();
11715 if (pMedium)
11716 {
11717 rc = pMedium->i_addBackReference(mData->mUuid);
11718 AssertComRC(rc);
11719 }
11720 }
11721 }
11722
11723 /** @todo convert all this Machine-based voodoo to MediumAttachment
11724 * based rollback logic. */
11725 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11726
11727 return;
11728}
11729
11730/**
11731 * Returns true if the settings file is located in the directory named exactly
11732 * as the machine; this means, among other things, that the machine directory
11733 * should be auto-renamed.
11734 *
11735 * @param aSettingsDir if not NULL, the full machine settings file directory
11736 * name will be assigned there.
11737 *
11738 * @note Doesn't lock anything.
11739 * @note Not thread safe (must be called from this object's lock).
11740 */
11741bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11742{
11743 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11744 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11745 if (aSettingsDir)
11746 *aSettingsDir = strMachineDirName;
11747 strMachineDirName.stripPath(); // vmname
11748 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11749 strConfigFileOnly.stripPath() // vmname.vbox
11750 .stripSuffix(); // vmname
11751 /** @todo hack, make somehow use of ComposeMachineFilename */
11752 if (mUserData->s.fDirectoryIncludesUUID)
11753 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11754
11755 AssertReturn(!strMachineDirName.isEmpty(), false);
11756 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11757
11758 return strMachineDirName == strConfigFileOnly;
11759}
11760
11761/**
11762 * Discards all changes to machine settings.
11763 *
11764 * @param aNotify Whether to notify the direct session about changes or not.
11765 *
11766 * @note Locks objects for writing!
11767 */
11768void Machine::i_rollback(bool aNotify)
11769{
11770 AutoCaller autoCaller(this);
11771 AssertComRCReturn(autoCaller.rc(), (void)0);
11772
11773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11774
11775 if (!mStorageControllers.isNull())
11776 {
11777 if (mStorageControllers.isBackedUp())
11778 {
11779 /* unitialize all new devices (absent in the backed up list). */
11780 StorageControllerList *backedList = mStorageControllers.backedUpData();
11781 for (StorageControllerList::const_iterator
11782 it = mStorageControllers->begin();
11783 it != mStorageControllers->end();
11784 ++it)
11785 {
11786 if ( std::find(backedList->begin(), backedList->end(), *it)
11787 == backedList->end()
11788 )
11789 {
11790 (*it)->uninit();
11791 }
11792 }
11793
11794 /* restore the list */
11795 mStorageControllers.rollback();
11796 }
11797
11798 /* rollback any changes to devices after restoring the list */
11799 if (mData->flModifications & IsModified_Storage)
11800 {
11801 for (StorageControllerList::const_iterator
11802 it = mStorageControllers->begin();
11803 it != mStorageControllers->end();
11804 ++it)
11805 {
11806 (*it)->i_rollback();
11807 }
11808 }
11809 }
11810
11811 if (!mUSBControllers.isNull())
11812 {
11813 if (mUSBControllers.isBackedUp())
11814 {
11815 /* unitialize all new devices (absent in the backed up list). */
11816 USBControllerList *backedList = mUSBControllers.backedUpData();
11817 for (USBControllerList::const_iterator
11818 it = mUSBControllers->begin();
11819 it != mUSBControllers->end();
11820 ++it)
11821 {
11822 if ( std::find(backedList->begin(), backedList->end(), *it)
11823 == backedList->end()
11824 )
11825 {
11826 (*it)->uninit();
11827 }
11828 }
11829
11830 /* restore the list */
11831 mUSBControllers.rollback();
11832 }
11833
11834 /* rollback any changes to devices after restoring the list */
11835 if (mData->flModifications & IsModified_USB)
11836 {
11837 for (USBControllerList::const_iterator
11838 it = mUSBControllers->begin();
11839 it != mUSBControllers->end();
11840 ++it)
11841 {
11842 (*it)->i_rollback();
11843 }
11844 }
11845 }
11846
11847 mUserData.rollback();
11848
11849 mHWData.rollback();
11850
11851 if (mData->flModifications & IsModified_Storage)
11852 i_rollbackMedia();
11853
11854 if (mBIOSSettings)
11855 mBIOSSettings->i_rollback();
11856
11857 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11858 mRecordingSettings->i_rollback();
11859
11860 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11861 mVRDEServer->i_rollback();
11862
11863 if (mAudioAdapter)
11864 mAudioAdapter->i_rollback();
11865
11866 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11867 mUSBDeviceFilters->i_rollback();
11868
11869 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11870 mBandwidthControl->i_rollback();
11871
11872 if (!mHWData.isNull())
11873 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11874 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11875 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11876 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11877
11878 if (mData->flModifications & IsModified_NetworkAdapters)
11879 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11880 if ( mNetworkAdapters[slot]
11881 && mNetworkAdapters[slot]->i_isModified())
11882 {
11883 mNetworkAdapters[slot]->i_rollback();
11884 networkAdapters[slot] = mNetworkAdapters[slot];
11885 }
11886
11887 if (mData->flModifications & IsModified_SerialPorts)
11888 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11889 if ( mSerialPorts[slot]
11890 && mSerialPorts[slot]->i_isModified())
11891 {
11892 mSerialPorts[slot]->i_rollback();
11893 serialPorts[slot] = mSerialPorts[slot];
11894 }
11895
11896 if (mData->flModifications & IsModified_ParallelPorts)
11897 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11898 if ( mParallelPorts[slot]
11899 && mParallelPorts[slot]->i_isModified())
11900 {
11901 mParallelPorts[slot]->i_rollback();
11902 parallelPorts[slot] = mParallelPorts[slot];
11903 }
11904
11905 if (aNotify)
11906 {
11907 /* inform the direct session about changes */
11908
11909 ComObjPtr<Machine> that = this;
11910 uint32_t flModifications = mData->flModifications;
11911 alock.release();
11912
11913 if (flModifications & IsModified_SharedFolders)
11914 that->i_onSharedFolderChange();
11915
11916 if (flModifications & IsModified_VRDEServer)
11917 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11918 if (flModifications & IsModified_USB)
11919 that->i_onUSBControllerChange();
11920
11921 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11922 if (networkAdapters[slot])
11923 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11924 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11925 if (serialPorts[slot])
11926 that->i_onSerialPortChange(serialPorts[slot]);
11927 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11928 if (parallelPorts[slot])
11929 that->i_onParallelPortChange(parallelPorts[slot]);
11930
11931 if (flModifications & IsModified_Storage)
11932 {
11933 for (StorageControllerList::const_iterator
11934 it = mStorageControllers->begin();
11935 it != mStorageControllers->end();
11936 ++it)
11937 {
11938 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11939 }
11940 }
11941
11942
11943#if 0
11944 if (flModifications & IsModified_BandwidthControl)
11945 that->onBandwidthControlChange();
11946#endif
11947 }
11948}
11949
11950/**
11951 * Commits all the changes to machine settings.
11952 *
11953 * Note that this operation is supposed to never fail.
11954 *
11955 * @note Locks this object and children for writing.
11956 */
11957void Machine::i_commit()
11958{
11959 AutoCaller autoCaller(this);
11960 AssertComRCReturnVoid(autoCaller.rc());
11961
11962 AutoCaller peerCaller(mPeer);
11963 AssertComRCReturnVoid(peerCaller.rc());
11964
11965 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11966
11967 /*
11968 * use safe commit to ensure Snapshot machines (that share mUserData)
11969 * will still refer to a valid memory location
11970 */
11971 mUserData.commitCopy();
11972
11973 mHWData.commit();
11974
11975 if (mMediumAttachments.isBackedUp())
11976 i_commitMedia(Global::IsOnline(mData->mMachineState));
11977
11978 mBIOSSettings->i_commit();
11979 mRecordingSettings->i_commit();
11980 mVRDEServer->i_commit();
11981 mAudioAdapter->i_commit();
11982 mUSBDeviceFilters->i_commit();
11983 mBandwidthControl->i_commit();
11984
11985 /* Since mNetworkAdapters is a list which might have been changed (resized)
11986 * without using the Backupable<> template we need to handle the copying
11987 * of the list entries manually, including the creation of peers for the
11988 * new objects. */
11989 bool commitNetworkAdapters = false;
11990 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11991 if (mPeer)
11992 {
11993 /* commit everything, even the ones which will go away */
11994 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11995 mNetworkAdapters[slot]->i_commit();
11996 /* copy over the new entries, creating a peer and uninit the original */
11997 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11998 for (size_t slot = 0; slot < newSize; slot++)
11999 {
12000 /* look if this adapter has a peer device */
12001 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12002 if (!peer)
12003 {
12004 /* no peer means the adapter is a newly created one;
12005 * create a peer owning data this data share it with */
12006 peer.createObject();
12007 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12008 }
12009 mPeer->mNetworkAdapters[slot] = peer;
12010 }
12011 /* uninit any no longer needed network adapters */
12012 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12013 mNetworkAdapters[slot]->uninit();
12014 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12015 {
12016 if (mPeer->mNetworkAdapters[slot])
12017 mPeer->mNetworkAdapters[slot]->uninit();
12018 }
12019 /* Keep the original network adapter count until this point, so that
12020 * discarding a chipset type change will not lose settings. */
12021 mNetworkAdapters.resize(newSize);
12022 mPeer->mNetworkAdapters.resize(newSize);
12023 }
12024 else
12025 {
12026 /* we have no peer (our parent is the newly created machine);
12027 * just commit changes to the network adapters */
12028 commitNetworkAdapters = true;
12029 }
12030 if (commitNetworkAdapters)
12031 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12032 mNetworkAdapters[slot]->i_commit();
12033
12034 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12035 mSerialPorts[slot]->i_commit();
12036 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12037 mParallelPorts[slot]->i_commit();
12038
12039 bool commitStorageControllers = false;
12040
12041 if (mStorageControllers.isBackedUp())
12042 {
12043 mStorageControllers.commit();
12044
12045 if (mPeer)
12046 {
12047 /* Commit all changes to new controllers (this will reshare data with
12048 * peers for those who have peers) */
12049 StorageControllerList *newList = new StorageControllerList();
12050 for (StorageControllerList::const_iterator
12051 it = mStorageControllers->begin();
12052 it != mStorageControllers->end();
12053 ++it)
12054 {
12055 (*it)->i_commit();
12056
12057 /* look if this controller has a peer device */
12058 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12059 if (!peer)
12060 {
12061 /* no peer means the device is a newly created one;
12062 * create a peer owning data this device share it with */
12063 peer.createObject();
12064 peer->init(mPeer, *it, true /* aReshare */);
12065 }
12066 else
12067 {
12068 /* remove peer from the old list */
12069 mPeer->mStorageControllers->remove(peer);
12070 }
12071 /* and add it to the new list */
12072 newList->push_back(peer);
12073 }
12074
12075 /* uninit old peer's controllers that are left */
12076 for (StorageControllerList::const_iterator
12077 it = mPeer->mStorageControllers->begin();
12078 it != mPeer->mStorageControllers->end();
12079 ++it)
12080 {
12081 (*it)->uninit();
12082 }
12083
12084 /* attach new list of controllers to our peer */
12085 mPeer->mStorageControllers.attach(newList);
12086 }
12087 else
12088 {
12089 /* we have no peer (our parent is the newly created machine);
12090 * just commit changes to devices */
12091 commitStorageControllers = true;
12092 }
12093 }
12094 else
12095 {
12096 /* the list of controllers itself is not changed,
12097 * just commit changes to controllers themselves */
12098 commitStorageControllers = true;
12099 }
12100
12101 if (commitStorageControllers)
12102 {
12103 for (StorageControllerList::const_iterator
12104 it = mStorageControllers->begin();
12105 it != mStorageControllers->end();
12106 ++it)
12107 {
12108 (*it)->i_commit();
12109 }
12110 }
12111
12112 bool commitUSBControllers = false;
12113
12114 if (mUSBControllers.isBackedUp())
12115 {
12116 mUSBControllers.commit();
12117
12118 if (mPeer)
12119 {
12120 /* Commit all changes to new controllers (this will reshare data with
12121 * peers for those who have peers) */
12122 USBControllerList *newList = new USBControllerList();
12123 for (USBControllerList::const_iterator
12124 it = mUSBControllers->begin();
12125 it != mUSBControllers->end();
12126 ++it)
12127 {
12128 (*it)->i_commit();
12129
12130 /* look if this controller has a peer device */
12131 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12132 if (!peer)
12133 {
12134 /* no peer means the device is a newly created one;
12135 * create a peer owning data this device share it with */
12136 peer.createObject();
12137 peer->init(mPeer, *it, true /* aReshare */);
12138 }
12139 else
12140 {
12141 /* remove peer from the old list */
12142 mPeer->mUSBControllers->remove(peer);
12143 }
12144 /* and add it to the new list */
12145 newList->push_back(peer);
12146 }
12147
12148 /* uninit old peer's controllers that are left */
12149 for (USBControllerList::const_iterator
12150 it = mPeer->mUSBControllers->begin();
12151 it != mPeer->mUSBControllers->end();
12152 ++it)
12153 {
12154 (*it)->uninit();
12155 }
12156
12157 /* attach new list of controllers to our peer */
12158 mPeer->mUSBControllers.attach(newList);
12159 }
12160 else
12161 {
12162 /* we have no peer (our parent is the newly created machine);
12163 * just commit changes to devices */
12164 commitUSBControllers = true;
12165 }
12166 }
12167 else
12168 {
12169 /* the list of controllers itself is not changed,
12170 * just commit changes to controllers themselves */
12171 commitUSBControllers = true;
12172 }
12173
12174 if (commitUSBControllers)
12175 {
12176 for (USBControllerList::const_iterator
12177 it = mUSBControllers->begin();
12178 it != mUSBControllers->end();
12179 ++it)
12180 {
12181 (*it)->i_commit();
12182 }
12183 }
12184
12185 if (i_isSessionMachine())
12186 {
12187 /* attach new data to the primary machine and reshare it */
12188 mPeer->mUserData.attach(mUserData);
12189 mPeer->mHWData.attach(mHWData);
12190 /* mmMediumAttachments is reshared by fixupMedia */
12191 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12192 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12193 }
12194}
12195
12196/**
12197 * Copies all the hardware data from the given machine.
12198 *
12199 * Currently, only called when the VM is being restored from a snapshot. In
12200 * particular, this implies that the VM is not running during this method's
12201 * call.
12202 *
12203 * @note This method must be called from under this object's lock.
12204 *
12205 * @note This method doesn't call #i_commit(), so all data remains backed up and
12206 * unsaved.
12207 */
12208void Machine::i_copyFrom(Machine *aThat)
12209{
12210 AssertReturnVoid(!i_isSnapshotMachine());
12211 AssertReturnVoid(aThat->i_isSnapshotMachine());
12212
12213 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12214
12215 mHWData.assignCopy(aThat->mHWData);
12216
12217 // create copies of all shared folders (mHWData after attaching a copy
12218 // contains just references to original objects)
12219 for (HWData::SharedFolderList::iterator
12220 it = mHWData->mSharedFolders.begin();
12221 it != mHWData->mSharedFolders.end();
12222 ++it)
12223 {
12224 ComObjPtr<SharedFolder> folder;
12225 folder.createObject();
12226 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12227 AssertComRC(rc);
12228 *it = folder;
12229 }
12230
12231 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12232 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12233 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12234 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12235 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12236 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12237
12238 /* create private copies of all controllers */
12239 mStorageControllers.backup();
12240 mStorageControllers->clear();
12241 for (StorageControllerList::const_iterator
12242 it = aThat->mStorageControllers->begin();
12243 it != aThat->mStorageControllers->end();
12244 ++it)
12245 {
12246 ComObjPtr<StorageController> ctrl;
12247 ctrl.createObject();
12248 ctrl->initCopy(this, *it);
12249 mStorageControllers->push_back(ctrl);
12250 }
12251
12252 /* create private copies of all USB controllers */
12253 mUSBControllers.backup();
12254 mUSBControllers->clear();
12255 for (USBControllerList::const_iterator
12256 it = aThat->mUSBControllers->begin();
12257 it != aThat->mUSBControllers->end();
12258 ++it)
12259 {
12260 ComObjPtr<USBController> ctrl;
12261 ctrl.createObject();
12262 ctrl->initCopy(this, *it);
12263 mUSBControllers->push_back(ctrl);
12264 }
12265
12266 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12267 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12268 {
12269 if (mNetworkAdapters[slot].isNotNull())
12270 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12271 else
12272 {
12273 unconst(mNetworkAdapters[slot]).createObject();
12274 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12275 }
12276 }
12277 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12278 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12279 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12280 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12281}
12282
12283/**
12284 * Returns whether the given storage controller is hotplug capable.
12285 *
12286 * @returns true if the controller supports hotplugging
12287 * false otherwise.
12288 * @param enmCtrlType The controller type to check for.
12289 */
12290bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12291{
12292 ComPtr<ISystemProperties> systemProperties;
12293 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12294 if (FAILED(rc))
12295 return false;
12296
12297 BOOL aHotplugCapable = FALSE;
12298 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12299
12300 return RT_BOOL(aHotplugCapable);
12301}
12302
12303#ifdef VBOX_WITH_RESOURCE_USAGE_API
12304
12305void Machine::i_getDiskList(MediaList &list)
12306{
12307 for (MediumAttachmentList::const_iterator
12308 it = mMediumAttachments->begin();
12309 it != mMediumAttachments->end();
12310 ++it)
12311 {
12312 MediumAttachment *pAttach = *it;
12313 /* just in case */
12314 AssertContinue(pAttach);
12315
12316 AutoCaller localAutoCallerA(pAttach);
12317 if (FAILED(localAutoCallerA.rc())) continue;
12318
12319 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12320
12321 if (pAttach->i_getType() == DeviceType_HardDisk)
12322 list.push_back(pAttach->i_getMedium());
12323 }
12324}
12325
12326void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12327{
12328 AssertReturnVoid(isWriteLockOnCurrentThread());
12329 AssertPtrReturnVoid(aCollector);
12330
12331 pm::CollectorHAL *hal = aCollector->getHAL();
12332 /* Create sub metrics */
12333 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12334 "Percentage of processor time spent in user mode by the VM process.");
12335 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12336 "Percentage of processor time spent in kernel mode by the VM process.");
12337 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12338 "Size of resident portion of VM process in memory.");
12339 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12340 "Actual size of all VM disks combined.");
12341 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12342 "Network receive rate.");
12343 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12344 "Network transmit rate.");
12345 /* Create and register base metrics */
12346 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12347 cpuLoadUser, cpuLoadKernel);
12348 aCollector->registerBaseMetric(cpuLoad);
12349 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12350 ramUsageUsed);
12351 aCollector->registerBaseMetric(ramUsage);
12352 MediaList disks;
12353 i_getDiskList(disks);
12354 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12355 diskUsageUsed);
12356 aCollector->registerBaseMetric(diskUsage);
12357
12358 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12359 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12360 new pm::AggregateAvg()));
12361 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12362 new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12364 new pm::AggregateMax()));
12365 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12366 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12367 new pm::AggregateAvg()));
12368 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12369 new pm::AggregateMin()));
12370 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12371 new pm::AggregateMax()));
12372
12373 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12374 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12375 new pm::AggregateAvg()));
12376 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12377 new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12379 new pm::AggregateMax()));
12380
12381 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12382 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12383 new pm::AggregateAvg()));
12384 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12385 new pm::AggregateMin()));
12386 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12387 new pm::AggregateMax()));
12388
12389
12390 /* Guest metrics collector */
12391 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12392 aCollector->registerGuest(mCollectorGuest);
12393 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12394
12395 /* Create sub metrics */
12396 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12397 "Percentage of processor time spent in user mode as seen by the guest.");
12398 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12399 "Percentage of processor time spent in kernel mode as seen by the guest.");
12400 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12401 "Percentage of processor time spent idling as seen by the guest.");
12402
12403 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12404 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12405 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12406 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12407 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12408 pm::SubMetric *guestMemCache = new pm::SubMetric(
12409 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12410
12411 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12412 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12413
12414 /* Create and register base metrics */
12415 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12416 machineNetRx, machineNetTx);
12417 aCollector->registerBaseMetric(machineNetRate);
12418
12419 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12420 guestLoadUser, guestLoadKernel, guestLoadIdle);
12421 aCollector->registerBaseMetric(guestCpuLoad);
12422
12423 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12424 guestMemTotal, guestMemFree,
12425 guestMemBalloon, guestMemShared,
12426 guestMemCache, guestPagedTotal);
12427 aCollector->registerBaseMetric(guestCpuMem);
12428
12429 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12430 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12431 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12432 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12433
12434 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12435 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12436 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12437 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12438
12439 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12440 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12441 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12442 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12443
12444 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12445 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12446 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12447 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12448
12449 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12450 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12451 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12452 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12453
12454 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12455 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12456 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12457 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12458
12459 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12460 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12461 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12462 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12463
12464 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12465 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12466 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12467 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12468
12469 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12470 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12471 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12472 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12473
12474 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12475 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12476 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12477 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12478
12479 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12480 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12481 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12482 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12483}
12484
12485void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12486{
12487 AssertReturnVoid(isWriteLockOnCurrentThread());
12488
12489 if (aCollector)
12490 {
12491 aCollector->unregisterMetricsFor(aMachine);
12492 aCollector->unregisterBaseMetricsFor(aMachine);
12493 }
12494}
12495
12496#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12497
12498
12499////////////////////////////////////////////////////////////////////////////////
12500
12501DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12502
12503HRESULT SessionMachine::FinalConstruct()
12504{
12505 LogFlowThisFunc(("\n"));
12506
12507 mClientToken = NULL;
12508
12509 return BaseFinalConstruct();
12510}
12511
12512void SessionMachine::FinalRelease()
12513{
12514 LogFlowThisFunc(("\n"));
12515
12516 Assert(!mClientToken);
12517 /* paranoia, should not hang around any more */
12518 if (mClientToken)
12519 {
12520 delete mClientToken;
12521 mClientToken = NULL;
12522 }
12523
12524 uninit(Uninit::Unexpected);
12525
12526 BaseFinalRelease();
12527}
12528
12529/**
12530 * @note Must be called only by Machine::LockMachine() from its own write lock.
12531 */
12532HRESULT SessionMachine::init(Machine *aMachine)
12533{
12534 LogFlowThisFuncEnter();
12535 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12536
12537 AssertReturn(aMachine, E_INVALIDARG);
12538
12539 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12540
12541 /* Enclose the state transition NotReady->InInit->Ready */
12542 AutoInitSpan autoInitSpan(this);
12543 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12544
12545 HRESULT rc = S_OK;
12546
12547 RT_ZERO(mAuthLibCtx);
12548
12549 /* create the machine client token */
12550 try
12551 {
12552 mClientToken = new ClientToken(aMachine, this);
12553 if (!mClientToken->isReady())
12554 {
12555 delete mClientToken;
12556 mClientToken = NULL;
12557 rc = E_FAIL;
12558 }
12559 }
12560 catch (std::bad_alloc &)
12561 {
12562 rc = E_OUTOFMEMORY;
12563 }
12564 if (FAILED(rc))
12565 return rc;
12566
12567 /* memorize the peer Machine */
12568 unconst(mPeer) = aMachine;
12569 /* share the parent pointer */
12570 unconst(mParent) = aMachine->mParent;
12571
12572 /* take the pointers to data to share */
12573 mData.share(aMachine->mData);
12574 mSSData.share(aMachine->mSSData);
12575
12576 mUserData.share(aMachine->mUserData);
12577 mHWData.share(aMachine->mHWData);
12578 mMediumAttachments.share(aMachine->mMediumAttachments);
12579
12580 mStorageControllers.allocate();
12581 for (StorageControllerList::const_iterator
12582 it = aMachine->mStorageControllers->begin();
12583 it != aMachine->mStorageControllers->end();
12584 ++it)
12585 {
12586 ComObjPtr<StorageController> ctl;
12587 ctl.createObject();
12588 ctl->init(this, *it);
12589 mStorageControllers->push_back(ctl);
12590 }
12591
12592 mUSBControllers.allocate();
12593 for (USBControllerList::const_iterator
12594 it = aMachine->mUSBControllers->begin();
12595 it != aMachine->mUSBControllers->end();
12596 ++it)
12597 {
12598 ComObjPtr<USBController> ctl;
12599 ctl.createObject();
12600 ctl->init(this, *it);
12601 mUSBControllers->push_back(ctl);
12602 }
12603
12604 unconst(mBIOSSettings).createObject();
12605 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12606 unconst(mRecordingSettings).createObject();
12607 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12608 /* create another VRDEServer object that will be mutable */
12609 unconst(mVRDEServer).createObject();
12610 mVRDEServer->init(this, aMachine->mVRDEServer);
12611 /* create another audio adapter object that will be mutable */
12612 unconst(mAudioAdapter).createObject();
12613 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12614 /* create a list of serial ports that will be mutable */
12615 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12616 {
12617 unconst(mSerialPorts[slot]).createObject();
12618 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12619 }
12620 /* create a list of parallel ports that will be mutable */
12621 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12622 {
12623 unconst(mParallelPorts[slot]).createObject();
12624 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12625 }
12626
12627 /* create another USB device filters object that will be mutable */
12628 unconst(mUSBDeviceFilters).createObject();
12629 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12630
12631 /* create a list of network adapters that will be mutable */
12632 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12633 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12634 {
12635 unconst(mNetworkAdapters[slot]).createObject();
12636 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12637 }
12638
12639 /* create another bandwidth control object that will be mutable */
12640 unconst(mBandwidthControl).createObject();
12641 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12642
12643 /* default is to delete saved state on Saved -> PoweredOff transition */
12644 mRemoveSavedState = true;
12645
12646 /* Confirm a successful initialization when it's the case */
12647 autoInitSpan.setSucceeded();
12648
12649 miNATNetworksStarted = 0;
12650
12651 LogFlowThisFuncLeave();
12652 return rc;
12653}
12654
12655/**
12656 * Uninitializes this session object. If the reason is other than
12657 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12658 * or the client watcher code.
12659 *
12660 * @param aReason uninitialization reason
12661 *
12662 * @note Locks mParent + this object for writing.
12663 */
12664void SessionMachine::uninit(Uninit::Reason aReason)
12665{
12666 LogFlowThisFuncEnter();
12667 LogFlowThisFunc(("reason=%d\n", aReason));
12668
12669 /*
12670 * Strongly reference ourselves to prevent this object deletion after
12671 * mData->mSession.mMachine.setNull() below (which can release the last
12672 * reference and call the destructor). Important: this must be done before
12673 * accessing any members (and before AutoUninitSpan that does it as well).
12674 * This self reference will be released as the very last step on return.
12675 */
12676 ComObjPtr<SessionMachine> selfRef;
12677 if (aReason != Uninit::Unexpected)
12678 selfRef = this;
12679
12680 /* Enclose the state transition Ready->InUninit->NotReady */
12681 AutoUninitSpan autoUninitSpan(this);
12682 if (autoUninitSpan.uninitDone())
12683 {
12684 LogFlowThisFunc(("Already uninitialized\n"));
12685 LogFlowThisFuncLeave();
12686 return;
12687 }
12688
12689 if (autoUninitSpan.initFailed())
12690 {
12691 /* We've been called by init() because it's failed. It's not really
12692 * necessary (nor it's safe) to perform the regular uninit sequence
12693 * below, the following is enough.
12694 */
12695 LogFlowThisFunc(("Initialization failed.\n"));
12696 /* destroy the machine client token */
12697 if (mClientToken)
12698 {
12699 delete mClientToken;
12700 mClientToken = NULL;
12701 }
12702 uninitDataAndChildObjects();
12703 mData.free();
12704 unconst(mParent) = NULL;
12705 unconst(mPeer) = NULL;
12706 LogFlowThisFuncLeave();
12707 return;
12708 }
12709
12710 MachineState_T lastState;
12711 {
12712 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12713 lastState = mData->mMachineState;
12714 }
12715 NOREF(lastState);
12716
12717#ifdef VBOX_WITH_USB
12718 // release all captured USB devices, but do this before requesting the locks below
12719 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12720 {
12721 /* Console::captureUSBDevices() is called in the VM process only after
12722 * setting the machine state to Starting or Restoring.
12723 * Console::detachAllUSBDevices() will be called upon successful
12724 * termination. So, we need to release USB devices only if there was
12725 * an abnormal termination of a running VM.
12726 *
12727 * This is identical to SessionMachine::DetachAllUSBDevices except
12728 * for the aAbnormal argument. */
12729 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12730 AssertComRC(rc);
12731 NOREF(rc);
12732
12733 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12734 if (service)
12735 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12736 }
12737#endif /* VBOX_WITH_USB */
12738
12739 // we need to lock this object in uninit() because the lock is shared
12740 // with mPeer (as well as data we modify below). mParent lock is needed
12741 // by several calls to it.
12742 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12743
12744#ifdef VBOX_WITH_RESOURCE_USAGE_API
12745 /*
12746 * It is safe to call Machine::i_unregisterMetrics() here because
12747 * PerformanceCollector::samplerCallback no longer accesses guest methods
12748 * holding the lock.
12749 */
12750 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12751 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12752 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12753 if (mCollectorGuest)
12754 {
12755 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12756 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12757 mCollectorGuest = NULL;
12758 }
12759#endif
12760
12761 if (aReason == Uninit::Abnormal)
12762 {
12763 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12764
12765 /* reset the state to Aborted */
12766 if (mData->mMachineState != MachineState_Aborted)
12767 i_setMachineState(MachineState_Aborted);
12768 }
12769
12770 // any machine settings modified?
12771 if (mData->flModifications)
12772 {
12773 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12774 i_rollback(false /* aNotify */);
12775 }
12776
12777 mData->mSession.mPID = NIL_RTPROCESS;
12778
12779 if (aReason == Uninit::Unexpected)
12780 {
12781 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12782 * client watcher thread to update the set of machines that have open
12783 * sessions. */
12784 mParent->i_updateClientWatcher();
12785 }
12786
12787 /* uninitialize all remote controls */
12788 if (mData->mSession.mRemoteControls.size())
12789 {
12790 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12791 mData->mSession.mRemoteControls.size()));
12792
12793 /* Always restart a the beginning, since the iterator is invalidated
12794 * by using erase(). */
12795 for (Data::Session::RemoteControlList::iterator
12796 it = mData->mSession.mRemoteControls.begin();
12797 it != mData->mSession.mRemoteControls.end();
12798 it = mData->mSession.mRemoteControls.begin())
12799 {
12800 ComPtr<IInternalSessionControl> pControl = *it;
12801 mData->mSession.mRemoteControls.erase(it);
12802 multilock.release();
12803 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12804 HRESULT rc = pControl->Uninitialize();
12805 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12806 if (FAILED(rc))
12807 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12808 multilock.acquire();
12809 }
12810 mData->mSession.mRemoteControls.clear();
12811 }
12812
12813 /* Remove all references to the NAT network service. The service will stop
12814 * if all references (also from other VMs) are removed. */
12815 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12816 {
12817 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12818 {
12819 BOOL enabled;
12820 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12821 if ( FAILED(hrc)
12822 || !enabled)
12823 continue;
12824
12825 NetworkAttachmentType_T type;
12826 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12827 if ( SUCCEEDED(hrc)
12828 && type == NetworkAttachmentType_NATNetwork)
12829 {
12830 Bstr name;
12831 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12832 if (SUCCEEDED(hrc))
12833 {
12834 multilock.release();
12835 Utf8Str strName(name);
12836 LogRel(("VM '%s' stops using NAT network '%s'\n",
12837 mUserData->s.strName.c_str(), strName.c_str()));
12838 mParent->i_natNetworkRefDec(strName);
12839 multilock.acquire();
12840 }
12841 }
12842 }
12843 }
12844
12845 /*
12846 * An expected uninitialization can come only from #i_checkForDeath().
12847 * Otherwise it means that something's gone really wrong (for example,
12848 * the Session implementation has released the VirtualBox reference
12849 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12850 * etc). However, it's also possible, that the client releases the IPC
12851 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12852 * but the VirtualBox release event comes first to the server process.
12853 * This case is practically possible, so we should not assert on an
12854 * unexpected uninit, just log a warning.
12855 */
12856
12857 if (aReason == Uninit::Unexpected)
12858 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12859
12860 if (aReason != Uninit::Normal)
12861 {
12862 mData->mSession.mDirectControl.setNull();
12863 }
12864 else
12865 {
12866 /* this must be null here (see #OnSessionEnd()) */
12867 Assert(mData->mSession.mDirectControl.isNull());
12868 Assert(mData->mSession.mState == SessionState_Unlocking);
12869 Assert(!mData->mSession.mProgress.isNull());
12870 }
12871 if (mData->mSession.mProgress)
12872 {
12873 if (aReason == Uninit::Normal)
12874 mData->mSession.mProgress->i_notifyComplete(S_OK);
12875 else
12876 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12877 COM_IIDOF(ISession),
12878 getComponentName(),
12879 tr("The VM session was aborted"));
12880 mData->mSession.mProgress.setNull();
12881 }
12882
12883 if (mConsoleTaskData.mProgress)
12884 {
12885 Assert(aReason == Uninit::Abnormal);
12886 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12887 COM_IIDOF(ISession),
12888 getComponentName(),
12889 tr("The VM session was aborted"));
12890 mConsoleTaskData.mProgress.setNull();
12891 }
12892
12893 /* remove the association between the peer machine and this session machine */
12894 Assert( (SessionMachine*)mData->mSession.mMachine == this
12895 || aReason == Uninit::Unexpected);
12896
12897 /* reset the rest of session data */
12898 mData->mSession.mLockType = LockType_Null;
12899 mData->mSession.mMachine.setNull();
12900 mData->mSession.mState = SessionState_Unlocked;
12901 mData->mSession.mName.setNull();
12902
12903 /* destroy the machine client token before leaving the exclusive lock */
12904 if (mClientToken)
12905 {
12906 delete mClientToken;
12907 mClientToken = NULL;
12908 }
12909
12910 /* fire an event */
12911 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12912
12913 uninitDataAndChildObjects();
12914
12915 /* free the essential data structure last */
12916 mData.free();
12917
12918 /* release the exclusive lock before setting the below two to NULL */
12919 multilock.release();
12920
12921 unconst(mParent) = NULL;
12922 unconst(mPeer) = NULL;
12923
12924 AuthLibUnload(&mAuthLibCtx);
12925
12926 LogFlowThisFuncLeave();
12927}
12928
12929// util::Lockable interface
12930////////////////////////////////////////////////////////////////////////////////
12931
12932/**
12933 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12934 * with the primary Machine instance (mPeer).
12935 */
12936RWLockHandle *SessionMachine::lockHandle() const
12937{
12938 AssertReturn(mPeer != NULL, NULL);
12939 return mPeer->lockHandle();
12940}
12941
12942// IInternalMachineControl methods
12943////////////////////////////////////////////////////////////////////////////////
12944
12945/**
12946 * Passes collected guest statistics to performance collector object
12947 */
12948HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12949 ULONG aCpuKernel, ULONG aCpuIdle,
12950 ULONG aMemTotal, ULONG aMemFree,
12951 ULONG aMemBalloon, ULONG aMemShared,
12952 ULONG aMemCache, ULONG aPageTotal,
12953 ULONG aAllocVMM, ULONG aFreeVMM,
12954 ULONG aBalloonedVMM, ULONG aSharedVMM,
12955 ULONG aVmNetRx, ULONG aVmNetTx)
12956{
12957#ifdef VBOX_WITH_RESOURCE_USAGE_API
12958 if (mCollectorGuest)
12959 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12960 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12961 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12962 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12963
12964 return S_OK;
12965#else
12966 NOREF(aValidStats);
12967 NOREF(aCpuUser);
12968 NOREF(aCpuKernel);
12969 NOREF(aCpuIdle);
12970 NOREF(aMemTotal);
12971 NOREF(aMemFree);
12972 NOREF(aMemBalloon);
12973 NOREF(aMemShared);
12974 NOREF(aMemCache);
12975 NOREF(aPageTotal);
12976 NOREF(aAllocVMM);
12977 NOREF(aFreeVMM);
12978 NOREF(aBalloonedVMM);
12979 NOREF(aSharedVMM);
12980 NOREF(aVmNetRx);
12981 NOREF(aVmNetTx);
12982 return E_NOTIMPL;
12983#endif
12984}
12985
12986////////////////////////////////////////////////////////////////////////////////
12987//
12988// SessionMachine task records
12989//
12990////////////////////////////////////////////////////////////////////////////////
12991
12992/**
12993 * Task record for saving the machine state.
12994 */
12995class SessionMachine::SaveStateTask
12996 : public Machine::Task
12997{
12998public:
12999 SaveStateTask(SessionMachine *m,
13000 Progress *p,
13001 const Utf8Str &t,
13002 Reason_T enmReason,
13003 const Utf8Str &strStateFilePath)
13004 : Task(m, p, t),
13005 m_enmReason(enmReason),
13006 m_strStateFilePath(strStateFilePath)
13007 {}
13008
13009private:
13010 void handler()
13011 {
13012 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13013 }
13014
13015 Reason_T m_enmReason;
13016 Utf8Str m_strStateFilePath;
13017
13018 friend class SessionMachine;
13019};
13020
13021/**
13022 * Task thread implementation for SessionMachine::SaveState(), called from
13023 * SessionMachine::taskHandler().
13024 *
13025 * @note Locks this object for writing.
13026 *
13027 * @param task
13028 * @return
13029 */
13030void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13031{
13032 LogFlowThisFuncEnter();
13033
13034 AutoCaller autoCaller(this);
13035 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13036 if (FAILED(autoCaller.rc()))
13037 {
13038 /* we might have been uninitialized because the session was accidentally
13039 * closed by the client, so don't assert */
13040 HRESULT rc = setError(E_FAIL,
13041 tr("The session has been accidentally closed"));
13042 task.m_pProgress->i_notifyComplete(rc);
13043 LogFlowThisFuncLeave();
13044 return;
13045 }
13046
13047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13048
13049 HRESULT rc = S_OK;
13050
13051 try
13052 {
13053 ComPtr<IInternalSessionControl> directControl;
13054 if (mData->mSession.mLockType == LockType_VM)
13055 directControl = mData->mSession.mDirectControl;
13056 if (directControl.isNull())
13057 throw setError(VBOX_E_INVALID_VM_STATE,
13058 tr("Trying to save state without a running VM"));
13059 alock.release();
13060 BOOL fSuspendedBySave;
13061 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13062 Assert(!fSuspendedBySave);
13063 alock.acquire();
13064
13065 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13066 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13067 throw E_FAIL);
13068
13069 if (SUCCEEDED(rc))
13070 {
13071 mSSData->strStateFilePath = task.m_strStateFilePath;
13072
13073 /* save all VM settings */
13074 rc = i_saveSettings(NULL);
13075 // no need to check whether VirtualBox.xml needs saving also since
13076 // we can't have a name change pending at this point
13077 }
13078 else
13079 {
13080 // On failure, set the state to the state we had at the beginning.
13081 i_setMachineState(task.m_machineStateBackup);
13082 i_updateMachineStateOnClient();
13083
13084 // Delete the saved state file (might have been already created).
13085 // No need to check whether this is shared with a snapshot here
13086 // because we certainly created a fresh saved state file here.
13087 RTFileDelete(task.m_strStateFilePath.c_str());
13088 }
13089 }
13090 catch (HRESULT aRC) { rc = aRC; }
13091
13092 task.m_pProgress->i_notifyComplete(rc);
13093
13094 LogFlowThisFuncLeave();
13095}
13096
13097/**
13098 * @note Locks this object for writing.
13099 */
13100HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13101{
13102 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13103}
13104
13105HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13106{
13107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13108
13109 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13110 if (FAILED(rc)) return rc;
13111
13112 if ( mData->mMachineState != MachineState_Running
13113 && mData->mMachineState != MachineState_Paused
13114 )
13115 return setError(VBOX_E_INVALID_VM_STATE,
13116 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13117 Global::stringifyMachineState(mData->mMachineState));
13118
13119 ComObjPtr<Progress> pProgress;
13120 pProgress.createObject();
13121 rc = pProgress->init(i_getVirtualBox(),
13122 static_cast<IMachine *>(this) /* aInitiator */,
13123 tr("Saving the execution state of the virtual machine"),
13124 FALSE /* aCancelable */);
13125 if (FAILED(rc))
13126 return rc;
13127
13128 Utf8Str strStateFilePath;
13129 i_composeSavedStateFilename(strStateFilePath);
13130
13131 /* create and start the task on a separate thread (note that it will not
13132 * start working until we release alock) */
13133 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13134 rc = pTask->createThread();
13135 if (FAILED(rc))
13136 return rc;
13137
13138 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13139 i_setMachineState(MachineState_Saving);
13140 i_updateMachineStateOnClient();
13141
13142 pProgress.queryInterfaceTo(aProgress.asOutParam());
13143
13144 return S_OK;
13145}
13146
13147/**
13148 * @note Locks this object for writing.
13149 */
13150HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13151{
13152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13153
13154 HRESULT rc = i_checkStateDependency(MutableStateDep);
13155 if (FAILED(rc)) return rc;
13156
13157 if ( mData->mMachineState != MachineState_PoweredOff
13158 && mData->mMachineState != MachineState_Teleported
13159 && mData->mMachineState != MachineState_Aborted
13160 )
13161 return setError(VBOX_E_INVALID_VM_STATE,
13162 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13163 Global::stringifyMachineState(mData->mMachineState));
13164
13165 com::Utf8Str stateFilePathFull;
13166 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13167 if (RT_FAILURE(vrc))
13168 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13169 tr("Invalid saved state file path '%s' (%Rrc)"),
13170 aSavedStateFile.c_str(),
13171 vrc);
13172
13173 mSSData->strStateFilePath = stateFilePathFull;
13174
13175 /* The below i_setMachineState() will detect the state transition and will
13176 * update the settings file */
13177
13178 return i_setMachineState(MachineState_Saved);
13179}
13180
13181/**
13182 * @note Locks this object for writing.
13183 */
13184HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13185{
13186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13187
13188 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13189 if (FAILED(rc)) return rc;
13190
13191 if (mData->mMachineState != MachineState_Saved)
13192 return setError(VBOX_E_INVALID_VM_STATE,
13193 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13194 Global::stringifyMachineState(mData->mMachineState));
13195
13196 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13197
13198 /*
13199 * Saved -> PoweredOff transition will be detected in the SessionMachine
13200 * and properly handled.
13201 */
13202 rc = i_setMachineState(MachineState_PoweredOff);
13203 return rc;
13204}
13205
13206
13207/**
13208 * @note Locks the same as #i_setMachineState() does.
13209 */
13210HRESULT SessionMachine::updateState(MachineState_T aState)
13211{
13212 return i_setMachineState(aState);
13213}
13214
13215/**
13216 * @note Locks this object for writing.
13217 */
13218HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13219{
13220 IProgress *pProgress(aProgress);
13221
13222 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13223
13224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13225
13226 if (mData->mSession.mState != SessionState_Locked)
13227 return VBOX_E_INVALID_OBJECT_STATE;
13228
13229 if (!mData->mSession.mProgress.isNull())
13230 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13231
13232 /* If we didn't reference the NAT network service yet, add a reference to
13233 * force a start */
13234 if (miNATNetworksStarted < 1)
13235 {
13236 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13237 {
13238 BOOL enabled;
13239 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13240 if ( FAILED(hrc)
13241 || !enabled)
13242 continue;
13243
13244 NetworkAttachmentType_T type;
13245 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13246 if ( SUCCEEDED(hrc)
13247 && type == NetworkAttachmentType_NATNetwork)
13248 {
13249 Bstr name;
13250 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13251 if (SUCCEEDED(hrc))
13252 {
13253 Utf8Str strName(name);
13254 LogRel(("VM '%s' starts using NAT network '%s'\n",
13255 mUserData->s.strName.c_str(), strName.c_str()));
13256 mPeer->lockHandle()->unlockWrite();
13257 mParent->i_natNetworkRefInc(strName);
13258#ifdef RT_LOCK_STRICT
13259 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13260#else
13261 mPeer->lockHandle()->lockWrite();
13262#endif
13263 }
13264 }
13265 }
13266 miNATNetworksStarted++;
13267 }
13268
13269 LogFlowThisFunc(("returns S_OK.\n"));
13270 return S_OK;
13271}
13272
13273/**
13274 * @note Locks this object for writing.
13275 */
13276HRESULT SessionMachine::endPowerUp(LONG aResult)
13277{
13278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13279
13280 if (mData->mSession.mState != SessionState_Locked)
13281 return VBOX_E_INVALID_OBJECT_STATE;
13282
13283 /* Finalize the LaunchVMProcess progress object. */
13284 if (mData->mSession.mProgress)
13285 {
13286 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13287 mData->mSession.mProgress.setNull();
13288 }
13289
13290 if (SUCCEEDED((HRESULT)aResult))
13291 {
13292#ifdef VBOX_WITH_RESOURCE_USAGE_API
13293 /* The VM has been powered up successfully, so it makes sense
13294 * now to offer the performance metrics for a running machine
13295 * object. Doing it earlier wouldn't be safe. */
13296 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13297 mData->mSession.mPID);
13298#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13299 }
13300
13301 return S_OK;
13302}
13303
13304/**
13305 * @note Locks this object for writing.
13306 */
13307HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13308{
13309 LogFlowThisFuncEnter();
13310
13311#ifdef VBOX_WITH_CLOUD_NET
13312 mPeer->i_disconnectFromCloudNetwork();
13313#endif /* VBOX_WITH_CLOUD_NET */
13314
13315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13316
13317 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13318 E_FAIL);
13319
13320 /* create a progress object to track operation completion */
13321 ComObjPtr<Progress> pProgress;
13322 pProgress.createObject();
13323 pProgress->init(i_getVirtualBox(),
13324 static_cast<IMachine *>(this) /* aInitiator */,
13325 tr("Stopping the virtual machine"),
13326 FALSE /* aCancelable */);
13327
13328 /* fill in the console task data */
13329 mConsoleTaskData.mLastState = mData->mMachineState;
13330 mConsoleTaskData.mProgress = pProgress;
13331
13332 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13333 i_setMachineState(MachineState_Stopping);
13334
13335 pProgress.queryInterfaceTo(aProgress.asOutParam());
13336
13337 return S_OK;
13338}
13339
13340/**
13341 * @note Locks this object for writing.
13342 */
13343HRESULT SessionMachine::endPoweringDown(LONG aResult,
13344 const com::Utf8Str &aErrMsg)
13345{
13346 LogFlowThisFuncEnter();
13347
13348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13349
13350 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13351 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13352 && mConsoleTaskData.mLastState != MachineState_Null,
13353 E_FAIL);
13354
13355 /*
13356 * On failure, set the state to the state we had when BeginPoweringDown()
13357 * was called (this is expected by Console::PowerDown() and the associated
13358 * task). On success the VM process already changed the state to
13359 * MachineState_PoweredOff, so no need to do anything.
13360 */
13361 if (FAILED(aResult))
13362 i_setMachineState(mConsoleTaskData.mLastState);
13363
13364 /* notify the progress object about operation completion */
13365 Assert(mConsoleTaskData.mProgress);
13366 if (SUCCEEDED(aResult))
13367 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13368 else
13369 {
13370 if (aErrMsg.length())
13371 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13372 COM_IIDOF(ISession),
13373 getComponentName(),
13374 aErrMsg.c_str());
13375 else
13376 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13377 }
13378
13379 /* clear out the temporary saved state data */
13380 mConsoleTaskData.mLastState = MachineState_Null;
13381 mConsoleTaskData.mProgress.setNull();
13382
13383 LogFlowThisFuncLeave();
13384 return S_OK;
13385}
13386
13387
13388/**
13389 * Goes through the USB filters of the given machine to see if the given
13390 * device matches any filter or not.
13391 *
13392 * @note Locks the same as USBController::hasMatchingFilter() does.
13393 */
13394HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13395 BOOL *aMatched,
13396 ULONG *aMaskedInterfaces)
13397{
13398 LogFlowThisFunc(("\n"));
13399
13400#ifdef VBOX_WITH_USB
13401 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13402#else
13403 NOREF(aDevice);
13404 NOREF(aMaskedInterfaces);
13405 *aMatched = FALSE;
13406#endif
13407
13408 return S_OK;
13409}
13410
13411/**
13412 * @note Locks the same as Host::captureUSBDevice() does.
13413 */
13414HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13415{
13416 LogFlowThisFunc(("\n"));
13417
13418#ifdef VBOX_WITH_USB
13419 /* if captureDeviceForVM() fails, it must have set extended error info */
13420 clearError();
13421 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13422 if (FAILED(rc)) return rc;
13423
13424 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13425 AssertReturn(service, E_FAIL);
13426 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13427#else
13428 RT_NOREF(aId, aCaptureFilename);
13429 return E_NOTIMPL;
13430#endif
13431}
13432
13433/**
13434 * @note Locks the same as Host::detachUSBDevice() does.
13435 */
13436HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13437 BOOL aDone)
13438{
13439 LogFlowThisFunc(("\n"));
13440
13441#ifdef VBOX_WITH_USB
13442 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13443 AssertReturn(service, E_FAIL);
13444 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13445#else
13446 NOREF(aId);
13447 NOREF(aDone);
13448 return E_NOTIMPL;
13449#endif
13450}
13451
13452/**
13453 * Inserts all machine filters to the USB proxy service and then calls
13454 * Host::autoCaptureUSBDevices().
13455 *
13456 * Called by Console from the VM process upon VM startup.
13457 *
13458 * @note Locks what called methods lock.
13459 */
13460HRESULT SessionMachine::autoCaptureUSBDevices()
13461{
13462 LogFlowThisFunc(("\n"));
13463
13464#ifdef VBOX_WITH_USB
13465 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13466 AssertComRC(rc);
13467 NOREF(rc);
13468
13469 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13470 AssertReturn(service, E_FAIL);
13471 return service->autoCaptureDevicesForVM(this);
13472#else
13473 return S_OK;
13474#endif
13475}
13476
13477/**
13478 * Removes all machine filters from the USB proxy service and then calls
13479 * Host::detachAllUSBDevices().
13480 *
13481 * Called by Console from the VM process upon normal VM termination or by
13482 * SessionMachine::uninit() upon abnormal VM termination (from under the
13483 * Machine/SessionMachine lock).
13484 *
13485 * @note Locks what called methods lock.
13486 */
13487HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13488{
13489 LogFlowThisFunc(("\n"));
13490
13491#ifdef VBOX_WITH_USB
13492 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13493 AssertComRC(rc);
13494 NOREF(rc);
13495
13496 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13497 AssertReturn(service, E_FAIL);
13498 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13499#else
13500 NOREF(aDone);
13501 return S_OK;
13502#endif
13503}
13504
13505/**
13506 * @note Locks this object for writing.
13507 */
13508HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13509 ComPtr<IProgress> &aProgress)
13510{
13511 LogFlowThisFuncEnter();
13512
13513 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13514 /*
13515 * We don't assert below because it might happen that a non-direct session
13516 * informs us it is closed right after we've been uninitialized -- it's ok.
13517 */
13518
13519 /* get IInternalSessionControl interface */
13520 ComPtr<IInternalSessionControl> control(aSession);
13521
13522 ComAssertRet(!control.isNull(), E_INVALIDARG);
13523
13524 /* Creating a Progress object requires the VirtualBox lock, and
13525 * thus locking it here is required by the lock order rules. */
13526 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13527
13528 if (control == mData->mSession.mDirectControl)
13529 {
13530 /* The direct session is being normally closed by the client process
13531 * ----------------------------------------------------------------- */
13532
13533 /* go to the closing state (essential for all open*Session() calls and
13534 * for #i_checkForDeath()) */
13535 Assert(mData->mSession.mState == SessionState_Locked);
13536 mData->mSession.mState = SessionState_Unlocking;
13537
13538 /* set direct control to NULL to release the remote instance */
13539 mData->mSession.mDirectControl.setNull();
13540 LogFlowThisFunc(("Direct control is set to NULL\n"));
13541
13542 if (mData->mSession.mProgress)
13543 {
13544 /* finalize the progress, someone might wait if a frontend
13545 * closes the session before powering on the VM. */
13546 mData->mSession.mProgress->notifyComplete(E_FAIL,
13547 COM_IIDOF(ISession),
13548 getComponentName(),
13549 tr("The VM session was closed before any attempt to power it on"));
13550 mData->mSession.mProgress.setNull();
13551 }
13552
13553 /* Create the progress object the client will use to wait until
13554 * #i_checkForDeath() is called to uninitialize this session object after
13555 * it releases the IPC semaphore.
13556 * Note! Because we're "reusing" mProgress here, this must be a proxy
13557 * object just like for LaunchVMProcess. */
13558 Assert(mData->mSession.mProgress.isNull());
13559 ComObjPtr<ProgressProxy> progress;
13560 progress.createObject();
13561 ComPtr<IUnknown> pPeer(mPeer);
13562 progress->init(mParent, pPeer,
13563 Bstr(tr("Closing session")).raw(),
13564 FALSE /* aCancelable */);
13565 progress.queryInterfaceTo(aProgress.asOutParam());
13566 mData->mSession.mProgress = progress;
13567 }
13568 else
13569 {
13570 /* the remote session is being normally closed */
13571 bool found = false;
13572 for (Data::Session::RemoteControlList::iterator
13573 it = mData->mSession.mRemoteControls.begin();
13574 it != mData->mSession.mRemoteControls.end();
13575 ++it)
13576 {
13577 if (control == *it)
13578 {
13579 found = true;
13580 // This MUST be erase(it), not remove(*it) as the latter
13581 // triggers a very nasty use after free due to the place where
13582 // the value "lives".
13583 mData->mSession.mRemoteControls.erase(it);
13584 break;
13585 }
13586 }
13587 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13588 E_INVALIDARG);
13589 }
13590
13591 /* signal the client watcher thread, because the client is going away */
13592 mParent->i_updateClientWatcher();
13593
13594 LogFlowThisFuncLeave();
13595 return S_OK;
13596}
13597
13598HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13599{
13600#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13601 ULONG uID;
13602 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13603 if (RT_SUCCESS(rc))
13604 {
13605 if (aID)
13606 *aID = uID;
13607 return S_OK;
13608 }
13609 return E_FAIL;
13610#else
13611 RT_NOREF(aParms, aID);
13612 ReturnComNotImplemented();
13613#endif
13614}
13615
13616HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13617{
13618#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13619 return mParent->i_onClipboardAreaUnregister(aID);
13620#else
13621 RT_NOREF(aID);
13622 ReturnComNotImplemented();
13623#endif
13624}
13625
13626HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13627{
13628#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13629 return mParent->i_onClipboardAreaAttach(aID);
13630#else
13631 RT_NOREF(aID);
13632 ReturnComNotImplemented();
13633#endif
13634}
13635HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13636{
13637#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13638 return mParent->i_onClipboardAreaDetach(aID);
13639#else
13640 RT_NOREF(aID);
13641 ReturnComNotImplemented();
13642#endif
13643}
13644
13645HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13646{
13647#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13648 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13649 if (aID)
13650 *aID = uID;
13651 return S_OK;
13652#else
13653 RT_NOREF(aID);
13654 ReturnComNotImplemented();
13655#endif
13656}
13657
13658HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13659{
13660#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13661 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13662 if (aRefCount)
13663 *aRefCount = uRefCount;
13664 return S_OK;
13665#else
13666 RT_NOREF(aID, aRefCount);
13667 ReturnComNotImplemented();
13668#endif
13669}
13670
13671HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13672 std::vector<com::Utf8Str> &aValues,
13673 std::vector<LONG64> &aTimestamps,
13674 std::vector<com::Utf8Str> &aFlags)
13675{
13676 LogFlowThisFunc(("\n"));
13677
13678#ifdef VBOX_WITH_GUEST_PROPS
13679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13680
13681 size_t cEntries = mHWData->mGuestProperties.size();
13682 aNames.resize(cEntries);
13683 aValues.resize(cEntries);
13684 aTimestamps.resize(cEntries);
13685 aFlags.resize(cEntries);
13686
13687 size_t i = 0;
13688 for (HWData::GuestPropertyMap::const_iterator
13689 it = mHWData->mGuestProperties.begin();
13690 it != mHWData->mGuestProperties.end();
13691 ++it, ++i)
13692 {
13693 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13694 aNames[i] = it->first;
13695 aValues[i] = it->second.strValue;
13696 aTimestamps[i] = it->second.mTimestamp;
13697
13698 /* If it is NULL, keep it NULL. */
13699 if (it->second.mFlags)
13700 {
13701 GuestPropWriteFlags(it->second.mFlags, szFlags);
13702 aFlags[i] = szFlags;
13703 }
13704 else
13705 aFlags[i] = "";
13706 }
13707 return S_OK;
13708#else
13709 ReturnComNotImplemented();
13710#endif
13711}
13712
13713HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13714 const com::Utf8Str &aValue,
13715 LONG64 aTimestamp,
13716 const com::Utf8Str &aFlags)
13717{
13718 LogFlowThisFunc(("\n"));
13719
13720#ifdef VBOX_WITH_GUEST_PROPS
13721 try
13722 {
13723 /*
13724 * Convert input up front.
13725 */
13726 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13727 if (aFlags.length())
13728 {
13729 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13730 AssertRCReturn(vrc, E_INVALIDARG);
13731 }
13732
13733 /*
13734 * Now grab the object lock, validate the state and do the update.
13735 */
13736
13737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13738
13739 if (!Global::IsOnline(mData->mMachineState))
13740 {
13741 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13742 VBOX_E_INVALID_VM_STATE);
13743 }
13744
13745 i_setModified(IsModified_MachineData);
13746 mHWData.backup();
13747
13748 bool fDelete = !aValue.length();
13749 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13750 if (it != mHWData->mGuestProperties.end())
13751 {
13752 if (!fDelete)
13753 {
13754 it->second.strValue = aValue;
13755 it->second.mTimestamp = aTimestamp;
13756 it->second.mFlags = fFlags;
13757 }
13758 else
13759 mHWData->mGuestProperties.erase(it);
13760
13761 mData->mGuestPropertiesModified = TRUE;
13762 }
13763 else if (!fDelete)
13764 {
13765 HWData::GuestProperty prop;
13766 prop.strValue = aValue;
13767 prop.mTimestamp = aTimestamp;
13768 prop.mFlags = fFlags;
13769
13770 mHWData->mGuestProperties[aName] = prop;
13771 mData->mGuestPropertiesModified = TRUE;
13772 }
13773
13774 alock.release();
13775
13776 mParent->i_onGuestPropertyChange(mData->mUuid,
13777 Bstr(aName).raw(),
13778 Bstr(aValue).raw(),
13779 Bstr(aFlags).raw());
13780 }
13781 catch (...)
13782 {
13783 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13784 }
13785 return S_OK;
13786#else
13787 ReturnComNotImplemented();
13788#endif
13789}
13790
13791
13792HRESULT SessionMachine::lockMedia()
13793{
13794 AutoMultiWriteLock2 alock(this->lockHandle(),
13795 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13796
13797 AssertReturn( mData->mMachineState == MachineState_Starting
13798 || mData->mMachineState == MachineState_Restoring
13799 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13800
13801 clearError();
13802 alock.release();
13803 return i_lockMedia();
13804}
13805
13806HRESULT SessionMachine::unlockMedia()
13807{
13808 HRESULT hrc = i_unlockMedia();
13809 return hrc;
13810}
13811
13812HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13813 ComPtr<IMediumAttachment> &aNewAttachment)
13814{
13815 // request the host lock first, since might be calling Host methods for getting host drives;
13816 // next, protect the media tree all the while we're in here, as well as our member variables
13817 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13818 this->lockHandle(),
13819 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13820
13821 IMediumAttachment *iAttach = aAttachment;
13822 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13823
13824 Utf8Str ctrlName;
13825 LONG lPort;
13826 LONG lDevice;
13827 bool fTempEject;
13828 {
13829 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13830
13831 /* Need to query the details first, as the IMediumAttachment reference
13832 * might be to the original settings, which we are going to change. */
13833 ctrlName = pAttach->i_getControllerName();
13834 lPort = pAttach->i_getPort();
13835 lDevice = pAttach->i_getDevice();
13836 fTempEject = pAttach->i_getTempEject();
13837 }
13838
13839 if (!fTempEject)
13840 {
13841 /* Remember previously mounted medium. The medium before taking the
13842 * backup is not necessarily the same thing. */
13843 ComObjPtr<Medium> oldmedium;
13844 oldmedium = pAttach->i_getMedium();
13845
13846 i_setModified(IsModified_Storage);
13847 mMediumAttachments.backup();
13848
13849 // The backup operation makes the pAttach reference point to the
13850 // old settings. Re-get the correct reference.
13851 pAttach = i_findAttachment(*mMediumAttachments.data(),
13852 ctrlName,
13853 lPort,
13854 lDevice);
13855
13856 {
13857 AutoCaller autoAttachCaller(this);
13858 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13859
13860 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13861 if (!oldmedium.isNull())
13862 oldmedium->i_removeBackReference(mData->mUuid);
13863
13864 pAttach->i_updateMedium(NULL);
13865 pAttach->i_updateEjected();
13866 }
13867
13868 i_setModified(IsModified_Storage);
13869 }
13870 else
13871 {
13872 {
13873 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13874 pAttach->i_updateEjected();
13875 }
13876 }
13877
13878 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13879
13880 return S_OK;
13881}
13882
13883HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13884 com::Utf8Str &aResult)
13885{
13886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13887
13888 HRESULT hr = S_OK;
13889
13890 if (!mAuthLibCtx.hAuthLibrary)
13891 {
13892 /* Load the external authentication library. */
13893 Bstr authLibrary;
13894 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13895
13896 Utf8Str filename = authLibrary;
13897
13898 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13899 if (RT_FAILURE(vrc))
13900 hr = setErrorBoth(E_FAIL, vrc,
13901 tr("Could not load the external authentication library '%s' (%Rrc)"),
13902 filename.c_str(), vrc);
13903 }
13904
13905 /* The auth library might need the machine lock. */
13906 alock.release();
13907
13908 if (FAILED(hr))
13909 return hr;
13910
13911 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13912 {
13913 enum VRDEAuthParams
13914 {
13915 parmUuid = 1,
13916 parmGuestJudgement,
13917 parmUser,
13918 parmPassword,
13919 parmDomain,
13920 parmClientId
13921 };
13922
13923 AuthResult result = AuthResultAccessDenied;
13924
13925 Guid uuid(aAuthParams[parmUuid]);
13926 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13927 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13928
13929 result = AuthLibAuthenticate(&mAuthLibCtx,
13930 uuid.raw(), guestJudgement,
13931 aAuthParams[parmUser].c_str(),
13932 aAuthParams[parmPassword].c_str(),
13933 aAuthParams[parmDomain].c_str(),
13934 u32ClientId);
13935
13936 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13937 size_t cbPassword = aAuthParams[parmPassword].length();
13938 if (cbPassword)
13939 {
13940 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13941 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13942 }
13943
13944 if (result == AuthResultAccessGranted)
13945 aResult = "granted";
13946 else
13947 aResult = "denied";
13948
13949 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13950 aAuthParams[parmUser].c_str(), aResult.c_str()));
13951 }
13952 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13953 {
13954 enum VRDEAuthDisconnectParams
13955 {
13956 parmUuid = 1,
13957 parmClientId
13958 };
13959
13960 Guid uuid(aAuthParams[parmUuid]);
13961 uint32_t u32ClientId = 0;
13962 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13963 }
13964 else
13965 {
13966 hr = E_INVALIDARG;
13967 }
13968
13969 return hr;
13970}
13971
13972// public methods only for internal purposes
13973/////////////////////////////////////////////////////////////////////////////
13974
13975#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13976/**
13977 * Called from the client watcher thread to check for expected or unexpected
13978 * death of the client process that has a direct session to this machine.
13979 *
13980 * On Win32 and on OS/2, this method is called only when we've got the
13981 * mutex (i.e. the client has either died or terminated normally) so it always
13982 * returns @c true (the client is terminated, the session machine is
13983 * uninitialized).
13984 *
13985 * On other platforms, the method returns @c true if the client process has
13986 * terminated normally or abnormally and the session machine was uninitialized,
13987 * and @c false if the client process is still alive.
13988 *
13989 * @note Locks this object for writing.
13990 */
13991bool SessionMachine::i_checkForDeath()
13992{
13993 Uninit::Reason reason;
13994 bool terminated = false;
13995
13996 /* Enclose autoCaller with a block because calling uninit() from under it
13997 * will deadlock. */
13998 {
13999 AutoCaller autoCaller(this);
14000 if (!autoCaller.isOk())
14001 {
14002 /* return true if not ready, to cause the client watcher to exclude
14003 * the corresponding session from watching */
14004 LogFlowThisFunc(("Already uninitialized!\n"));
14005 return true;
14006 }
14007
14008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14009
14010 /* Determine the reason of death: if the session state is Closing here,
14011 * everything is fine. Otherwise it means that the client did not call
14012 * OnSessionEnd() before it released the IPC semaphore. This may happen
14013 * either because the client process has abnormally terminated, or
14014 * because it simply forgot to call ISession::Close() before exiting. We
14015 * threat the latter also as an abnormal termination (see
14016 * Session::uninit() for details). */
14017 reason = mData->mSession.mState == SessionState_Unlocking ?
14018 Uninit::Normal :
14019 Uninit::Abnormal;
14020
14021 if (mClientToken)
14022 terminated = mClientToken->release();
14023 } /* AutoCaller block */
14024
14025 if (terminated)
14026 uninit(reason);
14027
14028 return terminated;
14029}
14030
14031void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14032{
14033 LogFlowThisFunc(("\n"));
14034
14035 strTokenId.setNull();
14036
14037 AutoCaller autoCaller(this);
14038 AssertComRCReturnVoid(autoCaller.rc());
14039
14040 Assert(mClientToken);
14041 if (mClientToken)
14042 mClientToken->getId(strTokenId);
14043}
14044#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14045IToken *SessionMachine::i_getToken()
14046{
14047 LogFlowThisFunc(("\n"));
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturn(autoCaller.rc(), NULL);
14051
14052 Assert(mClientToken);
14053 if (mClientToken)
14054 return mClientToken->getToken();
14055 else
14056 return NULL;
14057}
14058#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14059
14060Machine::ClientToken *SessionMachine::i_getClientToken()
14061{
14062 LogFlowThisFunc(("\n"));
14063
14064 AutoCaller autoCaller(this);
14065 AssertComRCReturn(autoCaller.rc(), NULL);
14066
14067 return mClientToken;
14068}
14069
14070
14071/**
14072 * @note Locks this object for reading.
14073 */
14074HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14075{
14076 LogFlowThisFunc(("\n"));
14077
14078 AutoCaller autoCaller(this);
14079 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14080
14081 ComPtr<IInternalSessionControl> directControl;
14082 {
14083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14084 if (mData->mSession.mLockType == LockType_VM)
14085 directControl = mData->mSession.mDirectControl;
14086 }
14087
14088 /* ignore notifications sent after #OnSessionEnd() is called */
14089 if (!directControl)
14090 return S_OK;
14091
14092 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14093}
14094
14095/**
14096 * @note Locks this object for reading.
14097 */
14098HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14099 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14100 IN_BSTR aGuestIp, LONG aGuestPort)
14101{
14102 LogFlowThisFunc(("\n"));
14103
14104 AutoCaller autoCaller(this);
14105 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14106
14107 ComPtr<IInternalSessionControl> directControl;
14108 {
14109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14110 if (mData->mSession.mLockType == LockType_VM)
14111 directControl = mData->mSession.mDirectControl;
14112 }
14113
14114 /* ignore notifications sent after #OnSessionEnd() is called */
14115 if (!directControl)
14116 return S_OK;
14117 /*
14118 * instead acting like callback we ask IVirtualBox deliver corresponding event
14119 */
14120
14121 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14122 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14123 return S_OK;
14124}
14125
14126/**
14127 * @note Locks this object for reading.
14128 */
14129HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14130{
14131 LogFlowThisFunc(("\n"));
14132
14133 AutoCaller autoCaller(this);
14134 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14135
14136 ComPtr<IInternalSessionControl> directControl;
14137 {
14138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14139 if (mData->mSession.mLockType == LockType_VM)
14140 directControl = mData->mSession.mDirectControl;
14141 }
14142
14143 /* ignore notifications sent after #OnSessionEnd() is called */
14144 if (!directControl)
14145 return S_OK;
14146
14147 return directControl->OnAudioAdapterChange(audioAdapter);
14148}
14149
14150/**
14151 * @note Locks this object for reading.
14152 */
14153HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14154{
14155 LogFlowThisFunc(("\n"));
14156
14157 AutoCaller autoCaller(this);
14158 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14159
14160 ComPtr<IInternalSessionControl> directControl;
14161 {
14162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14163 if (mData->mSession.mLockType == LockType_VM)
14164 directControl = mData->mSession.mDirectControl;
14165 }
14166
14167 /* ignore notifications sent after #OnSessionEnd() is called */
14168 if (!directControl)
14169 return S_OK;
14170
14171 return directControl->OnSerialPortChange(serialPort);
14172}
14173
14174/**
14175 * @note Locks this object for reading.
14176 */
14177HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14178{
14179 LogFlowThisFunc(("\n"));
14180
14181 AutoCaller autoCaller(this);
14182 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14183
14184 ComPtr<IInternalSessionControl> directControl;
14185 {
14186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14187 if (mData->mSession.mLockType == LockType_VM)
14188 directControl = mData->mSession.mDirectControl;
14189 }
14190
14191 /* ignore notifications sent after #OnSessionEnd() is called */
14192 if (!directControl)
14193 return S_OK;
14194
14195 return directControl->OnParallelPortChange(parallelPort);
14196}
14197
14198/**
14199 * @note Locks this object for reading.
14200 */
14201HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14202{
14203 LogFlowThisFunc(("\n"));
14204
14205 AutoCaller autoCaller(this);
14206 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14207
14208 ComPtr<IInternalSessionControl> directControl;
14209 {
14210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14211 if (mData->mSession.mLockType == LockType_VM)
14212 directControl = mData->mSession.mDirectControl;
14213 }
14214
14215 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14216
14217 /* ignore notifications sent after #OnSessionEnd() is called */
14218 if (!directControl)
14219 return S_OK;
14220
14221 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14222}
14223
14224/**
14225 * @note Locks this object for reading.
14226 */
14227HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14228{
14229 LogFlowThisFunc(("\n"));
14230
14231 AutoCaller autoCaller(this);
14232 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14233
14234 ComPtr<IInternalSessionControl> directControl;
14235 {
14236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14237 if (mData->mSession.mLockType == LockType_VM)
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 mParent->i_onMediumChanged(aAttachment);
14242
14243 /* ignore notifications sent after #OnSessionEnd() is called */
14244 if (!directControl)
14245 return S_OK;
14246
14247 return directControl->OnMediumChange(aAttachment, aForce);
14248}
14249
14250HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14251{
14252 LogFlowThisFunc(("\n"));
14253
14254 AutoCaller autoCaller(this);
14255 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14256
14257 ComPtr<IInternalSessionControl> directControl;
14258 {
14259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14260 if (mData->mSession.mLockType == LockType_VM)
14261 directControl = mData->mSession.mDirectControl;
14262 }
14263
14264 /* ignore notifications sent after #OnSessionEnd() is called */
14265 if (!directControl)
14266 return S_OK;
14267
14268 return directControl->OnVMProcessPriorityChange(aPriority);
14269}
14270
14271/**
14272 * @note Locks this object for reading.
14273 */
14274HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14275{
14276 LogFlowThisFunc(("\n"));
14277
14278 AutoCaller autoCaller(this);
14279 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14280
14281 ComPtr<IInternalSessionControl> directControl;
14282 {
14283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14284 if (mData->mSession.mLockType == LockType_VM)
14285 directControl = mData->mSession.mDirectControl;
14286 }
14287
14288 /* ignore notifications sent after #OnSessionEnd() is called */
14289 if (!directControl)
14290 return S_OK;
14291
14292 return directControl->OnCPUChange(aCPU, aRemove);
14293}
14294
14295HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14296{
14297 LogFlowThisFunc(("\n"));
14298
14299 AutoCaller autoCaller(this);
14300 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14301
14302 ComPtr<IInternalSessionControl> directControl;
14303 {
14304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14305 if (mData->mSession.mLockType == LockType_VM)
14306 directControl = mData->mSession.mDirectControl;
14307 }
14308
14309 /* ignore notifications sent after #OnSessionEnd() is called */
14310 if (!directControl)
14311 return S_OK;
14312
14313 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14314}
14315
14316/**
14317 * @note Locks this object for reading.
14318 */
14319HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14320{
14321 LogFlowThisFunc(("\n"));
14322
14323 AutoCaller autoCaller(this);
14324 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14325
14326 ComPtr<IInternalSessionControl> directControl;
14327 {
14328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14329 if (mData->mSession.mLockType == LockType_VM)
14330 directControl = mData->mSession.mDirectControl;
14331 }
14332
14333 /* ignore notifications sent after #OnSessionEnd() is called */
14334 if (!directControl)
14335 return S_OK;
14336
14337 return directControl->OnVRDEServerChange(aRestart);
14338}
14339
14340/**
14341 * @note Locks this object for reading.
14342 */
14343HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14344{
14345 LogFlowThisFunc(("\n"));
14346
14347 AutoCaller autoCaller(this);
14348 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14349
14350 ComPtr<IInternalSessionControl> directControl;
14351 {
14352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14353 if (mData->mSession.mLockType == LockType_VM)
14354 directControl = mData->mSession.mDirectControl;
14355 }
14356
14357 /* ignore notifications sent after #OnSessionEnd() is called */
14358 if (!directControl)
14359 return S_OK;
14360
14361 return directControl->OnRecordingChange(aEnable);
14362}
14363
14364/**
14365 * @note Locks this object for reading.
14366 */
14367HRESULT SessionMachine::i_onUSBControllerChange()
14368{
14369 LogFlowThisFunc(("\n"));
14370
14371 AutoCaller autoCaller(this);
14372 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14373
14374 ComPtr<IInternalSessionControl> directControl;
14375 {
14376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14377 if (mData->mSession.mLockType == LockType_VM)
14378 directControl = mData->mSession.mDirectControl;
14379 }
14380
14381 /* ignore notifications sent after #OnSessionEnd() is called */
14382 if (!directControl)
14383 return S_OK;
14384
14385 return directControl->OnUSBControllerChange();
14386}
14387
14388/**
14389 * @note Locks this object for reading.
14390 */
14391HRESULT SessionMachine::i_onSharedFolderChange()
14392{
14393 LogFlowThisFunc(("\n"));
14394
14395 AutoCaller autoCaller(this);
14396 AssertComRCReturnRC(autoCaller.rc());
14397
14398 ComPtr<IInternalSessionControl> directControl;
14399 {
14400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14401 if (mData->mSession.mLockType == LockType_VM)
14402 directControl = mData->mSession.mDirectControl;
14403 }
14404
14405 /* ignore notifications sent after #OnSessionEnd() is called */
14406 if (!directControl)
14407 return S_OK;
14408
14409 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14410}
14411
14412/**
14413 * @note Locks this object for reading.
14414 */
14415HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14416{
14417 LogFlowThisFunc(("\n"));
14418
14419 AutoCaller autoCaller(this);
14420 AssertComRCReturnRC(autoCaller.rc());
14421
14422 ComPtr<IInternalSessionControl> directControl;
14423 {
14424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14425 if (mData->mSession.mLockType == LockType_VM)
14426 directControl = mData->mSession.mDirectControl;
14427 }
14428
14429 /* ignore notifications sent after #OnSessionEnd() is called */
14430 if (!directControl)
14431 return S_OK;
14432
14433 return directControl->OnClipboardModeChange(aClipboardMode);
14434}
14435
14436/**
14437 * @note Locks this object for reading.
14438 */
14439HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14440{
14441 LogFlowThisFunc(("\n"));
14442
14443 AutoCaller autoCaller(this);
14444 AssertComRCReturnRC(autoCaller.rc());
14445
14446 ComPtr<IInternalSessionControl> directControl;
14447 {
14448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14449 if (mData->mSession.mLockType == LockType_VM)
14450 directControl = mData->mSession.mDirectControl;
14451 }
14452
14453 /* ignore notifications sent after #OnSessionEnd() is called */
14454 if (!directControl)
14455 return S_OK;
14456
14457 return directControl->OnClipboardFileTransferModeChange(aEnable);
14458}
14459
14460/**
14461 * @note Locks this object for reading.
14462 */
14463HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14464{
14465 LogFlowThisFunc(("\n"));
14466
14467 AutoCaller autoCaller(this);
14468 AssertComRCReturnRC(autoCaller.rc());
14469
14470 ComPtr<IInternalSessionControl> directControl;
14471 {
14472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14473 if (mData->mSession.mLockType == LockType_VM)
14474 directControl = mData->mSession.mDirectControl;
14475 }
14476
14477 /* ignore notifications sent after #OnSessionEnd() is called */
14478 if (!directControl)
14479 return S_OK;
14480
14481 return directControl->OnDnDModeChange(aDnDMode);
14482}
14483
14484/**
14485 * @note Locks this object for reading.
14486 */
14487HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14488{
14489 LogFlowThisFunc(("\n"));
14490
14491 AutoCaller autoCaller(this);
14492 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14493
14494 ComPtr<IInternalSessionControl> directControl;
14495 {
14496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14497 if (mData->mSession.mLockType == LockType_VM)
14498 directControl = mData->mSession.mDirectControl;
14499 }
14500
14501 /* ignore notifications sent after #OnSessionEnd() is called */
14502 if (!directControl)
14503 return S_OK;
14504
14505 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14506}
14507
14508/**
14509 * @note Locks this object for reading.
14510 */
14511HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14512{
14513 LogFlowThisFunc(("\n"));
14514
14515 AutoCaller autoCaller(this);
14516 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14517
14518 ComPtr<IInternalSessionControl> directControl;
14519 {
14520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14521 if (mData->mSession.mLockType == LockType_VM)
14522 directControl = mData->mSession.mDirectControl;
14523 }
14524
14525 /* ignore notifications sent after #OnSessionEnd() is called */
14526 if (!directControl)
14527 return S_OK;
14528
14529 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14530}
14531
14532/**
14533 * Returns @c true if this machine's USB controller reports it has a matching
14534 * filter for the given USB device and @c false otherwise.
14535 *
14536 * @note locks this object for reading.
14537 */
14538bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14539{
14540 AutoCaller autoCaller(this);
14541 /* silently return if not ready -- this method may be called after the
14542 * direct machine session has been called */
14543 if (!autoCaller.isOk())
14544 return false;
14545
14546#ifdef VBOX_WITH_USB
14547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14548
14549 switch (mData->mMachineState)
14550 {
14551 case MachineState_Starting:
14552 case MachineState_Restoring:
14553 case MachineState_TeleportingIn:
14554 case MachineState_Paused:
14555 case MachineState_Running:
14556 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14557 * elsewhere... */
14558 alock.release();
14559 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14560 default: break;
14561 }
14562#else
14563 NOREF(aDevice);
14564 NOREF(aMaskedIfs);
14565#endif
14566 return false;
14567}
14568
14569/**
14570 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14571 */
14572HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14573 IVirtualBoxErrorInfo *aError,
14574 ULONG aMaskedIfs,
14575 const com::Utf8Str &aCaptureFilename)
14576{
14577 LogFlowThisFunc(("\n"));
14578
14579 AutoCaller autoCaller(this);
14580
14581 /* This notification may happen after the machine object has been
14582 * uninitialized (the session was closed), so don't assert. */
14583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14584
14585 ComPtr<IInternalSessionControl> directControl;
14586 {
14587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14588 if (mData->mSession.mLockType == LockType_VM)
14589 directControl = mData->mSession.mDirectControl;
14590 }
14591
14592 /* fail on notifications sent after #OnSessionEnd() is called, it is
14593 * expected by the caller */
14594 if (!directControl)
14595 return E_FAIL;
14596
14597 /* No locks should be held at this point. */
14598 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14599 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14600
14601 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14602}
14603
14604/**
14605 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14606 */
14607HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14608 IVirtualBoxErrorInfo *aError)
14609{
14610 LogFlowThisFunc(("\n"));
14611
14612 AutoCaller autoCaller(this);
14613
14614 /* This notification may happen after the machine object has been
14615 * uninitialized (the session was closed), so don't assert. */
14616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14617
14618 ComPtr<IInternalSessionControl> directControl;
14619 {
14620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14621 if (mData->mSession.mLockType == LockType_VM)
14622 directControl = mData->mSession.mDirectControl;
14623 }
14624
14625 /* fail on notifications sent after #OnSessionEnd() is called, it is
14626 * expected by the caller */
14627 if (!directControl)
14628 return E_FAIL;
14629
14630 /* No locks should be held at this point. */
14631 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14632 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14633
14634 return directControl->OnUSBDeviceDetach(aId, aError);
14635}
14636
14637// protected methods
14638/////////////////////////////////////////////////////////////////////////////
14639
14640/**
14641 * Deletes the given file if it is no longer in use by either the current machine state
14642 * (if the machine is "saved") or any of the machine's snapshots.
14643 *
14644 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14645 * but is different for each SnapshotMachine. When calling this, the order of calling this
14646 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14647 * is therefore critical. I know, it's all rather messy.
14648 *
14649 * @param strStateFile
14650 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14651 * the test for whether the saved state file is in use.
14652 */
14653void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14654 Snapshot *pSnapshotToIgnore)
14655{
14656 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14657 if ( (strStateFile.isNotEmpty())
14658 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14659 )
14660 // ... and it must also not be shared with other snapshots
14661 if ( !mData->mFirstSnapshot
14662 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14663 // this checks the SnapshotMachine's state file paths
14664 )
14665 RTFileDelete(strStateFile.c_str());
14666}
14667
14668/**
14669 * Locks the attached media.
14670 *
14671 * All attached hard disks are locked for writing and DVD/floppy are locked for
14672 * reading. Parents of attached hard disks (if any) are locked for reading.
14673 *
14674 * This method also performs accessibility check of all media it locks: if some
14675 * media is inaccessible, the method will return a failure and a bunch of
14676 * extended error info objects per each inaccessible medium.
14677 *
14678 * Note that this method is atomic: if it returns a success, all media are
14679 * locked as described above; on failure no media is locked at all (all
14680 * succeeded individual locks will be undone).
14681 *
14682 * The caller is responsible for doing the necessary state sanity checks.
14683 *
14684 * The locks made by this method must be undone by calling #unlockMedia() when
14685 * no more needed.
14686 */
14687HRESULT SessionMachine::i_lockMedia()
14688{
14689 AutoCaller autoCaller(this);
14690 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14691
14692 AutoMultiWriteLock2 alock(this->lockHandle(),
14693 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14694
14695 /* bail out if trying to lock things with already set up locking */
14696 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14697
14698 MultiResult mrc(S_OK);
14699
14700 /* Collect locking information for all medium objects attached to the VM. */
14701 for (MediumAttachmentList::const_iterator
14702 it = mMediumAttachments->begin();
14703 it != mMediumAttachments->end();
14704 ++it)
14705 {
14706 MediumAttachment *pAtt = *it;
14707 DeviceType_T devType = pAtt->i_getType();
14708 Medium *pMedium = pAtt->i_getMedium();
14709
14710 MediumLockList *pMediumLockList(new MediumLockList());
14711 // There can be attachments without a medium (floppy/dvd), and thus
14712 // it's impossible to create a medium lock list. It still makes sense
14713 // to have the empty medium lock list in the map in case a medium is
14714 // attached later.
14715 if (pMedium != NULL)
14716 {
14717 MediumType_T mediumType = pMedium->i_getType();
14718 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14719 || mediumType == MediumType_Shareable;
14720 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14721
14722 alock.release();
14723 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14724 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14725 false /* fMediumLockWriteAll */,
14726 NULL,
14727 *pMediumLockList);
14728 alock.acquire();
14729 if (FAILED(mrc))
14730 {
14731 delete pMediumLockList;
14732 mData->mSession.mLockedMedia.Clear();
14733 break;
14734 }
14735 }
14736
14737 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14738 if (FAILED(rc))
14739 {
14740 mData->mSession.mLockedMedia.Clear();
14741 mrc = setError(rc,
14742 tr("Collecting locking information for all attached media failed"));
14743 break;
14744 }
14745 }
14746
14747 if (SUCCEEDED(mrc))
14748 {
14749 /* Now lock all media. If this fails, nothing is locked. */
14750 alock.release();
14751 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14752 alock.acquire();
14753 if (FAILED(rc))
14754 {
14755 mrc = setError(rc,
14756 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14757 }
14758 }
14759
14760 return mrc;
14761}
14762
14763/**
14764 * Undoes the locks made by by #lockMedia().
14765 */
14766HRESULT SessionMachine::i_unlockMedia()
14767{
14768 AutoCaller autoCaller(this);
14769 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14770
14771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14772
14773 /* we may be holding important error info on the current thread;
14774 * preserve it */
14775 ErrorInfoKeeper eik;
14776
14777 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14778 AssertComRC(rc);
14779 return rc;
14780}
14781
14782/**
14783 * Helper to change the machine state (reimplementation).
14784 *
14785 * @note Locks this object for writing.
14786 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14787 * it can cause crashes in random places due to unexpectedly committing
14788 * the current settings. The caller is responsible for that. The call
14789 * to saveStateSettings is fine, because this method does not commit.
14790 */
14791HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14792{
14793 LogFlowThisFuncEnter();
14794 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14795
14796 AutoCaller autoCaller(this);
14797 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14798
14799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14800
14801 MachineState_T oldMachineState = mData->mMachineState;
14802
14803 AssertMsgReturn(oldMachineState != aMachineState,
14804 ("oldMachineState=%s, aMachineState=%s\n",
14805 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14806 E_FAIL);
14807
14808 HRESULT rc = S_OK;
14809
14810 int stsFlags = 0;
14811 bool deleteSavedState = false;
14812
14813 /* detect some state transitions */
14814
14815 if ( ( oldMachineState == MachineState_Saved
14816 && aMachineState == MachineState_Restoring)
14817 || ( ( oldMachineState == MachineState_PoweredOff
14818 || oldMachineState == MachineState_Teleported
14819 || oldMachineState == MachineState_Aborted
14820 )
14821 && ( aMachineState == MachineState_TeleportingIn
14822 || aMachineState == MachineState_Starting
14823 )
14824 )
14825 )
14826 {
14827 /* The EMT thread is about to start */
14828
14829 /* Nothing to do here for now... */
14830
14831 /// @todo NEWMEDIA don't let mDVDDrive and other children
14832 /// change anything when in the Starting/Restoring state
14833 }
14834 else if ( ( oldMachineState == MachineState_Running
14835 || oldMachineState == MachineState_Paused
14836 || oldMachineState == MachineState_Teleporting
14837 || oldMachineState == MachineState_OnlineSnapshotting
14838 || oldMachineState == MachineState_LiveSnapshotting
14839 || oldMachineState == MachineState_Stuck
14840 || oldMachineState == MachineState_Starting
14841 || oldMachineState == MachineState_Stopping
14842 || oldMachineState == MachineState_Saving
14843 || oldMachineState == MachineState_Restoring
14844 || oldMachineState == MachineState_TeleportingPausedVM
14845 || oldMachineState == MachineState_TeleportingIn
14846 )
14847 && ( aMachineState == MachineState_PoweredOff
14848 || aMachineState == MachineState_Saved
14849 || aMachineState == MachineState_Teleported
14850 || aMachineState == MachineState_Aborted
14851 )
14852 )
14853 {
14854 /* The EMT thread has just stopped, unlock attached media. Note that as
14855 * opposed to locking that is done from Console, we do unlocking here
14856 * because the VM process may have aborted before having a chance to
14857 * properly unlock all media it locked. */
14858
14859 unlockMedia();
14860 }
14861
14862 if (oldMachineState == MachineState_Restoring)
14863 {
14864 if (aMachineState != MachineState_Saved)
14865 {
14866 /*
14867 * delete the saved state file once the machine has finished
14868 * restoring from it (note that Console sets the state from
14869 * Restoring to Saved if the VM couldn't restore successfully,
14870 * to give the user an ability to fix an error and retry --
14871 * we keep the saved state file in this case)
14872 */
14873 deleteSavedState = true;
14874 }
14875 }
14876 else if ( oldMachineState == MachineState_Saved
14877 && ( aMachineState == MachineState_PoweredOff
14878 || aMachineState == MachineState_Aborted
14879 || aMachineState == MachineState_Teleported
14880 )
14881 )
14882 {
14883 /*
14884 * delete the saved state after SessionMachine::ForgetSavedState() is called
14885 * or if the VM process (owning a direct VM session) crashed while the
14886 * VM was Saved
14887 */
14888
14889 /// @todo (dmik)
14890 // Not sure that deleting the saved state file just because of the
14891 // client death before it attempted to restore the VM is a good
14892 // thing. But when it crashes we need to go to the Aborted state
14893 // which cannot have the saved state file associated... The only
14894 // way to fix this is to make the Aborted condition not a VM state
14895 // but a bool flag: i.e., when a crash occurs, set it to true and
14896 // change the state to PoweredOff or Saved depending on the
14897 // saved state presence.
14898
14899 deleteSavedState = true;
14900 mData->mCurrentStateModified = TRUE;
14901 stsFlags |= SaveSTS_CurStateModified;
14902 }
14903
14904 if ( aMachineState == MachineState_Starting
14905 || aMachineState == MachineState_Restoring
14906 || aMachineState == MachineState_TeleportingIn
14907 )
14908 {
14909 /* set the current state modified flag to indicate that the current
14910 * state is no more identical to the state in the
14911 * current snapshot */
14912 if (!mData->mCurrentSnapshot.isNull())
14913 {
14914 mData->mCurrentStateModified = TRUE;
14915 stsFlags |= SaveSTS_CurStateModified;
14916 }
14917 }
14918
14919 if (deleteSavedState)
14920 {
14921 if (mRemoveSavedState)
14922 {
14923 Assert(!mSSData->strStateFilePath.isEmpty());
14924
14925 // it is safe to delete the saved state file if ...
14926 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14927 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14928 // ... none of the snapshots share the saved state file
14929 )
14930 RTFileDelete(mSSData->strStateFilePath.c_str());
14931 }
14932
14933 mSSData->strStateFilePath.setNull();
14934 stsFlags |= SaveSTS_StateFilePath;
14935 }
14936
14937 /* redirect to the underlying peer machine */
14938 mPeer->i_setMachineState(aMachineState);
14939
14940 if ( oldMachineState != MachineState_RestoringSnapshot
14941 && ( aMachineState == MachineState_PoweredOff
14942 || aMachineState == MachineState_Teleported
14943 || aMachineState == MachineState_Aborted
14944 || aMachineState == MachineState_Saved))
14945 {
14946 /* the machine has stopped execution
14947 * (or the saved state file was adopted) */
14948 stsFlags |= SaveSTS_StateTimeStamp;
14949 }
14950
14951 if ( ( oldMachineState == MachineState_PoweredOff
14952 || oldMachineState == MachineState_Aborted
14953 || oldMachineState == MachineState_Teleported
14954 )
14955 && aMachineState == MachineState_Saved)
14956 {
14957 /* the saved state file was adopted */
14958 Assert(!mSSData->strStateFilePath.isEmpty());
14959 stsFlags |= SaveSTS_StateFilePath;
14960 }
14961
14962#ifdef VBOX_WITH_GUEST_PROPS
14963 if ( aMachineState == MachineState_PoweredOff
14964 || aMachineState == MachineState_Aborted
14965 || aMachineState == MachineState_Teleported)
14966 {
14967 /* Make sure any transient guest properties get removed from the
14968 * property store on shutdown. */
14969 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14970
14971 /* remove it from the settings representation */
14972 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14973 for (settings::GuestPropertiesList::iterator
14974 it = llGuestProperties.begin();
14975 it != llGuestProperties.end();
14976 /*nothing*/)
14977 {
14978 const settings::GuestProperty &prop = *it;
14979 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14980 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14981 {
14982 it = llGuestProperties.erase(it);
14983 fNeedsSaving = true;
14984 }
14985 else
14986 {
14987 ++it;
14988 }
14989 }
14990
14991 /* Additionally remove it from the HWData representation. Required to
14992 * keep everything in sync, as this is what the API keeps using. */
14993 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14994 for (HWData::GuestPropertyMap::iterator
14995 it = llHWGuestProperties.begin();
14996 it != llHWGuestProperties.end();
14997 /*nothing*/)
14998 {
14999 uint32_t fFlags = it->second.mFlags;
15000 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15001 {
15002 /* iterator where we need to continue after the erase call
15003 * (C++03 is a fact still, and it doesn't return the iterator
15004 * which would allow continuing) */
15005 HWData::GuestPropertyMap::iterator it2 = it;
15006 ++it2;
15007 llHWGuestProperties.erase(it);
15008 it = it2;
15009 fNeedsSaving = true;
15010 }
15011 else
15012 {
15013 ++it;
15014 }
15015 }
15016
15017 if (fNeedsSaving)
15018 {
15019 mData->mCurrentStateModified = TRUE;
15020 stsFlags |= SaveSTS_CurStateModified;
15021 }
15022 }
15023#endif /* VBOX_WITH_GUEST_PROPS */
15024
15025 rc = i_saveStateSettings(stsFlags);
15026
15027 if ( ( oldMachineState != MachineState_PoweredOff
15028 && oldMachineState != MachineState_Aborted
15029 && oldMachineState != MachineState_Teleported
15030 )
15031 && ( aMachineState == MachineState_PoweredOff
15032 || aMachineState == MachineState_Aborted
15033 || aMachineState == MachineState_Teleported
15034 )
15035 )
15036 {
15037 /* we've been shut down for any reason */
15038 /* no special action so far */
15039 }
15040
15041 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15042 LogFlowThisFuncLeave();
15043 return rc;
15044}
15045
15046/**
15047 * Sends the current machine state value to the VM process.
15048 *
15049 * @note Locks this object for reading, then calls a client process.
15050 */
15051HRESULT SessionMachine::i_updateMachineStateOnClient()
15052{
15053 AutoCaller autoCaller(this);
15054 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15055
15056 ComPtr<IInternalSessionControl> directControl;
15057 {
15058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15059 AssertReturn(!!mData, E_FAIL);
15060 if (mData->mSession.mLockType == LockType_VM)
15061 directControl = mData->mSession.mDirectControl;
15062
15063 /* directControl may be already set to NULL here in #OnSessionEnd()
15064 * called too early by the direct session process while there is still
15065 * some operation (like deleting the snapshot) in progress. The client
15066 * process in this case is waiting inside Session::close() for the
15067 * "end session" process object to complete, while #uninit() called by
15068 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15069 * operation to complete. For now, we accept this inconsistent behavior
15070 * and simply do nothing here. */
15071
15072 if (mData->mSession.mState == SessionState_Unlocking)
15073 return S_OK;
15074 }
15075
15076 /* ignore notifications sent after #OnSessionEnd() is called */
15077 if (!directControl)
15078 return S_OK;
15079
15080 return directControl->UpdateMachineState(mData->mMachineState);
15081}
15082
15083
15084/*static*/
15085HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15086{
15087 va_list args;
15088 va_start(args, pcszMsg);
15089 HRESULT rc = setErrorInternal(aResultCode,
15090 getStaticClassIID(),
15091 getStaticComponentName(),
15092 Utf8Str(pcszMsg, args),
15093 false /* aWarning */,
15094 true /* aLogIt */);
15095 va_end(args);
15096 return rc;
15097}
15098
15099
15100HRESULT Machine::updateState(MachineState_T aState)
15101{
15102 NOREF(aState);
15103 ReturnComNotImplemented();
15104}
15105
15106HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15107{
15108 NOREF(aProgress);
15109 ReturnComNotImplemented();
15110}
15111
15112HRESULT Machine::endPowerUp(LONG aResult)
15113{
15114 NOREF(aResult);
15115 ReturnComNotImplemented();
15116}
15117
15118HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15119{
15120 NOREF(aProgress);
15121 ReturnComNotImplemented();
15122}
15123
15124HRESULT Machine::endPoweringDown(LONG aResult,
15125 const com::Utf8Str &aErrMsg)
15126{
15127 NOREF(aResult);
15128 NOREF(aErrMsg);
15129 ReturnComNotImplemented();
15130}
15131
15132HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15133 BOOL *aMatched,
15134 ULONG *aMaskedInterfaces)
15135{
15136 NOREF(aDevice);
15137 NOREF(aMatched);
15138 NOREF(aMaskedInterfaces);
15139 ReturnComNotImplemented();
15140
15141}
15142
15143HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15144{
15145 NOREF(aId); NOREF(aCaptureFilename);
15146 ReturnComNotImplemented();
15147}
15148
15149HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15150 BOOL aDone)
15151{
15152 NOREF(aId);
15153 NOREF(aDone);
15154 ReturnComNotImplemented();
15155}
15156
15157HRESULT Machine::autoCaptureUSBDevices()
15158{
15159 ReturnComNotImplemented();
15160}
15161
15162HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15163{
15164 NOREF(aDone);
15165 ReturnComNotImplemented();
15166}
15167
15168HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15169 ComPtr<IProgress> &aProgress)
15170{
15171 NOREF(aSession);
15172 NOREF(aProgress);
15173 ReturnComNotImplemented();
15174}
15175
15176HRESULT Machine::finishOnlineMergeMedium()
15177{
15178 ReturnComNotImplemented();
15179}
15180
15181HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
15182{
15183 RT_NOREF(aParms, aID);
15184 ReturnComNotImplemented();
15185}
15186
15187HRESULT Machine::clipboardAreaUnregister(ULONG aID)
15188{
15189 RT_NOREF(aID);
15190 ReturnComNotImplemented();
15191}
15192
15193HRESULT Machine::clipboardAreaAttach(ULONG aID)
15194{
15195 RT_NOREF(aID);
15196 ReturnComNotImplemented();
15197}
15198HRESULT Machine::clipboardAreaDetach(ULONG aID)
15199{
15200 RT_NOREF(aID);
15201 ReturnComNotImplemented();
15202}
15203
15204HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
15205{
15206 RT_NOREF(aID);
15207 ReturnComNotImplemented();
15208}
15209
15210HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
15211{
15212 RT_NOREF(aID, aRefCount);
15213 ReturnComNotImplemented();
15214}
15215
15216HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15217 std::vector<com::Utf8Str> &aValues,
15218 std::vector<LONG64> &aTimestamps,
15219 std::vector<com::Utf8Str> &aFlags)
15220{
15221 NOREF(aNames);
15222 NOREF(aValues);
15223 NOREF(aTimestamps);
15224 NOREF(aFlags);
15225 ReturnComNotImplemented();
15226}
15227
15228HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15229 const com::Utf8Str &aValue,
15230 LONG64 aTimestamp,
15231 const com::Utf8Str &aFlags)
15232{
15233 NOREF(aName);
15234 NOREF(aValue);
15235 NOREF(aTimestamp);
15236 NOREF(aFlags);
15237 ReturnComNotImplemented();
15238}
15239
15240HRESULT Machine::lockMedia()
15241{
15242 ReturnComNotImplemented();
15243}
15244
15245HRESULT Machine::unlockMedia()
15246{
15247 ReturnComNotImplemented();
15248}
15249
15250HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15251 ComPtr<IMediumAttachment> &aNewAttachment)
15252{
15253 NOREF(aAttachment);
15254 NOREF(aNewAttachment);
15255 ReturnComNotImplemented();
15256}
15257
15258HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15259 ULONG aCpuUser,
15260 ULONG aCpuKernel,
15261 ULONG aCpuIdle,
15262 ULONG aMemTotal,
15263 ULONG aMemFree,
15264 ULONG aMemBalloon,
15265 ULONG aMemShared,
15266 ULONG aMemCache,
15267 ULONG aPagedTotal,
15268 ULONG aMemAllocTotal,
15269 ULONG aMemFreeTotal,
15270 ULONG aMemBalloonTotal,
15271 ULONG aMemSharedTotal,
15272 ULONG aVmNetRx,
15273 ULONG aVmNetTx)
15274{
15275 NOREF(aValidStats);
15276 NOREF(aCpuUser);
15277 NOREF(aCpuKernel);
15278 NOREF(aCpuIdle);
15279 NOREF(aMemTotal);
15280 NOREF(aMemFree);
15281 NOREF(aMemBalloon);
15282 NOREF(aMemShared);
15283 NOREF(aMemCache);
15284 NOREF(aPagedTotal);
15285 NOREF(aMemAllocTotal);
15286 NOREF(aMemFreeTotal);
15287 NOREF(aMemBalloonTotal);
15288 NOREF(aMemSharedTotal);
15289 NOREF(aVmNetRx);
15290 NOREF(aVmNetTx);
15291 ReturnComNotImplemented();
15292}
15293
15294HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15295 com::Utf8Str &aResult)
15296{
15297 NOREF(aAuthParams);
15298 NOREF(aResult);
15299 ReturnComNotImplemented();
15300}
15301
15302com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15303{
15304 com::Utf8Str strControllerName = "Unknown";
15305 switch (aBusType)
15306 {
15307 case StorageBus_IDE:
15308 {
15309 strControllerName = "IDE";
15310 break;
15311 }
15312 case StorageBus_SATA:
15313 {
15314 strControllerName = "SATA";
15315 break;
15316 }
15317 case StorageBus_SCSI:
15318 {
15319 strControllerName = "SCSI";
15320 break;
15321 }
15322 case StorageBus_Floppy:
15323 {
15324 strControllerName = "Floppy";
15325 break;
15326 }
15327 case StorageBus_SAS:
15328 {
15329 strControllerName = "SAS";
15330 break;
15331 }
15332 case StorageBus_USB:
15333 {
15334 strControllerName = "USB";
15335 break;
15336 }
15337 default:
15338 break;
15339 }
15340 return strControllerName;
15341}
15342
15343HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15344{
15345 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15346
15347 AutoCaller autoCaller(this);
15348 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15349
15350 HRESULT rc = S_OK;
15351
15352 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15353 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15354 rc = getUSBDeviceFilters(usbDeviceFilters);
15355 if (FAILED(rc)) return rc;
15356
15357 NOREF(aFlags);
15358 com::Utf8Str osTypeId;
15359 ComObjPtr<GuestOSType> osType = NULL;
15360
15361 /* Get the guest os type as a string from the VB. */
15362 rc = getOSTypeId(osTypeId);
15363 if (FAILED(rc)) return rc;
15364
15365 /* Get the os type obj that coresponds, can be used to get
15366 * the defaults for this guest OS. */
15367 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15368 if (FAILED(rc)) return rc;
15369
15370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15371
15372 /* Let the OS type select 64-bit ness. */
15373 mHWData->mLongMode = osType->i_is64Bit()
15374 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15375
15376 /* Let the OS type enable the X2APIC */
15377 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15378
15379 /* This one covers IOAPICEnabled. */
15380 mBIOSSettings->i_applyDefaults(osType);
15381
15382 /* Initialize default record settings. */
15383 mRecordingSettings->i_applyDefaults();
15384
15385 /* Initialize default BIOS settings here */
15386 /* Hardware virtualization must be ON by default */
15387 mHWData->mAPIC = true;
15388 mHWData->mHWVirtExEnabled = true;
15389
15390 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15391 if (FAILED(rc)) return rc;
15392
15393 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15394 if (FAILED(rc)) return rc;
15395
15396 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15397 if (FAILED(rc)) return rc;
15398
15399 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15400 if (FAILED(rc)) return rc;
15401
15402 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15403 if (FAILED(rc)) return rc;
15404
15405 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15406 if (FAILED(rc)) return rc;
15407
15408 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15409 if (FAILED(rc)) return rc;
15410
15411 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15412 if (FAILED(rc)) return rc;
15413
15414 BOOL mRTCUseUTC;
15415 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15416 if (FAILED(rc)) return rc;
15417
15418 setRTCUseUTC(mRTCUseUTC);
15419 if (FAILED(rc)) return rc;
15420
15421 /* the setter does more than just the assignment, so use it */
15422 ChipsetType_T enmChipsetType;
15423 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15424 if (FAILED(rc)) return rc;
15425
15426 rc = COMSETTER(ChipsetType)(enmChipsetType);
15427 if (FAILED(rc)) return rc;
15428
15429 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15430 if (FAILED(rc)) return rc;
15431
15432 /* Apply network adapters defaults */
15433 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15434 mNetworkAdapters[slot]->i_applyDefaults(osType);
15435
15436 /* Apply serial port defaults */
15437 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15438 mSerialPorts[slot]->i_applyDefaults(osType);
15439
15440 /* Apply parallel port defaults - not OS dependent*/
15441 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15442 mParallelPorts[slot]->i_applyDefaults();
15443
15444 /* Audio stuff. */
15445 AudioControllerType_T audioController;
15446 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15447 if (FAILED(rc)) return rc;
15448
15449 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15450 if (FAILED(rc)) return rc;
15451
15452 AudioCodecType_T audioCodec;
15453 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15454 if (FAILED(rc)) return rc;
15455
15456 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15457 if (FAILED(rc)) return rc;
15458
15459 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15460 if (FAILED(rc)) return rc;
15461
15462 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15463 if (FAILED(rc)) return rc;
15464
15465 /* Storage Controllers */
15466 StorageControllerType_T hdStorageControllerType;
15467 StorageBus_T hdStorageBusType;
15468 StorageControllerType_T dvdStorageControllerType;
15469 StorageBus_T dvdStorageBusType;
15470 BOOL recommendedFloppy;
15471 ComPtr<IStorageController> floppyController;
15472 ComPtr<IStorageController> hdController;
15473 ComPtr<IStorageController> dvdController;
15474 Utf8Str strFloppyName, strDVDName, strHDName;
15475
15476 /* GUI auto generates controller names using bus type. Do the same*/
15477 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15478
15479 /* Floppy recommended? add one. */
15480 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15481 if (FAILED(rc)) return rc;
15482 if (recommendedFloppy)
15483 {
15484 rc = addStorageController(strFloppyName,
15485 StorageBus_Floppy,
15486 floppyController);
15487 if (FAILED(rc)) return rc;
15488 }
15489
15490 /* Setup one DVD storage controller. */
15491 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15492 if (FAILED(rc)) return rc;
15493
15494 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15495 if (FAILED(rc)) return rc;
15496
15497 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15498
15499 rc = addStorageController(strDVDName,
15500 dvdStorageBusType,
15501 dvdController);
15502 if (FAILED(rc)) return rc;
15503
15504 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15505 if (FAILED(rc)) return rc;
15506
15507 /* Setup one HDD storage controller. */
15508 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15509 if (FAILED(rc)) return rc;
15510
15511 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15512 if (FAILED(rc)) return rc;
15513
15514 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15515
15516 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15517 {
15518 rc = addStorageController(strHDName,
15519 hdStorageBusType,
15520 hdController);
15521 if (FAILED(rc)) return rc;
15522
15523 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15524 if (FAILED(rc)) return rc;
15525 }
15526 else
15527 {
15528 /* The HD controller is the same as DVD: */
15529 hdController = dvdController;
15530 }
15531
15532 /* Limit the AHCI port count if it's used because windows has trouble with
15533 * too many ports and other guest (OS X in particular) may take extra long
15534 * boot: */
15535
15536 // pParent = static_cast<Medium*>(aP)
15537 IStorageController *temp = hdController;
15538 ComObjPtr<StorageController> storageController;
15539 storageController = static_cast<StorageController *>(temp);
15540
15541 // tempHDController = aHDController;
15542 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15543 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15544 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15545 storageController->COMSETTER(PortCount)(1);
15546
15547 /* USB stuff */
15548
15549 bool ohciEnabled = false;
15550
15551 ComPtr<IUSBController> usbController;
15552 BOOL recommendedUSB3;
15553 BOOL recommendedUSB;
15554 BOOL usbProxyAvailable;
15555
15556 getUSBProxyAvailable(&usbProxyAvailable);
15557 if (FAILED(rc)) return rc;
15558
15559 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15560 if (FAILED(rc)) return rc;
15561 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15562 if (FAILED(rc)) return rc;
15563
15564 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15565 {
15566#ifdef VBOX_WITH_EXTPACK
15567 /* USB 3.0 is only available if the proper ExtPack is installed. */
15568 ExtPackManager *aManager = mParent->i_getExtPackManager();
15569 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15570 {
15571 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15572 if (FAILED(rc)) return rc;
15573
15574 /* xHci includes OHCI */
15575 ohciEnabled = true;
15576 }
15577#endif
15578 }
15579 if ( !ohciEnabled
15580 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15581 {
15582 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15583 if (FAILED(rc)) return rc;
15584 ohciEnabled = true;
15585
15586#ifdef VBOX_WITH_EXTPACK
15587 /* USB 2.0 is only available if the proper ExtPack is installed.
15588 * Note. Configuring EHCI here and providing messages about
15589 * the missing extpack isn't exactly clean, but it is a
15590 * necessary evil to patch over legacy compatability issues
15591 * introduced by the new distribution model. */
15592 ExtPackManager *manager = mParent->i_getExtPackManager();
15593 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15594 {
15595 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15596 if (FAILED(rc)) return rc;
15597 }
15598#endif
15599 }
15600
15601 /* Set recommended human interface device types: */
15602 BOOL recommendedUSBHID;
15603 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15604 if (FAILED(rc)) return rc;
15605
15606 if (recommendedUSBHID)
15607 {
15608 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15609 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15610 if (!ohciEnabled && !usbDeviceFilters.isNull())
15611 {
15612 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15613 if (FAILED(rc)) return rc;
15614 }
15615 }
15616
15617 BOOL recommendedUSBTablet;
15618 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15619 if (FAILED(rc)) return rc;
15620
15621 if (recommendedUSBTablet)
15622 {
15623 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15624 if (!ohciEnabled && !usbDeviceFilters.isNull())
15625 {
15626 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15627 if (FAILED(rc)) return rc;
15628 }
15629 }
15630 return S_OK;
15631}
15632
15633/* This isn't handled entirely by the wrapper generator yet. */
15634#ifdef VBOX_WITH_XPCOM
15635NS_DECL_CLASSINFO(SessionMachine)
15636NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15637
15638NS_DECL_CLASSINFO(SnapshotMachine)
15639NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15640#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