VirtualBox

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

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

OCI: (bugref:9469) cloud network integration concept (disabled in Config.kmk).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 541.3 KB
Line 
1/* $Id: MachineImpl.cpp 81422 2019-10-21 18:04:10Z 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 maschines 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
5044 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5045 // discard saved state
5046 if (mData->mMachineState == MachineState_Saved)
5047 {
5048 // add the saved state file to the list of files the caller should delete
5049 Assert(!mSSData->strStateFilePath.isEmpty());
5050 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5051
5052 mSSData->strStateFilePath.setNull();
5053
5054 // unconditionally set the machine state to powered off, we now
5055 // know no session has locked the machine
5056 mData->mMachineState = MachineState_PoweredOff;
5057 }
5058
5059 size_t cSnapshots = 0;
5060 if (mData->mFirstSnapshot)
5061 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5062 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5063 // fail now before we start detaching media
5064 return setError(VBOX_E_INVALID_OBJECT_STATE,
5065 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5066 mUserData->s.strName.c_str(), cSnapshots);
5067
5068 // This list collects the medium objects from all medium attachments
5069 // which we will detach from the machine and its snapshots, in a specific
5070 // order which allows for closing all media without getting "media in use"
5071 // errors, simply by going through the list from the front to the back:
5072 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5073 // and must be closed before the parent media from the snapshots, or closing the parents
5074 // will fail because they still have children);
5075 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5076 // the root ("first") snapshot of the machine.
5077 MediaList llMedia;
5078
5079 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5080 && mMediumAttachments->size()
5081 )
5082 {
5083 // we have media attachments: detach them all and add the Medium objects to our list
5084 if (aCleanupMode != CleanupMode_UnregisterOnly)
5085 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5086 else
5087 return setError(VBOX_E_INVALID_OBJECT_STATE,
5088 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5089 mUserData->s.strName.c_str(), mMediumAttachments->size());
5090 }
5091
5092 if (cSnapshots)
5093 {
5094 // add the media from the medium attachments of the snapshots to llMedia
5095 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5096 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5097 // into the children first
5098
5099 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5100 MachineState_T oldState = mData->mMachineState;
5101 mData->mMachineState = MachineState_DeletingSnapshot;
5102
5103 // make a copy of the first snapshot so the refcount does not drop to 0
5104 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5105 // because of the AutoCaller voodoo)
5106 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5107
5108 // GO!
5109 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5110
5111 mData->mMachineState = oldState;
5112 }
5113
5114 if (FAILED(rc))
5115 {
5116 i_rollbackMedia();
5117 return rc;
5118 }
5119
5120 // commit all the media changes made above
5121 i_commitMedia();
5122
5123 mData->mRegistered = false;
5124
5125 // machine lock no longer needed
5126 alock.release();
5127
5128 // return media to caller
5129 aMedia.resize(llMedia.size());
5130 size_t i = 0;
5131 for (MediaList::const_iterator
5132 it = llMedia.begin();
5133 it != llMedia.end();
5134 ++it, ++i)
5135 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5136
5137 mParent->i_unregisterMachine(this, id);
5138 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5139
5140 return S_OK;
5141}
5142
5143/**
5144 * Task record for deleting a machine config.
5145 */
5146class Machine::DeleteConfigTask
5147 : public Machine::Task
5148{
5149public:
5150 DeleteConfigTask(Machine *m,
5151 Progress *p,
5152 const Utf8Str &t,
5153 const RTCList<ComPtr<IMedium> > &llMediums,
5154 const StringsList &llFilesToDelete)
5155 : Task(m, p, t),
5156 m_llMediums(llMediums),
5157 m_llFilesToDelete(llFilesToDelete)
5158 {}
5159
5160private:
5161 void handler()
5162 {
5163 try
5164 {
5165 m_pMachine->i_deleteConfigHandler(*this);
5166 }
5167 catch (...)
5168 {
5169 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5170 }
5171 }
5172
5173 RTCList<ComPtr<IMedium> > m_llMediums;
5174 StringsList m_llFilesToDelete;
5175
5176 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5177};
5178
5179/**
5180 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5181 * SessionMachine::taskHandler().
5182 *
5183 * @note Locks this object for writing.
5184 *
5185 * @param task
5186 * @return
5187 */
5188void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5189{
5190 LogFlowThisFuncEnter();
5191
5192 AutoCaller autoCaller(this);
5193 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5194 if (FAILED(autoCaller.rc()))
5195 {
5196 /* we might have been uninitialized because the session was accidentally
5197 * closed by the client, so don't assert */
5198 HRESULT rc = setError(E_FAIL,
5199 tr("The session has been accidentally closed"));
5200 task.m_pProgress->i_notifyComplete(rc);
5201 LogFlowThisFuncLeave();
5202 return;
5203 }
5204
5205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5206
5207 HRESULT rc = S_OK;
5208
5209 try
5210 {
5211 ULONG uLogHistoryCount = 3;
5212 ComPtr<ISystemProperties> systemProperties;
5213 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5214 if (FAILED(rc)) throw rc;
5215
5216 if (!systemProperties.isNull())
5217 {
5218 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5219 if (FAILED(rc)) throw rc;
5220 }
5221
5222 MachineState_T oldState = mData->mMachineState;
5223 i_setMachineState(MachineState_SettingUp);
5224 alock.release();
5225 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5226 {
5227 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5228 {
5229 AutoCaller mac(pMedium);
5230 if (FAILED(mac.rc())) throw mac.rc();
5231 Utf8Str strLocation = pMedium->i_getLocationFull();
5232 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5233 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5234 if (FAILED(rc)) throw rc;
5235 }
5236 if (pMedium->i_isMediumFormatFile())
5237 {
5238 ComPtr<IProgress> pProgress2;
5239 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5240 if (FAILED(rc)) throw rc;
5241 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5242 if (FAILED(rc)) throw rc;
5243 }
5244
5245 /* Close the medium, deliberately without checking the return
5246 * code, and without leaving any trace in the error info, as
5247 * a failure here is a very minor issue, which shouldn't happen
5248 * as above we even managed to delete the medium. */
5249 {
5250 ErrorInfoKeeper eik;
5251 pMedium->Close();
5252 }
5253 }
5254 i_setMachineState(oldState);
5255 alock.acquire();
5256
5257 // delete the files pushed on the task list by Machine::Delete()
5258 // (this includes saved states of the machine and snapshots and
5259 // medium storage files from the IMedium list passed in, and the
5260 // machine XML file)
5261 for (StringsList::const_iterator
5262 it = task.m_llFilesToDelete.begin();
5263 it != task.m_llFilesToDelete.end();
5264 ++it)
5265 {
5266 const Utf8Str &strFile = *it;
5267 LogFunc(("Deleting file %s\n", strFile.c_str()));
5268 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5269 if (FAILED(rc)) throw rc;
5270
5271 int vrc = RTFileDelete(strFile.c_str());
5272 if (RT_FAILURE(vrc))
5273 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5274 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5275 }
5276
5277 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5278 if (FAILED(rc)) throw rc;
5279
5280 /* delete the settings only when the file actually exists */
5281 if (mData->pMachineConfigFile->fileExists())
5282 {
5283 /* Delete any backup or uncommitted XML files. Ignore failures.
5284 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5285 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5286 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5287 RTFileDelete(otherXml.c_str());
5288 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5289 RTFileDelete(otherXml.c_str());
5290
5291 /* delete the Logs folder, nothing important should be left
5292 * there (we don't check for errors because the user might have
5293 * some private files there that we don't want to delete) */
5294 Utf8Str logFolder;
5295 getLogFolder(logFolder);
5296 Assert(logFolder.length());
5297 if (RTDirExists(logFolder.c_str()))
5298 {
5299 /* Delete all VBox.log[.N] files from the Logs folder
5300 * (this must be in sync with the rotation logic in
5301 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5302 * files that may have been created by the GUI. */
5303 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5304 logFolder.c_str(), RTPATH_DELIMITER);
5305 RTFileDelete(log.c_str());
5306 log = Utf8StrFmt("%s%cVBox.png",
5307 logFolder.c_str(), RTPATH_DELIMITER);
5308 RTFileDelete(log.c_str());
5309 for (int i = uLogHistoryCount; i > 0; i--)
5310 {
5311 log = Utf8StrFmt("%s%cVBox.log.%d",
5312 logFolder.c_str(), RTPATH_DELIMITER, i);
5313 RTFileDelete(log.c_str());
5314 log = Utf8StrFmt("%s%cVBox.png.%d",
5315 logFolder.c_str(), RTPATH_DELIMITER, i);
5316 RTFileDelete(log.c_str());
5317 }
5318#if defined(RT_OS_WINDOWS)
5319 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5320 RTFileDelete(log.c_str());
5321 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5322 RTFileDelete(log.c_str());
5323#endif
5324
5325 RTDirRemove(logFolder.c_str());
5326 }
5327
5328 /* delete the Snapshots folder, nothing important should be left
5329 * there (we don't check for errors because the user might have
5330 * some private files there that we don't want to delete) */
5331 Utf8Str strFullSnapshotFolder;
5332 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5333 Assert(!strFullSnapshotFolder.isEmpty());
5334 if (RTDirExists(strFullSnapshotFolder.c_str()))
5335 RTDirRemove(strFullSnapshotFolder.c_str());
5336
5337 // delete the directory that contains the settings file, but only
5338 // if it matches the VM name
5339 Utf8Str settingsDir;
5340 if (i_isInOwnDir(&settingsDir))
5341 RTDirRemove(settingsDir.c_str());
5342 }
5343
5344 alock.release();
5345
5346 mParent->i_saveModifiedRegistries();
5347 }
5348 catch (HRESULT aRC) { rc = aRC; }
5349
5350 task.m_pProgress->i_notifyComplete(rc);
5351
5352 LogFlowThisFuncLeave();
5353}
5354
5355HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5356{
5357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5358
5359 HRESULT rc = i_checkStateDependency(MutableStateDep);
5360 if (FAILED(rc)) return rc;
5361
5362 if (mData->mRegistered)
5363 return setError(VBOX_E_INVALID_VM_STATE,
5364 tr("Cannot delete settings of a registered machine"));
5365
5366 // collect files to delete
5367 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5368 if (mData->pMachineConfigFile->fileExists())
5369 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5370
5371 RTCList<ComPtr<IMedium> > llMediums;
5372 for (size_t i = 0; i < aMedia.size(); ++i)
5373 {
5374 IMedium *pIMedium(aMedia[i]);
5375 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5376 if (pMedium.isNull())
5377 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5378 SafeArray<BSTR> ids;
5379 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5380 if (FAILED(rc)) return rc;
5381 /* At this point the medium should not have any back references
5382 * anymore. If it has it is attached to another VM and *must* not
5383 * deleted. */
5384 if (ids.size() < 1)
5385 llMediums.append(pMedium);
5386 }
5387
5388 ComObjPtr<Progress> pProgress;
5389 pProgress.createObject();
5390 rc = pProgress->init(i_getVirtualBox(),
5391 static_cast<IMachine*>(this) /* aInitiator */,
5392 tr("Deleting files"),
5393 true /* fCancellable */,
5394 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5395 tr("Collecting file inventory"));
5396 if (FAILED(rc))
5397 return rc;
5398
5399 /* create and start the task on a separate thread (note that it will not
5400 * start working until we release alock) */
5401 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5402 rc = pTask->createThread();
5403 pTask = NULL;
5404 if (FAILED(rc))
5405 return rc;
5406
5407 pProgress.queryInterfaceTo(aProgress.asOutParam());
5408
5409 LogFlowFuncLeave();
5410
5411 return S_OK;
5412}
5413
5414HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5415{
5416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5417
5418 ComObjPtr<Snapshot> pSnapshot;
5419 HRESULT rc;
5420
5421 if (aNameOrId.isEmpty())
5422 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5423 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5424 else
5425 {
5426 Guid uuid(aNameOrId);
5427 if (uuid.isValid())
5428 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5429 else
5430 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5431 }
5432 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5433
5434 return rc;
5435}
5436
5437HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5438 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5439{
5440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5441
5442 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5443 if (FAILED(rc)) return rc;
5444
5445 ComObjPtr<SharedFolder> sharedFolder;
5446 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5447 if (SUCCEEDED(rc))
5448 return setError(VBOX_E_OBJECT_IN_USE,
5449 tr("Shared folder named '%s' already exists"),
5450 aName.c_str());
5451
5452 sharedFolder.createObject();
5453 rc = sharedFolder->init(i_getMachine(),
5454 aName,
5455 aHostPath,
5456 !!aWritable,
5457 !!aAutomount,
5458 aAutoMountPoint,
5459 true /* fFailOnError */);
5460 if (FAILED(rc)) return rc;
5461
5462 i_setModified(IsModified_SharedFolders);
5463 mHWData.backup();
5464 mHWData->mSharedFolders.push_back(sharedFolder);
5465
5466 /* inform the direct session if any */
5467 alock.release();
5468 i_onSharedFolderChange();
5469
5470 return S_OK;
5471}
5472
5473HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5474{
5475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5476
5477 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5478 if (FAILED(rc)) return rc;
5479
5480 ComObjPtr<SharedFolder> sharedFolder;
5481 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5482 if (FAILED(rc)) return rc;
5483
5484 i_setModified(IsModified_SharedFolders);
5485 mHWData.backup();
5486 mHWData->mSharedFolders.remove(sharedFolder);
5487
5488 /* inform the direct session if any */
5489 alock.release();
5490 i_onSharedFolderChange();
5491
5492 return S_OK;
5493}
5494
5495HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5496{
5497 /* start with No */
5498 *aCanShow = FALSE;
5499
5500 ComPtr<IInternalSessionControl> directControl;
5501 {
5502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5503
5504 if (mData->mSession.mState != SessionState_Locked)
5505 return setError(VBOX_E_INVALID_VM_STATE,
5506 tr("Machine is not locked for session (session state: %s)"),
5507 Global::stringifySessionState(mData->mSession.mState));
5508
5509 if (mData->mSession.mLockType == LockType_VM)
5510 directControl = mData->mSession.mDirectControl;
5511 }
5512
5513 /* ignore calls made after #OnSessionEnd() is called */
5514 if (!directControl)
5515 return S_OK;
5516
5517 LONG64 dummy;
5518 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5519}
5520
5521HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5522{
5523 ComPtr<IInternalSessionControl> directControl;
5524 {
5525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5526
5527 if (mData->mSession.mState != SessionState_Locked)
5528 return setError(E_FAIL,
5529 tr("Machine is not locked for session (session state: %s)"),
5530 Global::stringifySessionState(mData->mSession.mState));
5531
5532 if (mData->mSession.mLockType == LockType_VM)
5533 directControl = mData->mSession.mDirectControl;
5534 }
5535
5536 /* ignore calls made after #OnSessionEnd() is called */
5537 if (!directControl)
5538 return S_OK;
5539
5540 BOOL dummy;
5541 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5542}
5543
5544#ifdef VBOX_WITH_GUEST_PROPS
5545/**
5546 * Look up a guest property in VBoxSVC's internal structures.
5547 */
5548HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5549 com::Utf8Str &aValue,
5550 LONG64 *aTimestamp,
5551 com::Utf8Str &aFlags) const
5552{
5553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5554
5555 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5556 if (it != mHWData->mGuestProperties.end())
5557 {
5558 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5559 aValue = it->second.strValue;
5560 *aTimestamp = it->second.mTimestamp;
5561 GuestPropWriteFlags(it->second.mFlags, szFlags);
5562 aFlags = Utf8Str(szFlags);
5563 }
5564
5565 return S_OK;
5566}
5567
5568/**
5569 * Query the VM that a guest property belongs to for the property.
5570 * @returns E_ACCESSDENIED if the VM process is not available or not
5571 * currently handling queries and the lookup should then be done in
5572 * VBoxSVC.
5573 */
5574HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5575 com::Utf8Str &aValue,
5576 LONG64 *aTimestamp,
5577 com::Utf8Str &aFlags) const
5578{
5579 HRESULT rc = S_OK;
5580 Bstr bstrValue;
5581 Bstr bstrFlags;
5582
5583 ComPtr<IInternalSessionControl> directControl;
5584 {
5585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5586 if (mData->mSession.mLockType == LockType_VM)
5587 directControl = mData->mSession.mDirectControl;
5588 }
5589
5590 /* ignore calls made after #OnSessionEnd() is called */
5591 if (!directControl)
5592 rc = E_ACCESSDENIED;
5593 else
5594 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5595 0 /* accessMode */,
5596 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5597
5598 aValue = bstrValue;
5599 aFlags = bstrFlags;
5600
5601 return rc;
5602}
5603#endif // VBOX_WITH_GUEST_PROPS
5604
5605HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5606 com::Utf8Str &aValue,
5607 LONG64 *aTimestamp,
5608 com::Utf8Str &aFlags)
5609{
5610#ifndef VBOX_WITH_GUEST_PROPS
5611 ReturnComNotImplemented();
5612#else // VBOX_WITH_GUEST_PROPS
5613
5614 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5615
5616 if (rc == E_ACCESSDENIED)
5617 /* The VM is not running or the service is not (yet) accessible */
5618 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5619 return rc;
5620#endif // VBOX_WITH_GUEST_PROPS
5621}
5622
5623HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5624{
5625 LONG64 dummyTimestamp;
5626 com::Utf8Str dummyFlags;
5627 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5628 return rc;
5629
5630}
5631HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5632{
5633 com::Utf8Str dummyFlags;
5634 com::Utf8Str dummyValue;
5635 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5636 return rc;
5637}
5638
5639#ifdef VBOX_WITH_GUEST_PROPS
5640/**
5641 * Set a guest property in VBoxSVC's internal structures.
5642 */
5643HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5644 const com::Utf8Str &aFlags, bool fDelete)
5645{
5646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5647 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5648 if (FAILED(rc)) return rc;
5649
5650 try
5651 {
5652 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5653 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5654 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5655
5656 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5657 if (it == mHWData->mGuestProperties.end())
5658 {
5659 if (!fDelete)
5660 {
5661 i_setModified(IsModified_MachineData);
5662 mHWData.backupEx();
5663
5664 RTTIMESPEC time;
5665 HWData::GuestProperty prop;
5666 prop.strValue = Bstr(aValue).raw();
5667 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5668 prop.mFlags = fFlags;
5669 mHWData->mGuestProperties[aName] = prop;
5670 }
5671 }
5672 else
5673 {
5674 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5675 {
5676 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5677 }
5678 else
5679 {
5680 i_setModified(IsModified_MachineData);
5681 mHWData.backupEx();
5682
5683 /* The backupEx() operation invalidates our iterator,
5684 * so get a new one. */
5685 it = mHWData->mGuestProperties.find(aName);
5686 Assert(it != mHWData->mGuestProperties.end());
5687
5688 if (!fDelete)
5689 {
5690 RTTIMESPEC time;
5691 it->second.strValue = aValue;
5692 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5693 it->second.mFlags = fFlags;
5694 }
5695 else
5696 mHWData->mGuestProperties.erase(it);
5697 }
5698 }
5699
5700 if (SUCCEEDED(rc))
5701 {
5702 alock.release();
5703
5704 mParent->i_onGuestPropertyChange(mData->mUuid,
5705 Bstr(aName).raw(),
5706 Bstr(aValue).raw(),
5707 Bstr(aFlags).raw());
5708 }
5709 }
5710 catch (std::bad_alloc &)
5711 {
5712 rc = E_OUTOFMEMORY;
5713 }
5714
5715 return rc;
5716}
5717
5718/**
5719 * Set a property on the VM that that property belongs to.
5720 * @returns E_ACCESSDENIED if the VM process is not available or not
5721 * currently handling queries and the setting should then be done in
5722 * VBoxSVC.
5723 */
5724HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5725 const com::Utf8Str &aFlags, bool fDelete)
5726{
5727 HRESULT rc;
5728
5729 try
5730 {
5731 ComPtr<IInternalSessionControl> directControl;
5732 {
5733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5734 if (mData->mSession.mLockType == LockType_VM)
5735 directControl = mData->mSession.mDirectControl;
5736 }
5737
5738 Bstr dummy1; /* will not be changed (setter) */
5739 Bstr dummy2; /* will not be changed (setter) */
5740 LONG64 dummy64;
5741 if (!directControl)
5742 rc = E_ACCESSDENIED;
5743 else
5744 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5745 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5746 fDelete ? 2 : 1 /* accessMode */,
5747 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5748 }
5749 catch (std::bad_alloc &)
5750 {
5751 rc = E_OUTOFMEMORY;
5752 }
5753
5754 return rc;
5755}
5756#endif // VBOX_WITH_GUEST_PROPS
5757
5758HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5759 const com::Utf8Str &aFlags)
5760{
5761#ifndef VBOX_WITH_GUEST_PROPS
5762 ReturnComNotImplemented();
5763#else // VBOX_WITH_GUEST_PROPS
5764 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5765 if (rc == E_ACCESSDENIED)
5766 /* The VM is not running or the service is not (yet) accessible */
5767 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5768 return rc;
5769#endif // VBOX_WITH_GUEST_PROPS
5770}
5771
5772HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5773{
5774 return setGuestProperty(aProperty, aValue, "");
5775}
5776
5777HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5778{
5779#ifndef VBOX_WITH_GUEST_PROPS
5780 ReturnComNotImplemented();
5781#else // VBOX_WITH_GUEST_PROPS
5782 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5783 if (rc == E_ACCESSDENIED)
5784 /* The VM is not running or the service is not (yet) accessible */
5785 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5786 return rc;
5787#endif // VBOX_WITH_GUEST_PROPS
5788}
5789
5790#ifdef VBOX_WITH_GUEST_PROPS
5791/**
5792 * Enumerate the guest properties in VBoxSVC's internal structures.
5793 */
5794HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5795 std::vector<com::Utf8Str> &aNames,
5796 std::vector<com::Utf8Str> &aValues,
5797 std::vector<LONG64> &aTimestamps,
5798 std::vector<com::Utf8Str> &aFlags)
5799{
5800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5801 Utf8Str strPatterns(aPatterns);
5802
5803 /*
5804 * Look for matching patterns and build up a list.
5805 */
5806 HWData::GuestPropertyMap propMap;
5807 for (HWData::GuestPropertyMap::const_iterator
5808 it = mHWData->mGuestProperties.begin();
5809 it != mHWData->mGuestProperties.end();
5810 ++it)
5811 {
5812 if ( strPatterns.isEmpty()
5813 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5814 RTSTR_MAX,
5815 it->first.c_str(),
5816 RTSTR_MAX,
5817 NULL)
5818 )
5819 propMap.insert(*it);
5820 }
5821
5822 alock.release();
5823
5824 /*
5825 * And build up the arrays for returning the property information.
5826 */
5827 size_t cEntries = propMap.size();
5828
5829 aNames.resize(cEntries);
5830 aValues.resize(cEntries);
5831 aTimestamps.resize(cEntries);
5832 aFlags.resize(cEntries);
5833
5834 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5835 size_t i = 0;
5836 for (HWData::GuestPropertyMap::const_iterator
5837 it = propMap.begin();
5838 it != propMap.end();
5839 ++it, ++i)
5840 {
5841 aNames[i] = it->first;
5842 aValues[i] = it->second.strValue;
5843 aTimestamps[i] = it->second.mTimestamp;
5844 GuestPropWriteFlags(it->second.mFlags, szFlags);
5845 aFlags[i] = Utf8Str(szFlags);
5846 }
5847
5848 return S_OK;
5849}
5850
5851/**
5852 * Enumerate the properties managed by a VM.
5853 * @returns E_ACCESSDENIED if the VM process is not available or not
5854 * currently handling queries and the setting should then be done in
5855 * VBoxSVC.
5856 */
5857HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5858 std::vector<com::Utf8Str> &aNames,
5859 std::vector<com::Utf8Str> &aValues,
5860 std::vector<LONG64> &aTimestamps,
5861 std::vector<com::Utf8Str> &aFlags)
5862{
5863 HRESULT rc;
5864 ComPtr<IInternalSessionControl> directControl;
5865 {
5866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5867 if (mData->mSession.mLockType == LockType_VM)
5868 directControl = mData->mSession.mDirectControl;
5869 }
5870
5871 com::SafeArray<BSTR> bNames;
5872 com::SafeArray<BSTR> bValues;
5873 com::SafeArray<LONG64> bTimestamps;
5874 com::SafeArray<BSTR> bFlags;
5875
5876 if (!directControl)
5877 rc = E_ACCESSDENIED;
5878 else
5879 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5880 ComSafeArrayAsOutParam(bNames),
5881 ComSafeArrayAsOutParam(bValues),
5882 ComSafeArrayAsOutParam(bTimestamps),
5883 ComSafeArrayAsOutParam(bFlags));
5884 size_t i;
5885 aNames.resize(bNames.size());
5886 for (i = 0; i < bNames.size(); ++i)
5887 aNames[i] = Utf8Str(bNames[i]);
5888 aValues.resize(bValues.size());
5889 for (i = 0; i < bValues.size(); ++i)
5890 aValues[i] = Utf8Str(bValues[i]);
5891 aTimestamps.resize(bTimestamps.size());
5892 for (i = 0; i < bTimestamps.size(); ++i)
5893 aTimestamps[i] = bTimestamps[i];
5894 aFlags.resize(bFlags.size());
5895 for (i = 0; i < bFlags.size(); ++i)
5896 aFlags[i] = Utf8Str(bFlags[i]);
5897
5898 return rc;
5899}
5900#endif // VBOX_WITH_GUEST_PROPS
5901HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5902 std::vector<com::Utf8Str> &aNames,
5903 std::vector<com::Utf8Str> &aValues,
5904 std::vector<LONG64> &aTimestamps,
5905 std::vector<com::Utf8Str> &aFlags)
5906{
5907#ifndef VBOX_WITH_GUEST_PROPS
5908 ReturnComNotImplemented();
5909#else // VBOX_WITH_GUEST_PROPS
5910
5911 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5912
5913 if (rc == E_ACCESSDENIED)
5914 /* The VM is not running or the service is not (yet) accessible */
5915 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5916 return rc;
5917#endif // VBOX_WITH_GUEST_PROPS
5918}
5919
5920HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5921 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5922{
5923 MediumAttachmentList atts;
5924
5925 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5926 if (FAILED(rc)) return rc;
5927
5928 aMediumAttachments.resize(atts.size());
5929 size_t i = 0;
5930 for (MediumAttachmentList::const_iterator
5931 it = atts.begin();
5932 it != atts.end();
5933 ++it, ++i)
5934 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5935
5936 return S_OK;
5937}
5938
5939HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5940 LONG aControllerPort,
5941 LONG aDevice,
5942 ComPtr<IMediumAttachment> &aAttachment)
5943{
5944 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5945 aName.c_str(), aControllerPort, aDevice));
5946
5947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5948
5949 aAttachment = NULL;
5950
5951 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5952 aName,
5953 aControllerPort,
5954 aDevice);
5955 if (pAttach.isNull())
5956 return setError(VBOX_E_OBJECT_NOT_FOUND,
5957 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5958 aDevice, aControllerPort, aName.c_str());
5959
5960 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5961
5962 return S_OK;
5963}
5964
5965
5966HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5967 StorageBus_T aConnectionType,
5968 ComPtr<IStorageController> &aController)
5969{
5970 if ( (aConnectionType <= StorageBus_Null)
5971 || (aConnectionType > StorageBus_VirtioSCSI))
5972 return setError(E_INVALIDARG,
5973 tr("Invalid connection type: %d"),
5974 aConnectionType);
5975
5976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5977
5978 HRESULT rc = i_checkStateDependency(MutableStateDep);
5979 if (FAILED(rc)) return rc;
5980
5981 /* try to find one with the name first. */
5982 ComObjPtr<StorageController> ctrl;
5983
5984 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5985 if (SUCCEEDED(rc))
5986 return setError(VBOX_E_OBJECT_IN_USE,
5987 tr("Storage controller named '%s' already exists"),
5988 aName.c_str());
5989
5990 ctrl.createObject();
5991
5992 /* get a new instance number for the storage controller */
5993 ULONG ulInstance = 0;
5994 bool fBootable = true;
5995 for (StorageControllerList::const_iterator
5996 it = mStorageControllers->begin();
5997 it != mStorageControllers->end();
5998 ++it)
5999 {
6000 if ((*it)->i_getStorageBus() == aConnectionType)
6001 {
6002 ULONG ulCurInst = (*it)->i_getInstance();
6003
6004 if (ulCurInst >= ulInstance)
6005 ulInstance = ulCurInst + 1;
6006
6007 /* Only one controller of each type can be marked as bootable. */
6008 if ((*it)->i_getBootable())
6009 fBootable = false;
6010 }
6011 }
6012
6013 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6014 if (FAILED(rc)) return rc;
6015
6016 i_setModified(IsModified_Storage);
6017 mStorageControllers.backup();
6018 mStorageControllers->push_back(ctrl);
6019
6020 ctrl.queryInterfaceTo(aController.asOutParam());
6021
6022 /* inform the direct session if any */
6023 alock.release();
6024 i_onStorageControllerChange(i_getId(), aName);
6025
6026 return S_OK;
6027}
6028
6029HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6030 ComPtr<IStorageController> &aStorageController)
6031{
6032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6033
6034 ComObjPtr<StorageController> ctrl;
6035
6036 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6037 if (SUCCEEDED(rc))
6038 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6039
6040 return rc;
6041}
6042
6043HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6044 ULONG aInstance,
6045 ComPtr<IStorageController> &aStorageController)
6046{
6047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6048
6049 for (StorageControllerList::const_iterator
6050 it = mStorageControllers->begin();
6051 it != mStorageControllers->end();
6052 ++it)
6053 {
6054 if ( (*it)->i_getStorageBus() == aConnectionType
6055 && (*it)->i_getInstance() == aInstance)
6056 {
6057 (*it).queryInterfaceTo(aStorageController.asOutParam());
6058 return S_OK;
6059 }
6060 }
6061
6062 return setError(VBOX_E_OBJECT_NOT_FOUND,
6063 tr("Could not find a storage controller with instance number '%lu'"),
6064 aInstance);
6065}
6066
6067HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6068{
6069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6070
6071 HRESULT rc = i_checkStateDependency(MutableStateDep);
6072 if (FAILED(rc)) return rc;
6073
6074 ComObjPtr<StorageController> ctrl;
6075
6076 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6077 if (SUCCEEDED(rc))
6078 {
6079 /* Ensure that only one controller of each type is marked as bootable. */
6080 if (aBootable == TRUE)
6081 {
6082 for (StorageControllerList::const_iterator
6083 it = mStorageControllers->begin();
6084 it != mStorageControllers->end();
6085 ++it)
6086 {
6087 ComObjPtr<StorageController> aCtrl = (*it);
6088
6089 if ( (aCtrl->i_getName() != aName)
6090 && aCtrl->i_getBootable() == TRUE
6091 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6092 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6093 {
6094 aCtrl->i_setBootable(FALSE);
6095 break;
6096 }
6097 }
6098 }
6099
6100 if (SUCCEEDED(rc))
6101 {
6102 ctrl->i_setBootable(aBootable);
6103 i_setModified(IsModified_Storage);
6104 }
6105 }
6106
6107 if (SUCCEEDED(rc))
6108 {
6109 /* inform the direct session if any */
6110 alock.release();
6111 i_onStorageControllerChange(i_getId(), aName);
6112 }
6113
6114 return rc;
6115}
6116
6117HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6118{
6119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6120
6121 HRESULT rc = i_checkStateDependency(MutableStateDep);
6122 if (FAILED(rc)) return rc;
6123
6124 ComObjPtr<StorageController> ctrl;
6125 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6126 if (FAILED(rc)) return rc;
6127
6128 MediumAttachmentList llDetachedAttachments;
6129 {
6130 /* find all attached devices to the appropriate storage controller and detach them all */
6131 // make a temporary list because detachDevice invalidates iterators into
6132 // mMediumAttachments
6133 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6134
6135 for (MediumAttachmentList::const_iterator
6136 it = llAttachments2.begin();
6137 it != llAttachments2.end();
6138 ++it)
6139 {
6140 MediumAttachment *pAttachTemp = *it;
6141
6142 AutoCaller localAutoCaller(pAttachTemp);
6143 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6144
6145 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6146
6147 if (pAttachTemp->i_getControllerName() == aName)
6148 {
6149 llDetachedAttachments.push_back(pAttachTemp);
6150 rc = i_detachDevice(pAttachTemp, alock, NULL);
6151 if (FAILED(rc)) return rc;
6152 }
6153 }
6154 }
6155
6156 /* send event about detached devices before removing parent controller */
6157 for (MediumAttachmentList::const_iterator
6158 it = llDetachedAttachments.begin();
6159 it != llDetachedAttachments.end();
6160 ++it)
6161 {
6162 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6163 }
6164
6165 /* We can remove it now. */
6166 i_setModified(IsModified_Storage);
6167 mStorageControllers.backup();
6168
6169 ctrl->i_unshare();
6170
6171 mStorageControllers->remove(ctrl);
6172
6173 /* inform the direct session if any */
6174 alock.release();
6175 i_onStorageControllerChange(i_getId(), aName);
6176
6177 return S_OK;
6178}
6179
6180HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6181 ComPtr<IUSBController> &aController)
6182{
6183 if ( (aType <= USBControllerType_Null)
6184 || (aType >= USBControllerType_Last))
6185 return setError(E_INVALIDARG,
6186 tr("Invalid USB controller type: %d"),
6187 aType);
6188
6189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6190
6191 HRESULT rc = i_checkStateDependency(MutableStateDep);
6192 if (FAILED(rc)) return rc;
6193
6194 /* try to find one with the same type first. */
6195 ComObjPtr<USBController> ctrl;
6196
6197 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6198 if (SUCCEEDED(rc))
6199 return setError(VBOX_E_OBJECT_IN_USE,
6200 tr("USB controller named '%s' already exists"),
6201 aName.c_str());
6202
6203 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6204 ULONG maxInstances;
6205 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6206 if (FAILED(rc))
6207 return rc;
6208
6209 ULONG cInstances = i_getUSBControllerCountByType(aType);
6210 if (cInstances >= maxInstances)
6211 return setError(E_INVALIDARG,
6212 tr("Too many USB controllers of this type"));
6213
6214 ctrl.createObject();
6215
6216 rc = ctrl->init(this, aName, aType);
6217 if (FAILED(rc)) return rc;
6218
6219 i_setModified(IsModified_USB);
6220 mUSBControllers.backup();
6221 mUSBControllers->push_back(ctrl);
6222
6223 ctrl.queryInterfaceTo(aController.asOutParam());
6224
6225 /* inform the direct session if any */
6226 alock.release();
6227 i_onUSBControllerChange();
6228
6229 return S_OK;
6230}
6231
6232HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6233{
6234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6235
6236 ComObjPtr<USBController> ctrl;
6237
6238 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6239 if (SUCCEEDED(rc))
6240 ctrl.queryInterfaceTo(aController.asOutParam());
6241
6242 return rc;
6243}
6244
6245HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6246 ULONG *aControllers)
6247{
6248 if ( (aType <= USBControllerType_Null)
6249 || (aType >= USBControllerType_Last))
6250 return setError(E_INVALIDARG,
6251 tr("Invalid USB controller type: %d"),
6252 aType);
6253
6254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6255
6256 ComObjPtr<USBController> ctrl;
6257
6258 *aControllers = i_getUSBControllerCountByType(aType);
6259
6260 return S_OK;
6261}
6262
6263HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6264{
6265
6266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6267
6268 HRESULT rc = i_checkStateDependency(MutableStateDep);
6269 if (FAILED(rc)) return rc;
6270
6271 ComObjPtr<USBController> ctrl;
6272 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6273 if (FAILED(rc)) return rc;
6274
6275 i_setModified(IsModified_USB);
6276 mUSBControllers.backup();
6277
6278 ctrl->i_unshare();
6279
6280 mUSBControllers->remove(ctrl);
6281
6282 /* inform the direct session if any */
6283 alock.release();
6284 i_onUSBControllerChange();
6285
6286 return S_OK;
6287}
6288
6289HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6290 ULONG *aOriginX,
6291 ULONG *aOriginY,
6292 ULONG *aWidth,
6293 ULONG *aHeight,
6294 BOOL *aEnabled)
6295{
6296 uint32_t u32OriginX= 0;
6297 uint32_t u32OriginY= 0;
6298 uint32_t u32Width = 0;
6299 uint32_t u32Height = 0;
6300 uint16_t u16Flags = 0;
6301
6302 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6303 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6304 if (RT_FAILURE(vrc))
6305 {
6306#ifdef RT_OS_WINDOWS
6307 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6308 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6309 * So just assign fEnable to TRUE again.
6310 * The right fix would be to change GUI API wrappers to make sure that parameters
6311 * are changed only if API succeeds.
6312 */
6313 *aEnabled = TRUE;
6314#endif
6315 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6316 tr("Saved guest size is not available (%Rrc)"),
6317 vrc);
6318 }
6319
6320 *aOriginX = u32OriginX;
6321 *aOriginY = u32OriginY;
6322 *aWidth = u32Width;
6323 *aHeight = u32Height;
6324 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6325
6326 return S_OK;
6327}
6328
6329HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6330 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6331{
6332 if (aScreenId != 0)
6333 return E_NOTIMPL;
6334
6335 if ( aBitmapFormat != BitmapFormat_BGR0
6336 && aBitmapFormat != BitmapFormat_BGRA
6337 && aBitmapFormat != BitmapFormat_RGBA
6338 && aBitmapFormat != BitmapFormat_PNG)
6339 return setError(E_NOTIMPL,
6340 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6341
6342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6343
6344 uint8_t *pu8Data = NULL;
6345 uint32_t cbData = 0;
6346 uint32_t u32Width = 0;
6347 uint32_t u32Height = 0;
6348
6349 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6350
6351 if (RT_FAILURE(vrc))
6352 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6353 tr("Saved thumbnail data is not available (%Rrc)"),
6354 vrc);
6355
6356 HRESULT hr = S_OK;
6357
6358 *aWidth = u32Width;
6359 *aHeight = u32Height;
6360
6361 if (cbData > 0)
6362 {
6363 /* Convert pixels to the format expected by the API caller. */
6364 if (aBitmapFormat == BitmapFormat_BGR0)
6365 {
6366 /* [0] B, [1] G, [2] R, [3] 0. */
6367 aData.resize(cbData);
6368 memcpy(&aData.front(), pu8Data, cbData);
6369 }
6370 else if (aBitmapFormat == BitmapFormat_BGRA)
6371 {
6372 /* [0] B, [1] G, [2] R, [3] A. */
6373 aData.resize(cbData);
6374 for (uint32_t i = 0; i < cbData; i += 4)
6375 {
6376 aData[i] = pu8Data[i];
6377 aData[i + 1] = pu8Data[i + 1];
6378 aData[i + 2] = pu8Data[i + 2];
6379 aData[i + 3] = 0xff;
6380 }
6381 }
6382 else if (aBitmapFormat == BitmapFormat_RGBA)
6383 {
6384 /* [0] R, [1] G, [2] B, [3] A. */
6385 aData.resize(cbData);
6386 for (uint32_t i = 0; i < cbData; i += 4)
6387 {
6388 aData[i] = pu8Data[i + 2];
6389 aData[i + 1] = pu8Data[i + 1];
6390 aData[i + 2] = pu8Data[i];
6391 aData[i + 3] = 0xff;
6392 }
6393 }
6394 else if (aBitmapFormat == BitmapFormat_PNG)
6395 {
6396 uint8_t *pu8PNG = NULL;
6397 uint32_t cbPNG = 0;
6398 uint32_t cxPNG = 0;
6399 uint32_t cyPNG = 0;
6400
6401 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6402
6403 if (RT_SUCCESS(vrc))
6404 {
6405 aData.resize(cbPNG);
6406 if (cbPNG)
6407 memcpy(&aData.front(), pu8PNG, cbPNG);
6408 }
6409 else
6410 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6411 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6412 vrc);
6413
6414 RTMemFree(pu8PNG);
6415 }
6416 }
6417
6418 freeSavedDisplayScreenshot(pu8Data);
6419
6420 return hr;
6421}
6422
6423HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6424 ULONG *aWidth,
6425 ULONG *aHeight,
6426 std::vector<BitmapFormat_T> &aBitmapFormats)
6427{
6428 if (aScreenId != 0)
6429 return E_NOTIMPL;
6430
6431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6432
6433 uint8_t *pu8Data = NULL;
6434 uint32_t cbData = 0;
6435 uint32_t u32Width = 0;
6436 uint32_t u32Height = 0;
6437
6438 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6439
6440 if (RT_FAILURE(vrc))
6441 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6442 tr("Saved screenshot data is not available (%Rrc)"),
6443 vrc);
6444
6445 *aWidth = u32Width;
6446 *aHeight = u32Height;
6447 aBitmapFormats.resize(1);
6448 aBitmapFormats[0] = BitmapFormat_PNG;
6449
6450 freeSavedDisplayScreenshot(pu8Data);
6451
6452 return S_OK;
6453}
6454
6455HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6456 BitmapFormat_T aBitmapFormat,
6457 ULONG *aWidth,
6458 ULONG *aHeight,
6459 std::vector<BYTE> &aData)
6460{
6461 if (aScreenId != 0)
6462 return E_NOTIMPL;
6463
6464 if (aBitmapFormat != BitmapFormat_PNG)
6465 return E_NOTIMPL;
6466
6467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6468
6469 uint8_t *pu8Data = NULL;
6470 uint32_t cbData = 0;
6471 uint32_t u32Width = 0;
6472 uint32_t u32Height = 0;
6473
6474 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6475
6476 if (RT_FAILURE(vrc))
6477 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6478 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6479 vrc);
6480
6481 *aWidth = u32Width;
6482 *aHeight = u32Height;
6483
6484 aData.resize(cbData);
6485 if (cbData)
6486 memcpy(&aData.front(), pu8Data, cbData);
6487
6488 freeSavedDisplayScreenshot(pu8Data);
6489
6490 return S_OK;
6491}
6492
6493HRESULT Machine::hotPlugCPU(ULONG aCpu)
6494{
6495 HRESULT rc = S_OK;
6496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6497
6498 if (!mHWData->mCPUHotPlugEnabled)
6499 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6500
6501 if (aCpu >= mHWData->mCPUCount)
6502 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6503
6504 if (mHWData->mCPUAttached[aCpu])
6505 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6506
6507 alock.release();
6508 rc = i_onCPUChange(aCpu, false);
6509 alock.acquire();
6510 if (FAILED(rc)) return rc;
6511
6512 i_setModified(IsModified_MachineData);
6513 mHWData.backup();
6514 mHWData->mCPUAttached[aCpu] = true;
6515
6516 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6517 if (Global::IsOnline(mData->mMachineState))
6518 i_saveSettings(NULL);
6519
6520 return S_OK;
6521}
6522
6523HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6524{
6525 HRESULT rc = S_OK;
6526
6527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6528
6529 if (!mHWData->mCPUHotPlugEnabled)
6530 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6531
6532 if (aCpu >= SchemaDefs::MaxCPUCount)
6533 return setError(E_INVALIDARG,
6534 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6535 SchemaDefs::MaxCPUCount);
6536
6537 if (!mHWData->mCPUAttached[aCpu])
6538 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6539
6540 /* CPU 0 can't be detached */
6541 if (aCpu == 0)
6542 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6543
6544 alock.release();
6545 rc = i_onCPUChange(aCpu, true);
6546 alock.acquire();
6547 if (FAILED(rc)) return rc;
6548
6549 i_setModified(IsModified_MachineData);
6550 mHWData.backup();
6551 mHWData->mCPUAttached[aCpu] = false;
6552
6553 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6554 if (Global::IsOnline(mData->mMachineState))
6555 i_saveSettings(NULL);
6556
6557 return S_OK;
6558}
6559
6560HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6561{
6562 *aAttached = false;
6563
6564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6565
6566 /* If hotplug is enabled the CPU is always enabled. */
6567 if (!mHWData->mCPUHotPlugEnabled)
6568 {
6569 if (aCpu < mHWData->mCPUCount)
6570 *aAttached = true;
6571 }
6572 else
6573 {
6574 if (aCpu < SchemaDefs::MaxCPUCount)
6575 *aAttached = mHWData->mCPUAttached[aCpu];
6576 }
6577
6578 return S_OK;
6579}
6580
6581HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6582{
6583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6584
6585 Utf8Str log = i_getLogFilename(aIdx);
6586 if (!RTFileExists(log.c_str()))
6587 log.setNull();
6588 aFilename = log;
6589
6590 return S_OK;
6591}
6592
6593HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6594{
6595 if (aSize < 0)
6596 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6597
6598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6599
6600 HRESULT rc = S_OK;
6601 Utf8Str log = i_getLogFilename(aIdx);
6602
6603 /* do not unnecessarily hold the lock while doing something which does
6604 * not need the lock and potentially takes a long time. */
6605 alock.release();
6606
6607 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6608 * keeps the SOAP reply size under 1M for the webservice (we're using
6609 * base64 encoded strings for binary data for years now, avoiding the
6610 * expansion of each byte array element to approx. 25 bytes of XML. */
6611 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6612 aData.resize(cbData);
6613
6614 RTFILE LogFile;
6615 int vrc = RTFileOpen(&LogFile, log.c_str(),
6616 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6617 if (RT_SUCCESS(vrc))
6618 {
6619 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6620 if (RT_SUCCESS(vrc))
6621 aData.resize(cbData);
6622 else
6623 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6624 tr("Could not read log file '%s' (%Rrc)"),
6625 log.c_str(), vrc);
6626 RTFileClose(LogFile);
6627 }
6628 else
6629 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6630 tr("Could not open log file '%s' (%Rrc)"),
6631 log.c_str(), vrc);
6632
6633 if (FAILED(rc))
6634 aData.resize(0);
6635
6636 return rc;
6637}
6638
6639
6640/**
6641 * Currently this method doesn't attach device to the running VM,
6642 * just makes sure it's plugged on next VM start.
6643 */
6644HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6645{
6646 // lock scope
6647 {
6648 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 HRESULT rc = i_checkStateDependency(MutableStateDep);
6651 if (FAILED(rc)) return rc;
6652
6653 ChipsetType_T aChipset = ChipsetType_PIIX3;
6654 COMGETTER(ChipsetType)(&aChipset);
6655
6656 if (aChipset != ChipsetType_ICH9)
6657 {
6658 return setError(E_INVALIDARG,
6659 tr("Host PCI attachment only supported with ICH9 chipset"));
6660 }
6661
6662 // check if device with this host PCI address already attached
6663 for (HWData::PCIDeviceAssignmentList::const_iterator
6664 it = mHWData->mPCIDeviceAssignments.begin();
6665 it != mHWData->mPCIDeviceAssignments.end();
6666 ++it)
6667 {
6668 LONG iHostAddress = -1;
6669 ComPtr<PCIDeviceAttachment> pAttach;
6670 pAttach = *it;
6671 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6672 if (iHostAddress == aHostAddress)
6673 return setError(E_INVALIDARG,
6674 tr("Device with host PCI address already attached to this VM"));
6675 }
6676
6677 ComObjPtr<PCIDeviceAttachment> pda;
6678 char name[32];
6679
6680 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6681 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6682 pda.createObject();
6683 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6684 i_setModified(IsModified_MachineData);
6685 mHWData.backup();
6686 mHWData->mPCIDeviceAssignments.push_back(pda);
6687 }
6688
6689 return S_OK;
6690}
6691
6692/**
6693 * Currently this method doesn't detach device from the running VM,
6694 * just makes sure it's not plugged on next VM start.
6695 */
6696HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6697{
6698 ComObjPtr<PCIDeviceAttachment> pAttach;
6699 bool fRemoved = false;
6700 HRESULT rc;
6701
6702 // lock scope
6703 {
6704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6705
6706 rc = i_checkStateDependency(MutableStateDep);
6707 if (FAILED(rc)) return rc;
6708
6709 for (HWData::PCIDeviceAssignmentList::const_iterator
6710 it = mHWData->mPCIDeviceAssignments.begin();
6711 it != mHWData->mPCIDeviceAssignments.end();
6712 ++it)
6713 {
6714 LONG iHostAddress = -1;
6715 pAttach = *it;
6716 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6717 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6718 {
6719 i_setModified(IsModified_MachineData);
6720 mHWData.backup();
6721 mHWData->mPCIDeviceAssignments.remove(pAttach);
6722 fRemoved = true;
6723 break;
6724 }
6725 }
6726 }
6727
6728
6729 /* Fire event outside of the lock */
6730 if (fRemoved)
6731 {
6732 Assert(!pAttach.isNull());
6733 ComPtr<IEventSource> es;
6734 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6735 Assert(SUCCEEDED(rc));
6736 Bstr mid;
6737 rc = this->COMGETTER(Id)(mid.asOutParam());
6738 Assert(SUCCEEDED(rc));
6739 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6740 }
6741
6742 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6743 tr("No host PCI device %08x attached"),
6744 aHostAddress
6745 );
6746}
6747
6748HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6749{
6750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6751
6752 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6753 size_t i = 0;
6754 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6755 it = mHWData->mPCIDeviceAssignments.begin();
6756 it != mHWData->mPCIDeviceAssignments.end();
6757 ++it, ++i)
6758 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6759
6760 return S_OK;
6761}
6762
6763HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6764{
6765 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6766
6767 return S_OK;
6768}
6769
6770HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6771{
6772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6773
6774 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6775
6776 return S_OK;
6777}
6778
6779HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6780{
6781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6782 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6783 if (SUCCEEDED(hrc))
6784 {
6785 hrc = mHWData.backupEx();
6786 if (SUCCEEDED(hrc))
6787 {
6788 i_setModified(IsModified_MachineData);
6789 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6790 }
6791 }
6792 return hrc;
6793}
6794
6795HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6796{
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6799 return S_OK;
6800}
6801
6802HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6803{
6804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6805 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6806 if (SUCCEEDED(hrc))
6807 {
6808 hrc = mHWData.backupEx();
6809 if (SUCCEEDED(hrc))
6810 {
6811 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6812 if (SUCCEEDED(hrc))
6813 i_setModified(IsModified_MachineData);
6814 }
6815 }
6816 return hrc;
6817}
6818
6819HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6820{
6821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6822
6823 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6824
6825 return S_OK;
6826}
6827
6828HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6829{
6830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6831 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6832 if (SUCCEEDED(hrc))
6833 {
6834 hrc = mHWData.backupEx();
6835 if (SUCCEEDED(hrc))
6836 {
6837 i_setModified(IsModified_MachineData);
6838 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6839 }
6840 }
6841 return hrc;
6842}
6843
6844HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6845{
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6849
6850 return S_OK;
6851}
6852
6853HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6854{
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856
6857 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6858 if ( SUCCEEDED(hrc)
6859 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6860 {
6861 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6862 int vrc;
6863
6864 if (aAutostartEnabled)
6865 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6866 else
6867 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6868
6869 if (RT_SUCCESS(vrc))
6870 {
6871 hrc = mHWData.backupEx();
6872 if (SUCCEEDED(hrc))
6873 {
6874 i_setModified(IsModified_MachineData);
6875 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6876 }
6877 }
6878 else if (vrc == VERR_NOT_SUPPORTED)
6879 hrc = setError(VBOX_E_NOT_SUPPORTED,
6880 tr("The VM autostart feature is not supported on this platform"));
6881 else if (vrc == VERR_PATH_NOT_FOUND)
6882 hrc = setError(E_FAIL,
6883 tr("The path to the autostart database is not set"));
6884 else
6885 hrc = setError(E_UNEXPECTED,
6886 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6887 aAutostartEnabled ? "Adding" : "Removing",
6888 mUserData->s.strName.c_str(), vrc);
6889 }
6890 return hrc;
6891}
6892
6893HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6894{
6895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6896
6897 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6898
6899 return S_OK;
6900}
6901
6902HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6903{
6904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6905 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6906 if (SUCCEEDED(hrc))
6907 {
6908 hrc = mHWData.backupEx();
6909 if (SUCCEEDED(hrc))
6910 {
6911 i_setModified(IsModified_MachineData);
6912 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6913 }
6914 }
6915 return hrc;
6916}
6917
6918HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6919{
6920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6921
6922 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6923
6924 return S_OK;
6925}
6926
6927HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6928{
6929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6930 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6931 if ( SUCCEEDED(hrc)
6932 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6933 {
6934 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6935 int vrc;
6936
6937 if (aAutostopType != AutostopType_Disabled)
6938 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6939 else
6940 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6941
6942 if (RT_SUCCESS(vrc))
6943 {
6944 hrc = mHWData.backupEx();
6945 if (SUCCEEDED(hrc))
6946 {
6947 i_setModified(IsModified_MachineData);
6948 mHWData->mAutostart.enmAutostopType = aAutostopType;
6949 }
6950 }
6951 else if (vrc == VERR_NOT_SUPPORTED)
6952 hrc = setError(VBOX_E_NOT_SUPPORTED,
6953 tr("The VM autostop feature is not supported on this platform"));
6954 else if (vrc == VERR_PATH_NOT_FOUND)
6955 hrc = setError(E_FAIL,
6956 tr("The path to the autostart database is not set"));
6957 else
6958 hrc = setError(E_UNEXPECTED,
6959 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6960 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6961 mUserData->s.strName.c_str(), vrc);
6962 }
6963 return hrc;
6964}
6965
6966HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6967{
6968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6969
6970 aDefaultFrontend = mHWData->mDefaultFrontend;
6971
6972 return S_OK;
6973}
6974
6975HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6976{
6977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6978 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6979 if (SUCCEEDED(hrc))
6980 {
6981 hrc = mHWData.backupEx();
6982 if (SUCCEEDED(hrc))
6983 {
6984 i_setModified(IsModified_MachineData);
6985 mHWData->mDefaultFrontend = aDefaultFrontend;
6986 }
6987 }
6988 return hrc;
6989}
6990
6991HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6992{
6993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6994 size_t cbIcon = mUserData->s.ovIcon.size();
6995 aIcon.resize(cbIcon);
6996 if (cbIcon)
6997 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6998 return S_OK;
6999}
7000
7001HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7002{
7003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7004 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7005 if (SUCCEEDED(hrc))
7006 {
7007 i_setModified(IsModified_MachineData);
7008 mUserData.backup();
7009 size_t cbIcon = aIcon.size();
7010 mUserData->s.ovIcon.resize(cbIcon);
7011 if (cbIcon)
7012 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7013 }
7014 return hrc;
7015}
7016
7017HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7018{
7019#ifdef VBOX_WITH_USB
7020 *aUSBProxyAvailable = true;
7021#else
7022 *aUSBProxyAvailable = false;
7023#endif
7024 return S_OK;
7025}
7026
7027HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7028{
7029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7030
7031 *aVMProcessPriority = mUserData->s.enmVMPriority;
7032
7033 return S_OK;
7034}
7035
7036HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7037{
7038 RT_NOREF(aVMProcessPriority);
7039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7040 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7041 if (SUCCEEDED(hrc))
7042 {
7043 hrc = mUserData.backupEx();
7044 if (SUCCEEDED(hrc))
7045 {
7046 i_setModified(IsModified_MachineData);
7047 mUserData->s.enmVMPriority = aVMProcessPriority;
7048 }
7049 }
7050 alock.release();
7051 if (SUCCEEDED(hrc))
7052 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7053 return hrc;
7054}
7055
7056HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7057 ComPtr<IProgress> &aProgress)
7058{
7059 ComObjPtr<Progress> pP;
7060 Progress *ppP = pP;
7061 IProgress *iP = static_cast<IProgress *>(ppP);
7062 IProgress **pProgress = &iP;
7063
7064 IMachine *pTarget = aTarget;
7065
7066 /* Convert the options. */
7067 RTCList<CloneOptions_T> optList;
7068 if (aOptions.size())
7069 for (size_t i = 0; i < aOptions.size(); ++i)
7070 optList.append(aOptions[i]);
7071
7072 if (optList.contains(CloneOptions_Link))
7073 {
7074 if (!i_isSnapshotMachine())
7075 return setError(E_INVALIDARG,
7076 tr("Linked clone can only be created from a snapshot"));
7077 if (aMode != CloneMode_MachineState)
7078 return setError(E_INVALIDARG,
7079 tr("Linked clone can only be created for a single machine state"));
7080 }
7081 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7082
7083 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7084
7085 HRESULT rc = pWorker->start(pProgress);
7086
7087 pP = static_cast<Progress *>(*pProgress);
7088 pP.queryInterfaceTo(aProgress.asOutParam());
7089
7090 return rc;
7091
7092}
7093
7094HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7095 const com::Utf8Str &aType,
7096 ComPtr<IProgress> &aProgress)
7097{
7098 LogFlowThisFuncEnter();
7099
7100 ComObjPtr<Progress> ptrProgress;
7101 HRESULT hrc = ptrProgress.createObject();
7102 if (SUCCEEDED(hrc))
7103 {
7104 /* Initialize our worker task */
7105 MachineMoveVM *pTask = NULL;
7106 try
7107 {
7108 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
7109 }
7110 catch (std::bad_alloc &)
7111 {
7112 return E_OUTOFMEMORY;
7113 }
7114
7115 hrc = pTask->init();//no exceptions are thrown
7116
7117 if (SUCCEEDED(hrc))
7118 {
7119 hrc = pTask->createThread();
7120 pTask = NULL; /* Consumed by createThread(). */
7121 if (SUCCEEDED(hrc))
7122 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7123 else
7124 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7125 }
7126 else
7127 delete pTask;
7128 }
7129
7130 LogFlowThisFuncLeave();
7131 return hrc;
7132
7133}
7134
7135HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7136{
7137 NOREF(aProgress);
7138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7139
7140 // This check should always fail.
7141 HRESULT rc = i_checkStateDependency(MutableStateDep);
7142 if (FAILED(rc)) return rc;
7143
7144 AssertFailedReturn(E_NOTIMPL);
7145}
7146
7147HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7148{
7149 NOREF(aSavedStateFile);
7150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7151
7152 // This check should always fail.
7153 HRESULT rc = i_checkStateDependency(MutableStateDep);
7154 if (FAILED(rc)) return rc;
7155
7156 AssertFailedReturn(E_NOTIMPL);
7157}
7158
7159HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7160{
7161 NOREF(aFRemoveFile);
7162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7163
7164 // This check should always fail.
7165 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7166 if (FAILED(rc)) return rc;
7167
7168 AssertFailedReturn(E_NOTIMPL);
7169}
7170
7171// public methods for internal purposes
7172/////////////////////////////////////////////////////////////////////////////
7173
7174/**
7175 * Adds the given IsModified_* flag to the dirty flags of the machine.
7176 * This must be called either during i_loadSettings or under the machine write lock.
7177 * @param fl Flag
7178 * @param fAllowStateModification If state modifications are allowed.
7179 */
7180void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7181{
7182 mData->flModifications |= fl;
7183 if (fAllowStateModification && i_isStateModificationAllowed())
7184 mData->mCurrentStateModified = true;
7185}
7186
7187/**
7188 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7189 * care of the write locking.
7190 *
7191 * @param fModification The flag to add.
7192 * @param fAllowStateModification If state modifications are allowed.
7193 */
7194void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7195{
7196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7197 i_setModified(fModification, fAllowStateModification);
7198}
7199
7200/**
7201 * Saves the registry entry of this machine to the given configuration node.
7202 *
7203 * @param data Machine registry data.
7204 *
7205 * @note locks this object for reading.
7206 */
7207HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7208{
7209 AutoLimitedCaller autoCaller(this);
7210 AssertComRCReturnRC(autoCaller.rc());
7211
7212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7213
7214 data.uuid = mData->mUuid;
7215 data.strSettingsFile = mData->m_strConfigFile;
7216
7217 return S_OK;
7218}
7219
7220/**
7221 * Calculates the absolute path of the given path taking the directory of the
7222 * machine settings file as the current directory.
7223 *
7224 * @param strPath Path to calculate the absolute path for.
7225 * @param aResult Where to put the result (used only on success, can be the
7226 * same Utf8Str instance as passed in @a aPath).
7227 * @return IPRT result.
7228 *
7229 * @note Locks this object for reading.
7230 */
7231int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7232{
7233 AutoCaller autoCaller(this);
7234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7235
7236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7237
7238 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7239
7240 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7241
7242 strSettingsDir.stripFilename();
7243 char szFolder[RTPATH_MAX];
7244 size_t cbFolder = sizeof(szFolder);
7245 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7246 if (RT_SUCCESS(vrc))
7247 aResult = szFolder;
7248
7249 return vrc;
7250}
7251
7252/**
7253 * Copies strSource to strTarget, making it relative to the machine folder
7254 * if it is a subdirectory thereof, or simply copying it otherwise.
7255 *
7256 * @param strSource Path to evaluate and copy.
7257 * @param strTarget Buffer to receive target path.
7258 *
7259 * @note Locks this object for reading.
7260 */
7261void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7262 Utf8Str &strTarget)
7263{
7264 AutoCaller autoCaller(this);
7265 AssertComRCReturn(autoCaller.rc(), (void)0);
7266
7267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7268
7269 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7270 // use strTarget as a temporary buffer to hold the machine settings dir
7271 strTarget = mData->m_strConfigFileFull;
7272 strTarget.stripFilename();
7273 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7274 {
7275 // is relative: then append what's left
7276 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7277 // for empty paths (only possible for subdirs) use "." to avoid
7278 // triggering default settings for not present config attributes.
7279 if (strTarget.isEmpty())
7280 strTarget = ".";
7281 }
7282 else
7283 // is not relative: then overwrite
7284 strTarget = strSource;
7285}
7286
7287/**
7288 * Returns the full path to the machine's log folder in the
7289 * \a aLogFolder argument.
7290 */
7291void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7292{
7293 AutoCaller autoCaller(this);
7294 AssertComRCReturnVoid(autoCaller.rc());
7295
7296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7297
7298 char szTmp[RTPATH_MAX];
7299 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7300 if (RT_SUCCESS(vrc))
7301 {
7302 if (szTmp[0] && !mUserData.isNull())
7303 {
7304 char szTmp2[RTPATH_MAX];
7305 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7306 if (RT_SUCCESS(vrc))
7307 aLogFolder = Utf8StrFmt("%s%c%s",
7308 szTmp2,
7309 RTPATH_DELIMITER,
7310 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7311 }
7312 else
7313 vrc = VERR_PATH_IS_RELATIVE;
7314 }
7315
7316 if (RT_FAILURE(vrc))
7317 {
7318 // fallback if VBOX_USER_LOGHOME is not set or invalid
7319 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7320 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7321 aLogFolder.append(RTPATH_DELIMITER);
7322 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7323 }
7324}
7325
7326/**
7327 * Returns the full path to the machine's log file for an given index.
7328 */
7329Utf8Str Machine::i_getLogFilename(ULONG idx)
7330{
7331 Utf8Str logFolder;
7332 getLogFolder(logFolder);
7333 Assert(logFolder.length());
7334
7335 Utf8Str log;
7336 if (idx == 0)
7337 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7338#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7339 else if (idx == 1)
7340 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7341 else
7342 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7343#else
7344 else
7345 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7346#endif
7347 return log;
7348}
7349
7350/**
7351 * Returns the full path to the machine's hardened log file.
7352 */
7353Utf8Str Machine::i_getHardeningLogFilename(void)
7354{
7355 Utf8Str strFilename;
7356 getLogFolder(strFilename);
7357 Assert(strFilename.length());
7358 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7359 return strFilename;
7360}
7361
7362/**
7363 * Returns the default NVRAM filename based on the location of the VM config.
7364 * This intentionally works differently than the saved state file naming since
7365 * it is part of the current state. Taking a snapshot will use a similar naming
7366 * as for saved state, because these are actually read-only, retaining a
7367 * a specific state just like saved state. Note that this is a relative path.
7368 */
7369
7370Utf8Str Machine::i_getDefaultNVRAMFilename()
7371{
7372 AutoCaller autoCaller(this);
7373 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7374
7375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7376
7377 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7378 return Utf8Str::Empty;
7379
7380 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7381 strNVRAMFilePath.stripPath();
7382 strNVRAMFilePath.stripSuffix();
7383 strNVRAMFilePath += ".nvram";
7384
7385 return strNVRAMFilePath;
7386}
7387
7388
7389/**
7390 * Composes a unique saved state filename based on the current system time. The filename is
7391 * granular to the second so this will work so long as no more than one snapshot is taken on
7392 * a machine per second.
7393 *
7394 * Before version 4.1, we used this formula for saved state files:
7395 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7396 * which no longer works because saved state files can now be shared between the saved state of the
7397 * "saved" machine and an online snapshot, and the following would cause problems:
7398 * 1) save machine
7399 * 2) create online snapshot from that machine state --> reusing saved state file
7400 * 3) save machine again --> filename would be reused, breaking the online snapshot
7401 *
7402 * So instead we now use a timestamp.
7403 *
7404 * @param strStateFilePath
7405 */
7406
7407void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7408{
7409 AutoCaller autoCaller(this);
7410 AssertComRCReturnVoid(autoCaller.rc());
7411
7412 {
7413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7414 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7415 }
7416
7417 RTTIMESPEC ts;
7418 RTTimeNow(&ts);
7419 RTTIME time;
7420 RTTimeExplode(&time, &ts);
7421
7422 strStateFilePath += RTPATH_DELIMITER;
7423 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7424 time.i32Year, time.u8Month, time.u8MonthDay,
7425 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7426}
7427
7428/**
7429 * Returns whether at least one USB controller is present for the VM.
7430 */
7431bool Machine::i_isUSBControllerPresent()
7432{
7433 AutoCaller autoCaller(this);
7434 AssertComRCReturn(autoCaller.rc(), false);
7435
7436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7437
7438 return (mUSBControllers->size() > 0);
7439}
7440
7441#ifdef VBOX_WITH_CLOUD_NET
7442HRESULT Machine::i_connectToCloudNetwork(ProgressProxy *aProgress)
7443{
7444 LogFlowThisFuncEnter();
7445 AssertReturn(aProgress, E_FAIL);
7446
7447 HRESULT hrc = E_FAIL;
7448 Bstr name;
7449
7450 LogFlowThisFunc(("Checking if cloud network needs to be connected\n"));
7451 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
7452 {
7453 BOOL enabled;
7454 hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
7455 if ( FAILED(hrc)
7456 || !enabled)
7457 continue;
7458
7459 NetworkAttachmentType_T type;
7460 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
7461 if ( SUCCEEDED(hrc)
7462 && type == NetworkAttachmentType_Cloud)
7463 {
7464 if (name.isNotEmpty())
7465 {
7466 LogRel(("VM '%s' uses multiple cloud network attachments. '%ls' will be ignored.\n",
7467 mUserData->s.strName.c_str(), name.raw()));
7468 continue;
7469 }
7470 hrc = mNetworkAdapters[slot]->COMGETTER(CloudNetwork)(name.asOutParam());
7471 if (SUCCEEDED(hrc))
7472 {
7473 LogRel(("VM '%s' uses cloud network '%ls'\n",
7474 mUserData->s.strName.c_str(), name.raw()));
7475 }
7476 }
7477 }
7478 if (name.isNotEmpty())
7479 {
7480 LogFlowThisFunc(("Connecting to cloud network '%ls'...\n", name.raw()));
7481 ComObjPtr<CloudNetwork> network;
7482 hrc = mParent->i_findCloudNetworkByName(name, &network);
7483 if (FAILED(hrc))
7484 {
7485 LogRel(("Could not find cloud network '%ls'.\n", name.raw()));
7486 return hrc;
7487 }
7488 GatewayInfo gateways;
7489 hrc = startGateways(mParent, network, gateways);
7490 if (SUCCEEDED(hrc))
7491 {
7492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7493 mData->mGatewayInfo = gateways;
7494 }
7495 }
7496 else
7497 LogFlowThisFunc(("VM '%s' has no cloud network attachments.\n", mUserData->s.strName.c_str()));
7498
7499 LogFlowThisFuncLeave();
7500 return hrc;
7501}
7502
7503HRESULT Machine::i_disconnectFromCloudNetwork()
7504{
7505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7506 GatewayInfo gateways(mData->mGatewayInfo);
7507 mData->mGatewayInfo.setNull();
7508 alock.release();
7509
7510 HRESULT hrc = stopGateways(mParent, gateways);
7511 return hrc;
7512}
7513#endif /* VBOX_WITH_CLOUD_NET */
7514
7515
7516/**
7517 * @note Locks this object for writing, calls the client process
7518 * (inside the lock).
7519 */
7520HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7521 const Utf8Str &strFrontend,
7522 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7523 ProgressProxy *aProgress)
7524{
7525 LogFlowThisFuncEnter();
7526
7527 AssertReturn(aControl, E_FAIL);
7528 AssertReturn(aProgress, E_FAIL);
7529 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7530
7531 AutoCaller autoCaller(this);
7532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7533
7534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7535
7536 if (!mData->mRegistered)
7537 return setError(E_UNEXPECTED,
7538 tr("The machine '%s' is not registered"),
7539 mUserData->s.strName.c_str());
7540
7541 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7542
7543 /* The process started when launching a VM with separate UI/VM processes is always
7544 * the UI process, i.e. needs special handling as it won't claim the session. */
7545 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7546
7547 if (fSeparate)
7548 {
7549 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7550 return setError(VBOX_E_INVALID_OBJECT_STATE,
7551 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7552 mUserData->s.strName.c_str());
7553 }
7554 else
7555 {
7556 if ( mData->mSession.mState == SessionState_Locked
7557 || mData->mSession.mState == SessionState_Spawning
7558 || mData->mSession.mState == SessionState_Unlocking)
7559 return setError(VBOX_E_INVALID_OBJECT_STATE,
7560 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7561 mUserData->s.strName.c_str());
7562
7563 /* may not be busy */
7564 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7565 }
7566
7567 /* Hardening logging */
7568#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7569 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7570 {
7571 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7572 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7573 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7574 {
7575 Utf8Str strStartupLogDir = strHardeningLogFile;
7576 strStartupLogDir.stripFilename();
7577 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7578 file without stripping the file. */
7579 }
7580 strSupHardeningLogArg.append(strHardeningLogFile);
7581
7582 /* Remove legacy log filename to avoid confusion. */
7583 Utf8Str strOldStartupLogFile;
7584 getLogFolder(strOldStartupLogFile);
7585 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7586 RTFileDelete(strOldStartupLogFile.c_str());
7587 }
7588#else
7589 Utf8Str strSupHardeningLogArg;
7590#endif
7591
7592 Utf8Str strAppOverride;
7593#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7594 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7595#endif
7596
7597 bool fUseVBoxSDS = false;
7598 Utf8Str strCanonicalName;
7599 if (false)
7600 { }
7601#ifdef VBOX_WITH_QTGUI
7602 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7603 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7604 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7605 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7606 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7607 {
7608 strCanonicalName = "GUI/Qt";
7609 fUseVBoxSDS = true;
7610 }
7611#endif
7612#ifdef VBOX_WITH_VBOXSDL
7613 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7614 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7615 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7616 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7617 {
7618 strCanonicalName = "GUI/SDL";
7619 fUseVBoxSDS = true;
7620 }
7621#endif
7622#ifdef VBOX_WITH_HEADLESS
7623 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7624 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7625 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7626 {
7627 strCanonicalName = "headless";
7628 }
7629#endif
7630 else
7631 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7632
7633 Utf8Str idStr = mData->mUuid.toString();
7634 Utf8Str const &strMachineName = mUserData->s.strName;
7635 RTPROCESS pid = NIL_RTPROCESS;
7636
7637#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7638 RT_NOREF(fUseVBoxSDS);
7639#else
7640 DWORD idCallerSession = ~(DWORD)0;
7641 if (fUseVBoxSDS)
7642 {
7643 /*
7644 * The VBoxSDS should be used for process launching the VM with
7645 * GUI only if the caller and the VBoxSDS are in different Windows
7646 * sessions and the caller in the interactive one.
7647 */
7648 fUseVBoxSDS = false;
7649
7650 /* Get windows session of the current process. The process token used
7651 due to several reasons:
7652 1. The token is absent for the current thread except someone set it
7653 for us.
7654 2. Needs to get the id of the session where the process is started.
7655 We only need to do this once, though. */
7656 static DWORD s_idCurrentSession = ~(DWORD)0;
7657 DWORD idCurrentSession = s_idCurrentSession;
7658 if (idCurrentSession == ~(DWORD)0)
7659 {
7660 HANDLE hCurrentProcessToken = NULL;
7661 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7662 {
7663 DWORD cbIgn = 0;
7664 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7665 s_idCurrentSession = idCurrentSession;
7666 else
7667 {
7668 idCurrentSession = ~(DWORD)0;
7669 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7670 }
7671 CloseHandle(hCurrentProcessToken);
7672 }
7673 else
7674 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7675 }
7676
7677 /* get the caller's session */
7678 HRESULT hrc = CoImpersonateClient();
7679 if (SUCCEEDED(hrc))
7680 {
7681 HANDLE hCallerThreadToken;
7682 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7683 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7684 &hCallerThreadToken))
7685 {
7686 SetLastError(NO_ERROR);
7687 DWORD cbIgn = 0;
7688 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7689 {
7690 /* Only need to use SDS if the session ID differs: */
7691 if (idCurrentSession != idCallerSession)
7692 {
7693 fUseVBoxSDS = false;
7694
7695 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7696 DWORD cbTokenGroups = 0;
7697 PTOKEN_GROUPS pTokenGroups = NULL;
7698 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7699 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7700 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7701 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7702 {
7703 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7704 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7705 PSID pInteractiveSid = NULL;
7706 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7707 {
7708 /* Iterate over the groups looking for the interactive SID: */
7709 fUseVBoxSDS = false;
7710 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7711 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7712 {
7713 fUseVBoxSDS = true;
7714 break;
7715 }
7716 FreeSid(pInteractiveSid);
7717 }
7718 }
7719 else
7720 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7721 RTMemTmpFree(pTokenGroups);
7722 }
7723 }
7724 else
7725 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7726 CloseHandle(hCallerThreadToken);
7727 }
7728 else
7729 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7730 CoRevertToSelf();
7731 }
7732 else
7733 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7734 }
7735 if (fUseVBoxSDS)
7736 {
7737 /* connect to VBoxSDS */
7738 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7739 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7740 if (FAILED(rc))
7741 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7742 strMachineName.c_str());
7743
7744 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7745 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7746 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7747 service to access the files. */
7748 rc = CoSetProxyBlanket(pVBoxSDS,
7749 RPC_C_AUTHN_DEFAULT,
7750 RPC_C_AUTHZ_DEFAULT,
7751 COLE_DEFAULT_PRINCIPAL,
7752 RPC_C_AUTHN_LEVEL_DEFAULT,
7753 RPC_C_IMP_LEVEL_IMPERSONATE,
7754 NULL,
7755 EOAC_DEFAULT);
7756 if (FAILED(rc))
7757 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7758
7759 size_t const cEnvVars = aEnvironmentChanges.size();
7760 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7761 for (size_t i = 0; i < cEnvVars; i++)
7762 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7763
7764 ULONG uPid = 0;
7765 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7766 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7767 idCallerSession, &uPid);
7768 if (FAILED(rc))
7769 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7770 pid = (RTPROCESS)uPid;
7771 }
7772 else
7773#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7774 {
7775 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7776 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7777 if (RT_FAILURE(vrc))
7778 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7779 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7780 }
7781
7782 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7783 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7784
7785 if (!fSeparate)
7786 {
7787 /*
7788 * Note that we don't release the lock here before calling the client,
7789 * because it doesn't need to call us back if called with a NULL argument.
7790 * Releasing the lock here is dangerous because we didn't prepare the
7791 * launch data yet, but the client we've just started may happen to be
7792 * too fast and call LockMachine() that will fail (because of PID, etc.),
7793 * so that the Machine will never get out of the Spawning session state.
7794 */
7795
7796 /* inform the session that it will be a remote one */
7797 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7798#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7799 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7800#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7801 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7802#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7803 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7804
7805 if (FAILED(rc))
7806 {
7807 /* restore the session state */
7808 mData->mSession.mState = SessionState_Unlocked;
7809 alock.release();
7810 mParent->i_addProcessToReap(pid);
7811 /* The failure may occur w/o any error info (from RPC), so provide one */
7812 return setError(VBOX_E_VM_ERROR,
7813 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7814 }
7815
7816 /* attach launch data to the machine */
7817 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7818 mData->mSession.mRemoteControls.push_back(aControl);
7819 mData->mSession.mProgress = aProgress;
7820 mData->mSession.mPID = pid;
7821 mData->mSession.mState = SessionState_Spawning;
7822 Assert(strCanonicalName.isNotEmpty());
7823 mData->mSession.mName = strCanonicalName;
7824 }
7825 else
7826 {
7827 /* For separate UI process we declare the launch as completed instantly, as the
7828 * actual headless VM start may or may not come. No point in remembering anything
7829 * yet, as what matters for us is when the headless VM gets started. */
7830 aProgress->i_notifyComplete(S_OK);
7831 }
7832
7833 alock.release();
7834 mParent->i_addProcessToReap(pid);
7835
7836 LogFlowThisFuncLeave();
7837 return S_OK;
7838}
7839
7840/**
7841 * Returns @c true if the given session machine instance has an open direct
7842 * session (and optionally also for direct sessions which are closing) and
7843 * returns the session control machine instance if so.
7844 *
7845 * Note that when the method returns @c false, the arguments remain unchanged.
7846 *
7847 * @param aMachine Session machine object.
7848 * @param aControl Direct session control object (optional).
7849 * @param aRequireVM If true then only allow VM sessions.
7850 * @param aAllowClosing If true then additionally a session which is currently
7851 * being closed will also be allowed.
7852 *
7853 * @note locks this object for reading.
7854 */
7855bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7856 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7857 bool aRequireVM /*= false*/,
7858 bool aAllowClosing /*= false*/)
7859{
7860 AutoLimitedCaller autoCaller(this);
7861 AssertComRCReturn(autoCaller.rc(), false);
7862
7863 /* just return false for inaccessible machines */
7864 if (getObjectState().getState() != ObjectState::Ready)
7865 return false;
7866
7867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7868
7869 if ( ( mData->mSession.mState == SessionState_Locked
7870 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7871 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7872 )
7873 {
7874 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7875
7876 aMachine = mData->mSession.mMachine;
7877
7878 if (aControl != NULL)
7879 *aControl = mData->mSession.mDirectControl;
7880
7881 return true;
7882 }
7883
7884 return false;
7885}
7886
7887/**
7888 * Returns @c true if the given machine has an spawning direct session.
7889 *
7890 * @note locks this object for reading.
7891 */
7892bool Machine::i_isSessionSpawning()
7893{
7894 AutoLimitedCaller autoCaller(this);
7895 AssertComRCReturn(autoCaller.rc(), false);
7896
7897 /* just return false for inaccessible machines */
7898 if (getObjectState().getState() != ObjectState::Ready)
7899 return false;
7900
7901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7902
7903 if (mData->mSession.mState == SessionState_Spawning)
7904 return true;
7905
7906 return false;
7907}
7908
7909/**
7910 * Called from the client watcher thread to check for unexpected client process
7911 * death during Session_Spawning state (e.g. before it successfully opened a
7912 * direct session).
7913 *
7914 * On Win32 and on OS/2, this method is called only when we've got the
7915 * direct client's process termination notification, so it always returns @c
7916 * true.
7917 *
7918 * On other platforms, this method returns @c true if the client process is
7919 * terminated and @c false if it's still alive.
7920 *
7921 * @note Locks this object for writing.
7922 */
7923bool Machine::i_checkForSpawnFailure()
7924{
7925 AutoCaller autoCaller(this);
7926 if (!autoCaller.isOk())
7927 {
7928 /* nothing to do */
7929 LogFlowThisFunc(("Already uninitialized!\n"));
7930 return true;
7931 }
7932
7933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7934
7935 if (mData->mSession.mState != SessionState_Spawning)
7936 {
7937 /* nothing to do */
7938 LogFlowThisFunc(("Not spawning any more!\n"));
7939 return true;
7940 }
7941
7942 HRESULT rc = S_OK;
7943
7944 /* PID not yet initialized, skip check. */
7945 if (mData->mSession.mPID == NIL_RTPROCESS)
7946 return false;
7947
7948 RTPROCSTATUS status;
7949 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7950
7951 if (vrc != VERR_PROCESS_RUNNING)
7952 {
7953 Utf8Str strExtraInfo;
7954
7955#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7956 /* If the startup logfile exists and is of non-zero length, tell the
7957 user to look there for more details to encourage them to attach it
7958 when reporting startup issues. */
7959 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7960 uint64_t cbStartupLogFile = 0;
7961 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7962 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7963 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7964#endif
7965
7966 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7967 rc = setError(E_FAIL,
7968 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7969 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7970 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7971 rc = setError(E_FAIL,
7972 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7973 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7974 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7975 rc = setError(E_FAIL,
7976 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7977 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7978 else
7979 rc = setErrorBoth(E_FAIL, vrc,
7980 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7981 i_getName().c_str(), vrc, strExtraInfo.c_str());
7982 }
7983
7984 if (FAILED(rc))
7985 {
7986 /* Close the remote session, remove the remote control from the list
7987 * and reset session state to Closed (@note keep the code in sync with
7988 * the relevant part in LockMachine()). */
7989
7990 Assert(mData->mSession.mRemoteControls.size() == 1);
7991 if (mData->mSession.mRemoteControls.size() == 1)
7992 {
7993 ErrorInfoKeeper eik;
7994 mData->mSession.mRemoteControls.front()->Uninitialize();
7995 }
7996
7997 mData->mSession.mRemoteControls.clear();
7998 mData->mSession.mState = SessionState_Unlocked;
7999
8000 /* finalize the progress after setting the state */
8001 if (!mData->mSession.mProgress.isNull())
8002 {
8003 mData->mSession.mProgress->notifyComplete(rc);
8004 mData->mSession.mProgress.setNull();
8005 }
8006
8007 mData->mSession.mPID = NIL_RTPROCESS;
8008
8009 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8010 return true;
8011 }
8012
8013 return false;
8014}
8015
8016/**
8017 * Checks whether the machine can be registered. If so, commits and saves
8018 * all settings.
8019 *
8020 * @note Must be called from mParent's write lock. Locks this object and
8021 * children for writing.
8022 */
8023HRESULT Machine::i_prepareRegister()
8024{
8025 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8026
8027 AutoLimitedCaller autoCaller(this);
8028 AssertComRCReturnRC(autoCaller.rc());
8029
8030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8031
8032 /* wait for state dependents to drop to zero */
8033 i_ensureNoStateDependencies();
8034
8035 if (!mData->mAccessible)
8036 return setError(VBOX_E_INVALID_OBJECT_STATE,
8037 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8038 mUserData->s.strName.c_str(),
8039 mData->mUuid.toString().c_str());
8040
8041 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8042
8043 if (mData->mRegistered)
8044 return setError(VBOX_E_INVALID_OBJECT_STATE,
8045 tr("The machine '%s' with UUID {%s} is already registered"),
8046 mUserData->s.strName.c_str(),
8047 mData->mUuid.toString().c_str());
8048
8049 HRESULT rc = S_OK;
8050
8051 // Ensure the settings are saved. If we are going to be registered and
8052 // no config file exists yet, create it by calling i_saveSettings() too.
8053 if ( (mData->flModifications)
8054 || (!mData->pMachineConfigFile->fileExists())
8055 )
8056 {
8057 rc = i_saveSettings(NULL);
8058 // no need to check whether VirtualBox.xml needs saving too since
8059 // we can't have a machine XML file rename pending
8060 if (FAILED(rc)) return rc;
8061 }
8062
8063 /* more config checking goes here */
8064
8065 if (SUCCEEDED(rc))
8066 {
8067 /* we may have had implicit modifications we want to fix on success */
8068 i_commit();
8069
8070 mData->mRegistered = true;
8071 }
8072 else
8073 {
8074 /* we may have had implicit modifications we want to cancel on failure*/
8075 i_rollback(false /* aNotify */);
8076 }
8077
8078 return rc;
8079}
8080
8081/**
8082 * Increases the number of objects dependent on the machine state or on the
8083 * registered state. Guarantees that these two states will not change at least
8084 * until #i_releaseStateDependency() is called.
8085 *
8086 * Depending on the @a aDepType value, additional state checks may be made.
8087 * These checks will set extended error info on failure. See
8088 * #i_checkStateDependency() for more info.
8089 *
8090 * If this method returns a failure, the dependency is not added and the caller
8091 * is not allowed to rely on any particular machine state or registration state
8092 * value and may return the failed result code to the upper level.
8093 *
8094 * @param aDepType Dependency type to add.
8095 * @param aState Current machine state (NULL if not interested).
8096 * @param aRegistered Current registered state (NULL if not interested).
8097 *
8098 * @note Locks this object for writing.
8099 */
8100HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8101 MachineState_T *aState /* = NULL */,
8102 BOOL *aRegistered /* = NULL */)
8103{
8104 AutoCaller autoCaller(this);
8105 AssertComRCReturnRC(autoCaller.rc());
8106
8107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8108
8109 HRESULT rc = i_checkStateDependency(aDepType);
8110 if (FAILED(rc)) return rc;
8111
8112 {
8113 if (mData->mMachineStateChangePending != 0)
8114 {
8115 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8116 * drop to zero so don't add more. It may make sense to wait a bit
8117 * and retry before reporting an error (since the pending state
8118 * transition should be really quick) but let's just assert for
8119 * now to see if it ever happens on practice. */
8120
8121 AssertFailed();
8122
8123 return setError(E_ACCESSDENIED,
8124 tr("Machine state change is in progress. Please retry the operation later."));
8125 }
8126
8127 ++mData->mMachineStateDeps;
8128 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8129 }
8130
8131 if (aState)
8132 *aState = mData->mMachineState;
8133 if (aRegistered)
8134 *aRegistered = mData->mRegistered;
8135
8136 return S_OK;
8137}
8138
8139/**
8140 * Decreases the number of objects dependent on the machine state.
8141 * Must always complete the #i_addStateDependency() call after the state
8142 * dependency is no more necessary.
8143 */
8144void Machine::i_releaseStateDependency()
8145{
8146 AutoCaller autoCaller(this);
8147 AssertComRCReturnVoid(autoCaller.rc());
8148
8149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8150
8151 /* releaseStateDependency() w/o addStateDependency()? */
8152 AssertReturnVoid(mData->mMachineStateDeps != 0);
8153 -- mData->mMachineStateDeps;
8154
8155 if (mData->mMachineStateDeps == 0)
8156 {
8157 /* inform i_ensureNoStateDependencies() that there are no more deps */
8158 if (mData->mMachineStateChangePending != 0)
8159 {
8160 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8161 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8162 }
8163 }
8164}
8165
8166Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8167{
8168 /* start with nothing found */
8169 Utf8Str strResult("");
8170
8171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8172
8173 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8174 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8175 // found:
8176 strResult = it->second; // source is a Utf8Str
8177
8178 return strResult;
8179}
8180
8181// protected methods
8182/////////////////////////////////////////////////////////////////////////////
8183
8184/**
8185 * Performs machine state checks based on the @a aDepType value. If a check
8186 * fails, this method will set extended error info, otherwise it will return
8187 * S_OK. It is supposed, that on failure, the caller will immediately return
8188 * the return value of this method to the upper level.
8189 *
8190 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8191 *
8192 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8193 * current state of this machine object allows to change settings of the
8194 * machine (i.e. the machine is not registered, or registered but not running
8195 * and not saved). It is useful to call this method from Machine setters
8196 * before performing any change.
8197 *
8198 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8199 * as for MutableStateDep except that if the machine is saved, S_OK is also
8200 * returned. This is useful in setters which allow changing machine
8201 * properties when it is in the saved state.
8202 *
8203 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8204 * if the current state of this machine object allows to change runtime
8205 * changeable settings of the machine (i.e. the machine is not registered, or
8206 * registered but either running or not running and not saved). It is useful
8207 * to call this method from Machine setters before performing any changes to
8208 * runtime changeable settings.
8209 *
8210 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8211 * the same as for MutableOrRunningStateDep except that if the machine is
8212 * saved, S_OK is also returned. This is useful in setters which allow
8213 * changing runtime and saved state changeable machine properties.
8214 *
8215 * @param aDepType Dependency type to check.
8216 *
8217 * @note Non Machine based classes should use #i_addStateDependency() and
8218 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8219 * template.
8220 *
8221 * @note This method must be called from under this object's read or write
8222 * lock.
8223 */
8224HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8225{
8226 switch (aDepType)
8227 {
8228 case AnyStateDep:
8229 {
8230 break;
8231 }
8232 case MutableStateDep:
8233 {
8234 if ( mData->mRegistered
8235 && ( !i_isSessionMachine()
8236 || ( mData->mMachineState != MachineState_Aborted
8237 && mData->mMachineState != MachineState_Teleported
8238 && mData->mMachineState != MachineState_PoweredOff
8239 )
8240 )
8241 )
8242 return setError(VBOX_E_INVALID_VM_STATE,
8243 tr("The machine is not mutable (state is %s)"),
8244 Global::stringifyMachineState(mData->mMachineState));
8245 break;
8246 }
8247 case MutableOrSavedStateDep:
8248 {
8249 if ( mData->mRegistered
8250 && ( !i_isSessionMachine()
8251 || ( mData->mMachineState != MachineState_Aborted
8252 && mData->mMachineState != MachineState_Teleported
8253 && mData->mMachineState != MachineState_Saved
8254 && mData->mMachineState != MachineState_PoweredOff
8255 )
8256 )
8257 )
8258 return setError(VBOX_E_INVALID_VM_STATE,
8259 tr("The machine is not mutable or saved (state is %s)"),
8260 Global::stringifyMachineState(mData->mMachineState));
8261 break;
8262 }
8263 case MutableOrRunningStateDep:
8264 {
8265 if ( mData->mRegistered
8266 && ( !i_isSessionMachine()
8267 || ( mData->mMachineState != MachineState_Aborted
8268 && mData->mMachineState != MachineState_Teleported
8269 && mData->mMachineState != MachineState_PoweredOff
8270 && !Global::IsOnline(mData->mMachineState)
8271 )
8272 )
8273 )
8274 return setError(VBOX_E_INVALID_VM_STATE,
8275 tr("The machine is not mutable or running (state is %s)"),
8276 Global::stringifyMachineState(mData->mMachineState));
8277 break;
8278 }
8279 case MutableOrSavedOrRunningStateDep:
8280 {
8281 if ( mData->mRegistered
8282 && ( !i_isSessionMachine()
8283 || ( mData->mMachineState != MachineState_Aborted
8284 && mData->mMachineState != MachineState_Teleported
8285 && mData->mMachineState != MachineState_Saved
8286 && mData->mMachineState != MachineState_PoweredOff
8287 && !Global::IsOnline(mData->mMachineState)
8288 )
8289 )
8290 )
8291 return setError(VBOX_E_INVALID_VM_STATE,
8292 tr("The machine is not mutable, saved or running (state is %s)"),
8293 Global::stringifyMachineState(mData->mMachineState));
8294 break;
8295 }
8296 }
8297
8298 return S_OK;
8299}
8300
8301/**
8302 * Helper to initialize all associated child objects and allocate data
8303 * structures.
8304 *
8305 * This method must be called as a part of the object's initialization procedure
8306 * (usually done in the #init() method).
8307 *
8308 * @note Must be called only from #init() or from #i_registeredInit().
8309 */
8310HRESULT Machine::initDataAndChildObjects()
8311{
8312 AutoCaller autoCaller(this);
8313 AssertComRCReturnRC(autoCaller.rc());
8314 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8315 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8316
8317 AssertReturn(!mData->mAccessible, E_FAIL);
8318
8319 /* allocate data structures */
8320 mSSData.allocate();
8321 mUserData.allocate();
8322 mHWData.allocate();
8323 mMediumAttachments.allocate();
8324 mStorageControllers.allocate();
8325 mUSBControllers.allocate();
8326
8327 /* initialize mOSTypeId */
8328 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8329
8330/** @todo r=bird: init() methods never fails, right? Why don't we make them
8331 * return void then! */
8332
8333 /* create associated BIOS settings object */
8334 unconst(mBIOSSettings).createObject();
8335 mBIOSSettings->init(this);
8336
8337 /* create associated record settings object */
8338 unconst(mRecordingSettings).createObject();
8339 mRecordingSettings->init(this);
8340
8341 /* create an associated VRDE object (default is disabled) */
8342 unconst(mVRDEServer).createObject();
8343 mVRDEServer->init(this);
8344
8345 /* create associated serial port objects */
8346 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8347 {
8348 unconst(mSerialPorts[slot]).createObject();
8349 mSerialPorts[slot]->init(this, slot);
8350 }
8351
8352 /* create associated parallel port objects */
8353 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8354 {
8355 unconst(mParallelPorts[slot]).createObject();
8356 mParallelPorts[slot]->init(this, slot);
8357 }
8358
8359 /* create the audio adapter object (always present, default is disabled) */
8360 unconst(mAudioAdapter).createObject();
8361 mAudioAdapter->init(this);
8362
8363 /* create the USB device filters object (always present) */
8364 unconst(mUSBDeviceFilters).createObject();
8365 mUSBDeviceFilters->init(this);
8366
8367 /* create associated network adapter objects */
8368 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8369 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8370 {
8371 unconst(mNetworkAdapters[slot]).createObject();
8372 mNetworkAdapters[slot]->init(this, slot);
8373 }
8374
8375 /* create the bandwidth control */
8376 unconst(mBandwidthControl).createObject();
8377 mBandwidthControl->init(this);
8378
8379 return S_OK;
8380}
8381
8382/**
8383 * Helper to uninitialize all associated child objects and to free all data
8384 * structures.
8385 *
8386 * This method must be called as a part of the object's uninitialization
8387 * procedure (usually done in the #uninit() method).
8388 *
8389 * @note Must be called only from #uninit() or from #i_registeredInit().
8390 */
8391void Machine::uninitDataAndChildObjects()
8392{
8393 AutoCaller autoCaller(this);
8394 AssertComRCReturnVoid(autoCaller.rc());
8395 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8396 || getObjectState().getState() == ObjectState::Limited);
8397
8398 /* tell all our other child objects we've been uninitialized */
8399 if (mBandwidthControl)
8400 {
8401 mBandwidthControl->uninit();
8402 unconst(mBandwidthControl).setNull();
8403 }
8404
8405 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8406 {
8407 if (mNetworkAdapters[slot])
8408 {
8409 mNetworkAdapters[slot]->uninit();
8410 unconst(mNetworkAdapters[slot]).setNull();
8411 }
8412 }
8413
8414 if (mUSBDeviceFilters)
8415 {
8416 mUSBDeviceFilters->uninit();
8417 unconst(mUSBDeviceFilters).setNull();
8418 }
8419
8420 if (mAudioAdapter)
8421 {
8422 mAudioAdapter->uninit();
8423 unconst(mAudioAdapter).setNull();
8424 }
8425
8426 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8427 {
8428 if (mParallelPorts[slot])
8429 {
8430 mParallelPorts[slot]->uninit();
8431 unconst(mParallelPorts[slot]).setNull();
8432 }
8433 }
8434
8435 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8436 {
8437 if (mSerialPorts[slot])
8438 {
8439 mSerialPorts[slot]->uninit();
8440 unconst(mSerialPorts[slot]).setNull();
8441 }
8442 }
8443
8444 if (mVRDEServer)
8445 {
8446 mVRDEServer->uninit();
8447 unconst(mVRDEServer).setNull();
8448 }
8449
8450 if (mBIOSSettings)
8451 {
8452 mBIOSSettings->uninit();
8453 unconst(mBIOSSettings).setNull();
8454 }
8455
8456 if (mRecordingSettings)
8457 {
8458 mRecordingSettings->uninit();
8459 unconst(mRecordingSettings).setNull();
8460 }
8461
8462 /* Deassociate media (only when a real Machine or a SnapshotMachine
8463 * instance is uninitialized; SessionMachine instances refer to real
8464 * Machine media). This is necessary for a clean re-initialization of
8465 * the VM after successfully re-checking the accessibility state. Note
8466 * that in case of normal Machine or SnapshotMachine uninitialization (as
8467 * a result of unregistering or deleting the snapshot), outdated media
8468 * attachments will already be uninitialized and deleted, so this
8469 * code will not affect them. */
8470 if ( !mMediumAttachments.isNull()
8471 && !i_isSessionMachine()
8472 )
8473 {
8474 for (MediumAttachmentList::const_iterator
8475 it = mMediumAttachments->begin();
8476 it != mMediumAttachments->end();
8477 ++it)
8478 {
8479 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8480 if (pMedium.isNull())
8481 continue;
8482 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8483 AssertComRC(rc);
8484 }
8485 }
8486
8487 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8488 {
8489 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8490 if (mData->mFirstSnapshot)
8491 {
8492 // snapshots tree is protected by machine write lock; strictly
8493 // this isn't necessary here since we're deleting the entire
8494 // machine, but otherwise we assert in Snapshot::uninit()
8495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8496 mData->mFirstSnapshot->uninit();
8497 mData->mFirstSnapshot.setNull();
8498 }
8499
8500 mData->mCurrentSnapshot.setNull();
8501 }
8502
8503 /* free data structures (the essential mData structure is not freed here
8504 * since it may be still in use) */
8505 mMediumAttachments.free();
8506 mStorageControllers.free();
8507 mUSBControllers.free();
8508 mHWData.free();
8509 mUserData.free();
8510 mSSData.free();
8511}
8512
8513/**
8514 * Returns a pointer to the Machine object for this machine that acts like a
8515 * parent for complex machine data objects such as shared folders, etc.
8516 *
8517 * For primary Machine objects and for SnapshotMachine objects, returns this
8518 * object's pointer itself. For SessionMachine objects, returns the peer
8519 * (primary) machine pointer.
8520 */
8521Machine *Machine::i_getMachine()
8522{
8523 if (i_isSessionMachine())
8524 return (Machine*)mPeer;
8525 return this;
8526}
8527
8528/**
8529 * Makes sure that there are no machine state dependents. If necessary, waits
8530 * for the number of dependents to drop to zero.
8531 *
8532 * Make sure this method is called from under this object's write lock to
8533 * guarantee that no new dependents may be added when this method returns
8534 * control to the caller.
8535 *
8536 * @note Locks this object for writing. The lock will be released while waiting
8537 * (if necessary).
8538 *
8539 * @warning To be used only in methods that change the machine state!
8540 */
8541void Machine::i_ensureNoStateDependencies()
8542{
8543 AssertReturnVoid(isWriteLockOnCurrentThread());
8544
8545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8546
8547 /* Wait for all state dependents if necessary */
8548 if (mData->mMachineStateDeps != 0)
8549 {
8550 /* lazy semaphore creation */
8551 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8552 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8553
8554 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8555 mData->mMachineStateDeps));
8556
8557 ++mData->mMachineStateChangePending;
8558
8559 /* reset the semaphore before waiting, the last dependent will signal
8560 * it */
8561 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8562
8563 alock.release();
8564
8565 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8566
8567 alock.acquire();
8568
8569 -- mData->mMachineStateChangePending;
8570 }
8571}
8572
8573/**
8574 * Changes the machine state and informs callbacks.
8575 *
8576 * This method is not intended to fail so it either returns S_OK or asserts (and
8577 * returns a failure).
8578 *
8579 * @note Locks this object for writing.
8580 */
8581HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8582{
8583 LogFlowThisFuncEnter();
8584 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8585 Assert(aMachineState != MachineState_Null);
8586
8587 AutoCaller autoCaller(this);
8588 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8589
8590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8591
8592 /* wait for state dependents to drop to zero */
8593 i_ensureNoStateDependencies();
8594
8595 MachineState_T const enmOldState = mData->mMachineState;
8596 if (enmOldState != aMachineState)
8597 {
8598 mData->mMachineState = aMachineState;
8599 RTTimeNow(&mData->mLastStateChange);
8600
8601#ifdef VBOX_WITH_DTRACE_R3_MAIN
8602 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8603#endif
8604 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8605 }
8606
8607 LogFlowThisFuncLeave();
8608 return S_OK;
8609}
8610
8611/**
8612 * Searches for a shared folder with the given logical name
8613 * in the collection of shared folders.
8614 *
8615 * @param aName logical name of the shared folder
8616 * @param aSharedFolder where to return the found object
8617 * @param aSetError whether to set the error info if the folder is
8618 * not found
8619 * @return
8620 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8621 *
8622 * @note
8623 * must be called from under the object's lock!
8624 */
8625HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8626 ComObjPtr<SharedFolder> &aSharedFolder,
8627 bool aSetError /* = false */)
8628{
8629 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8630 for (HWData::SharedFolderList::const_iterator
8631 it = mHWData->mSharedFolders.begin();
8632 it != mHWData->mSharedFolders.end();
8633 ++it)
8634 {
8635 SharedFolder *pSF = *it;
8636 AutoCaller autoCaller(pSF);
8637 if (pSF->i_getName() == aName)
8638 {
8639 aSharedFolder = pSF;
8640 rc = S_OK;
8641 break;
8642 }
8643 }
8644
8645 if (aSetError && FAILED(rc))
8646 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8647
8648 return rc;
8649}
8650
8651/**
8652 * Initializes all machine instance data from the given settings structures
8653 * from XML. The exception is the machine UUID which needs special handling
8654 * depending on the caller's use case, so the caller needs to set that herself.
8655 *
8656 * This gets called in several contexts during machine initialization:
8657 *
8658 * -- When machine XML exists on disk already and needs to be loaded into memory,
8659 * for example, from #i_registeredInit() to load all registered machines on
8660 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8661 * attached to the machine should be part of some media registry already.
8662 *
8663 * -- During OVF import, when a machine config has been constructed from an
8664 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8665 * ensure that the media listed as attachments in the config (which have
8666 * been imported from the OVF) receive the correct registry ID.
8667 *
8668 * -- During VM cloning.
8669 *
8670 * @param config Machine settings from XML.
8671 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8672 * for each attached medium in the config.
8673 * @return
8674 */
8675HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8676 const Guid *puuidRegistry)
8677{
8678 // copy name, description, OS type, teleporter, UTC etc.
8679 mUserData->s = config.machineUserData;
8680
8681 // look up the object by Id to check it is valid
8682 ComObjPtr<GuestOSType> pGuestOSType;
8683 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8684 if (!pGuestOSType.isNull())
8685 mUserData->s.strOsType = pGuestOSType->i_id();
8686
8687 // stateFile (optional)
8688 if (config.strStateFile.isEmpty())
8689 mSSData->strStateFilePath.setNull();
8690 else
8691 {
8692 Utf8Str stateFilePathFull(config.strStateFile);
8693 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8694 if (RT_FAILURE(vrc))
8695 return setErrorBoth(E_FAIL, vrc,
8696 tr("Invalid saved state file path '%s' (%Rrc)"),
8697 config.strStateFile.c_str(),
8698 vrc);
8699 mSSData->strStateFilePath = stateFilePathFull;
8700 }
8701
8702 // snapshot folder needs special processing so set it again
8703 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8704 if (FAILED(rc)) return rc;
8705
8706 /* Copy the extra data items (config may or may not be the same as
8707 * mData->pMachineConfigFile) if necessary. When loading the XML files
8708 * from disk they are the same, but not for OVF import. */
8709 if (mData->pMachineConfigFile != &config)
8710 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8711
8712 /* currentStateModified (optional, default is true) */
8713 mData->mCurrentStateModified = config.fCurrentStateModified;
8714
8715 mData->mLastStateChange = config.timeLastStateChange;
8716
8717 /*
8718 * note: all mUserData members must be assigned prior this point because
8719 * we need to commit changes in order to let mUserData be shared by all
8720 * snapshot machine instances.
8721 */
8722 mUserData.commitCopy();
8723
8724 // machine registry, if present (must be loaded before snapshots)
8725 if (config.canHaveOwnMediaRegistry())
8726 {
8727 // determine machine folder
8728 Utf8Str strMachineFolder = i_getSettingsFileFull();
8729 strMachineFolder.stripFilename();
8730 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8731 config.mediaRegistry,
8732 strMachineFolder);
8733 if (FAILED(rc)) return rc;
8734 }
8735
8736 /* Snapshot node (optional) */
8737 size_t cRootSnapshots;
8738 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8739 {
8740 // there must be only one root snapshot
8741 Assert(cRootSnapshots == 1);
8742
8743 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8744
8745 rc = i_loadSnapshot(snap,
8746 config.uuidCurrentSnapshot,
8747 NULL); // no parent == first snapshot
8748 if (FAILED(rc)) return rc;
8749 }
8750
8751 // hardware data
8752 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8753 if (FAILED(rc)) return rc;
8754
8755 /*
8756 * NOTE: the assignment below must be the last thing to do,
8757 * otherwise it will be not possible to change the settings
8758 * somewhere in the code above because all setters will be
8759 * blocked by i_checkStateDependency(MutableStateDep).
8760 */
8761
8762 /* set the machine state to Aborted or Saved when appropriate */
8763 if (config.fAborted)
8764 {
8765 mSSData->strStateFilePath.setNull();
8766
8767 /* no need to use i_setMachineState() during init() */
8768 mData->mMachineState = MachineState_Aborted;
8769 }
8770 else if (!mSSData->strStateFilePath.isEmpty())
8771 {
8772 /* no need to use i_setMachineState() during init() */
8773 mData->mMachineState = MachineState_Saved;
8774 }
8775
8776 // after loading settings, we are no longer different from the XML on disk
8777 mData->flModifications = 0;
8778
8779 return S_OK;
8780}
8781
8782/**
8783 * Recursively loads all snapshots starting from the given.
8784 *
8785 * @param data snapshot settings.
8786 * @param aCurSnapshotId Current snapshot ID from the settings file.
8787 * @param aParentSnapshot Parent snapshot.
8788 */
8789HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8790 const Guid &aCurSnapshotId,
8791 Snapshot *aParentSnapshot)
8792{
8793 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8794 AssertReturn(!i_isSessionMachine(), E_FAIL);
8795
8796 HRESULT rc = S_OK;
8797
8798 Utf8Str strStateFile;
8799 if (!data.strStateFile.isEmpty())
8800 {
8801 /* optional */
8802 strStateFile = data.strStateFile;
8803 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8804 if (RT_FAILURE(vrc))
8805 return setErrorBoth(E_FAIL, vrc,
8806 tr("Invalid saved state file path '%s' (%Rrc)"),
8807 strStateFile.c_str(),
8808 vrc);
8809 }
8810
8811 /* create a snapshot machine object */
8812 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8813 pSnapshotMachine.createObject();
8814 rc = pSnapshotMachine->initFromSettings(this,
8815 data.hardware,
8816 &data.debugging,
8817 &data.autostart,
8818 data.uuid.ref(),
8819 strStateFile);
8820 if (FAILED(rc)) return rc;
8821
8822 /* create a snapshot object */
8823 ComObjPtr<Snapshot> pSnapshot;
8824 pSnapshot.createObject();
8825 /* initialize the snapshot */
8826 rc = pSnapshot->init(mParent, // VirtualBox object
8827 data.uuid,
8828 data.strName,
8829 data.strDescription,
8830 data.timestamp,
8831 pSnapshotMachine,
8832 aParentSnapshot);
8833 if (FAILED(rc)) return rc;
8834
8835 /* memorize the first snapshot if necessary */
8836 if (!mData->mFirstSnapshot)
8837 mData->mFirstSnapshot = pSnapshot;
8838
8839 /* memorize the current snapshot when appropriate */
8840 if ( !mData->mCurrentSnapshot
8841 && pSnapshot->i_getId() == aCurSnapshotId
8842 )
8843 mData->mCurrentSnapshot = pSnapshot;
8844
8845 // now create the children
8846 for (settings::SnapshotsList::const_iterator
8847 it = data.llChildSnapshots.begin();
8848 it != data.llChildSnapshots.end();
8849 ++it)
8850 {
8851 const settings::Snapshot &childData = *it;
8852 // recurse
8853 rc = i_loadSnapshot(childData,
8854 aCurSnapshotId,
8855 pSnapshot); // parent = the one we created above
8856 if (FAILED(rc)) return rc;
8857 }
8858
8859 return rc;
8860}
8861
8862/**
8863 * Loads settings into mHWData.
8864 *
8865 * @param puuidRegistry Registry ID.
8866 * @param puuidSnapshot Snapshot ID
8867 * @param data Reference to the hardware settings.
8868 * @param pDbg Pointer to the debugging settings.
8869 * @param pAutostart Pointer to the autostart settings.
8870 */
8871HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8872 const Guid *puuidSnapshot,
8873 const settings::Hardware &data,
8874 const settings::Debugging *pDbg,
8875 const settings::Autostart *pAutostart)
8876{
8877 AssertReturn(!i_isSessionMachine(), E_FAIL);
8878
8879 HRESULT rc = S_OK;
8880
8881 try
8882 {
8883 ComObjPtr<GuestOSType> pGuestOSType;
8884 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8885
8886 /* The hardware version attribute (optional). */
8887 mHWData->mHWVersion = data.strVersion;
8888 mHWData->mHardwareUUID = data.uuid;
8889
8890 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8891 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8892 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8893 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8894 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8895 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8896 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8897 mHWData->mPAEEnabled = data.fPAE;
8898 mHWData->mLongMode = data.enmLongMode;
8899 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8900 mHWData->mAPIC = data.fAPIC;
8901 mHWData->mX2APIC = data.fX2APIC;
8902 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8903 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8904 mHWData->mSpecCtrl = data.fSpecCtrl;
8905 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8906 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8907 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8908 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8909 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8910 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8911 mHWData->mCPUCount = data.cCPUs;
8912 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8913 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8914 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8915 mHWData->mCpuProfile = data.strCpuProfile;
8916
8917 // cpu
8918 if (mHWData->mCPUHotPlugEnabled)
8919 {
8920 for (settings::CpuList::const_iterator
8921 it = data.llCpus.begin();
8922 it != data.llCpus.end();
8923 ++it)
8924 {
8925 const settings::Cpu &cpu = *it;
8926
8927 mHWData->mCPUAttached[cpu.ulId] = true;
8928 }
8929 }
8930
8931 // cpuid leafs
8932 for (settings::CpuIdLeafsList::const_iterator
8933 it = data.llCpuIdLeafs.begin();
8934 it != data.llCpuIdLeafs.end();
8935 ++it)
8936 {
8937 const settings::CpuIdLeaf &rLeaf= *it;
8938 if ( rLeaf.idx < UINT32_C(0x20)
8939 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8940 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8941 mHWData->mCpuIdLeafList.push_back(rLeaf);
8942 /* else: just ignore */
8943 }
8944
8945 mHWData->mMemorySize = data.ulMemorySizeMB;
8946 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8947
8948 // boot order
8949 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8950 {
8951 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8952 if (it == data.mapBootOrder.end())
8953 mHWData->mBootOrder[i] = DeviceType_Null;
8954 else
8955 mHWData->mBootOrder[i] = it->second;
8956 }
8957
8958 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8959 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8960 mHWData->mMonitorCount = data.cMonitors;
8961 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8962 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8963 mHWData->mFirmwareType = data.firmwareType;
8964 mHWData->mPointingHIDType = data.pointingHIDType;
8965 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8966 mHWData->mChipsetType = data.chipsetType;
8967 mHWData->mParavirtProvider = data.paravirtProvider;
8968 mHWData->mParavirtDebug = data.strParavirtDebug;
8969 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8970 mHWData->mHPETEnabled = data.fHPETEnabled;
8971
8972 /* VRDEServer */
8973 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8974 if (FAILED(rc)) return rc;
8975
8976 /* BIOS */
8977 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8978 if (FAILED(rc)) return rc;
8979
8980 /* Recording settings */
8981 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8982 if (FAILED(rc)) return rc;
8983
8984 // Bandwidth control (must come before network adapters)
8985 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8986 if (FAILED(rc)) return rc;
8987
8988 /* USB controllers */
8989 for (settings::USBControllerList::const_iterator
8990 it = data.usbSettings.llUSBControllers.begin();
8991 it != data.usbSettings.llUSBControllers.end();
8992 ++it)
8993 {
8994 const settings::USBController &settingsCtrl = *it;
8995 ComObjPtr<USBController> newCtrl;
8996
8997 newCtrl.createObject();
8998 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8999 mUSBControllers->push_back(newCtrl);
9000 }
9001
9002 /* USB device filters */
9003 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9004 if (FAILED(rc)) return rc;
9005
9006 // network adapters (establish array size first and apply defaults, to
9007 // ensure reading the same settings as we saved, since the list skips
9008 // adapters having defaults)
9009 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9010 size_t oldCount = mNetworkAdapters.size();
9011 if (newCount > oldCount)
9012 {
9013 mNetworkAdapters.resize(newCount);
9014 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9015 {
9016 unconst(mNetworkAdapters[slot]).createObject();
9017 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9018 }
9019 }
9020 else if (newCount < oldCount)
9021 mNetworkAdapters.resize(newCount);
9022 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9023 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9024 for (settings::NetworkAdaptersList::const_iterator
9025 it = data.llNetworkAdapters.begin();
9026 it != data.llNetworkAdapters.end();
9027 ++it)
9028 {
9029 const settings::NetworkAdapter &nic = *it;
9030
9031 /* slot uniqueness is guaranteed by XML Schema */
9032 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9033 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9034 if (FAILED(rc)) return rc;
9035 }
9036
9037 // serial ports (establish defaults first, to ensure reading the same
9038 // settings as we saved, since the list skips ports having defaults)
9039 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9040 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9041 for (settings::SerialPortsList::const_iterator
9042 it = data.llSerialPorts.begin();
9043 it != data.llSerialPorts.end();
9044 ++it)
9045 {
9046 const settings::SerialPort &s = *it;
9047
9048 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9049 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9050 if (FAILED(rc)) return rc;
9051 }
9052
9053 // parallel ports (establish defaults first, to ensure reading the same
9054 // settings as we saved, since the list skips ports having defaults)
9055 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9056 mParallelPorts[i]->i_applyDefaults();
9057 for (settings::ParallelPortsList::const_iterator
9058 it = data.llParallelPorts.begin();
9059 it != data.llParallelPorts.end();
9060 ++it)
9061 {
9062 const settings::ParallelPort &p = *it;
9063
9064 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9065 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9066 if (FAILED(rc)) return rc;
9067 }
9068
9069 /* AudioAdapter */
9070 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9071 if (FAILED(rc)) return rc;
9072
9073 /* storage controllers */
9074 rc = i_loadStorageControllers(data.storage,
9075 puuidRegistry,
9076 puuidSnapshot);
9077 if (FAILED(rc)) return rc;
9078
9079 /* Shared folders */
9080 for (settings::SharedFoldersList::const_iterator
9081 it = data.llSharedFolders.begin();
9082 it != data.llSharedFolders.end();
9083 ++it)
9084 {
9085 const settings::SharedFolder &sf = *it;
9086
9087 ComObjPtr<SharedFolder> sharedFolder;
9088 /* Check for double entries. Not allowed! */
9089 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9090 if (SUCCEEDED(rc))
9091 return setError(VBOX_E_OBJECT_IN_USE,
9092 tr("Shared folder named '%s' already exists"),
9093 sf.strName.c_str());
9094
9095 /* Create the new shared folder. Don't break on error. This will be
9096 * reported when the machine starts. */
9097 sharedFolder.createObject();
9098 rc = sharedFolder->init(i_getMachine(),
9099 sf.strName,
9100 sf.strHostPath,
9101 RT_BOOL(sf.fWritable),
9102 RT_BOOL(sf.fAutoMount),
9103 sf.strAutoMountPoint,
9104 false /* fFailOnError */);
9105 if (FAILED(rc)) return rc;
9106 mHWData->mSharedFolders.push_back(sharedFolder);
9107 }
9108
9109 // Clipboard
9110 mHWData->mClipboardMode = data.clipboardMode;
9111 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9112
9113 // drag'n'drop
9114 mHWData->mDnDMode = data.dndMode;
9115
9116 // guest settings
9117 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9118
9119 // IO settings
9120 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9121 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9122
9123 // Host PCI devices
9124 for (settings::HostPCIDeviceAttachmentList::const_iterator
9125 it = data.pciAttachments.begin();
9126 it != data.pciAttachments.end();
9127 ++it)
9128 {
9129 const settings::HostPCIDeviceAttachment &hpda = *it;
9130 ComObjPtr<PCIDeviceAttachment> pda;
9131
9132 pda.createObject();
9133 pda->i_loadSettings(this, hpda);
9134 mHWData->mPCIDeviceAssignments.push_back(pda);
9135 }
9136
9137 /*
9138 * (The following isn't really real hardware, but it lives in HWData
9139 * for reasons of convenience.)
9140 */
9141
9142#ifdef VBOX_WITH_GUEST_PROPS
9143 /* Guest properties (optional) */
9144
9145 /* Only load transient guest properties for configs which have saved
9146 * state, because there shouldn't be any for powered off VMs. The same
9147 * logic applies for snapshots, as offline snapshots shouldn't have
9148 * any such properties. They confuse the code in various places.
9149 * Note: can't rely on the machine state, as it isn't set yet. */
9150 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9151 /* apologies for the hacky unconst() usage, but this needs hacking
9152 * actually inconsistent settings into consistency, otherwise there
9153 * will be some corner cases where the inconsistency survives
9154 * surprisingly long without getting fixed, especially for snapshots
9155 * as there are no config changes. */
9156 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9157 for (settings::GuestPropertiesList::iterator
9158 it = llGuestProperties.begin();
9159 it != llGuestProperties.end();
9160 /*nothing*/)
9161 {
9162 const settings::GuestProperty &prop = *it;
9163 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9164 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9165 if ( fSkipTransientGuestProperties
9166 && ( fFlags & GUEST_PROP_F_TRANSIENT
9167 || fFlags & GUEST_PROP_F_TRANSRESET))
9168 {
9169 it = llGuestProperties.erase(it);
9170 continue;
9171 }
9172 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9173 mHWData->mGuestProperties[prop.strName] = property;
9174 ++it;
9175 }
9176#endif /* VBOX_WITH_GUEST_PROPS defined */
9177
9178 rc = i_loadDebugging(pDbg);
9179 if (FAILED(rc))
9180 return rc;
9181
9182 mHWData->mAutostart = *pAutostart;
9183
9184 /* default frontend */
9185 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9186 }
9187 catch (std::bad_alloc &)
9188 {
9189 return E_OUTOFMEMORY;
9190 }
9191
9192 AssertComRC(rc);
9193 return rc;
9194}
9195
9196/**
9197 * Called from i_loadHardware() to load the debugging settings of the
9198 * machine.
9199 *
9200 * @param pDbg Pointer to the settings.
9201 */
9202HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9203{
9204 mHWData->mDebugging = *pDbg;
9205 /* no more processing currently required, this will probably change. */
9206 return S_OK;
9207}
9208
9209/**
9210 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9211 *
9212 * @param data storage settings.
9213 * @param puuidRegistry media registry ID to set media to or NULL;
9214 * see Machine::i_loadMachineDataFromSettings()
9215 * @param puuidSnapshot snapshot ID
9216 * @return
9217 */
9218HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9219 const Guid *puuidRegistry,
9220 const Guid *puuidSnapshot)
9221{
9222 AssertReturn(!i_isSessionMachine(), E_FAIL);
9223
9224 HRESULT rc = S_OK;
9225
9226 for (settings::StorageControllersList::const_iterator
9227 it = data.llStorageControllers.begin();
9228 it != data.llStorageControllers.end();
9229 ++it)
9230 {
9231 const settings::StorageController &ctlData = *it;
9232
9233 ComObjPtr<StorageController> pCtl;
9234 /* Try to find one with the name first. */
9235 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9236 if (SUCCEEDED(rc))
9237 return setError(VBOX_E_OBJECT_IN_USE,
9238 tr("Storage controller named '%s' already exists"),
9239 ctlData.strName.c_str());
9240
9241 pCtl.createObject();
9242 rc = pCtl->init(this,
9243 ctlData.strName,
9244 ctlData.storageBus,
9245 ctlData.ulInstance,
9246 ctlData.fBootable);
9247 if (FAILED(rc)) return rc;
9248
9249 mStorageControllers->push_back(pCtl);
9250
9251 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9252 if (FAILED(rc)) return rc;
9253
9254 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9255 if (FAILED(rc)) return rc;
9256
9257 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9258 if (FAILED(rc)) return rc;
9259
9260 /* Load the attached devices now. */
9261 rc = i_loadStorageDevices(pCtl,
9262 ctlData,
9263 puuidRegistry,
9264 puuidSnapshot);
9265 if (FAILED(rc)) return rc;
9266 }
9267
9268 return S_OK;
9269}
9270
9271/**
9272 * Called from i_loadStorageControllers for a controller's devices.
9273 *
9274 * @param aStorageController
9275 * @param data
9276 * @param puuidRegistry media registry ID to set media to or NULL; see
9277 * Machine::i_loadMachineDataFromSettings()
9278 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9279 * @return
9280 */
9281HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9282 const settings::StorageController &data,
9283 const Guid *puuidRegistry,
9284 const Guid *puuidSnapshot)
9285{
9286 HRESULT rc = S_OK;
9287
9288 /* paranoia: detect duplicate attachments */
9289 for (settings::AttachedDevicesList::const_iterator
9290 it = data.llAttachedDevices.begin();
9291 it != data.llAttachedDevices.end();
9292 ++it)
9293 {
9294 const settings::AttachedDevice &ad = *it;
9295
9296 for (settings::AttachedDevicesList::const_iterator it2 = it;
9297 it2 != data.llAttachedDevices.end();
9298 ++it2)
9299 {
9300 if (it == it2)
9301 continue;
9302
9303 const settings::AttachedDevice &ad2 = *it2;
9304
9305 if ( ad.lPort == ad2.lPort
9306 && ad.lDevice == ad2.lDevice)
9307 {
9308 return setError(E_FAIL,
9309 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9310 aStorageController->i_getName().c_str(),
9311 ad.lPort,
9312 ad.lDevice,
9313 mUserData->s.strName.c_str());
9314 }
9315 }
9316 }
9317
9318 for (settings::AttachedDevicesList::const_iterator
9319 it = data.llAttachedDevices.begin();
9320 it != data.llAttachedDevices.end();
9321 ++it)
9322 {
9323 const settings::AttachedDevice &dev = *it;
9324 ComObjPtr<Medium> medium;
9325
9326 switch (dev.deviceType)
9327 {
9328 case DeviceType_Floppy:
9329 case DeviceType_DVD:
9330 if (dev.strHostDriveSrc.isNotEmpty())
9331 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9332 false /* fRefresh */, medium);
9333 else
9334 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9335 dev.uuid,
9336 false /* fRefresh */,
9337 false /* aSetError */,
9338 medium);
9339 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9340 // This is not an error. The host drive or UUID might have vanished, so just go
9341 // ahead without this removeable medium attachment
9342 rc = S_OK;
9343 break;
9344
9345 case DeviceType_HardDisk:
9346 {
9347 /* find a hard disk by UUID */
9348 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9349 if (FAILED(rc))
9350 {
9351 if (i_isSnapshotMachine())
9352 {
9353 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9354 // so the user knows that the bad disk is in a snapshot somewhere
9355 com::ErrorInfo info;
9356 return setError(E_FAIL,
9357 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9358 puuidSnapshot->raw(),
9359 info.getText().raw());
9360 }
9361 else
9362 return rc;
9363 }
9364
9365 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9366
9367 if (medium->i_getType() == MediumType_Immutable)
9368 {
9369 if (i_isSnapshotMachine())
9370 return setError(E_FAIL,
9371 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9372 "of the virtual machine '%s' ('%s')"),
9373 medium->i_getLocationFull().c_str(),
9374 dev.uuid.raw(),
9375 puuidSnapshot->raw(),
9376 mUserData->s.strName.c_str(),
9377 mData->m_strConfigFileFull.c_str());
9378
9379 return setError(E_FAIL,
9380 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9381 medium->i_getLocationFull().c_str(),
9382 dev.uuid.raw(),
9383 mUserData->s.strName.c_str(),
9384 mData->m_strConfigFileFull.c_str());
9385 }
9386
9387 if (medium->i_getType() == MediumType_MultiAttach)
9388 {
9389 if (i_isSnapshotMachine())
9390 return setError(E_FAIL,
9391 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9392 "of the virtual machine '%s' ('%s')"),
9393 medium->i_getLocationFull().c_str(),
9394 dev.uuid.raw(),
9395 puuidSnapshot->raw(),
9396 mUserData->s.strName.c_str(),
9397 mData->m_strConfigFileFull.c_str());
9398
9399 return setError(E_FAIL,
9400 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9401 medium->i_getLocationFull().c_str(),
9402 dev.uuid.raw(),
9403 mUserData->s.strName.c_str(),
9404 mData->m_strConfigFileFull.c_str());
9405 }
9406
9407 if ( !i_isSnapshotMachine()
9408 && medium->i_getChildren().size() != 0
9409 )
9410 return setError(E_FAIL,
9411 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9412 "because it has %d differencing child hard disks"),
9413 medium->i_getLocationFull().c_str(),
9414 dev.uuid.raw(),
9415 mUserData->s.strName.c_str(),
9416 mData->m_strConfigFileFull.c_str(),
9417 medium->i_getChildren().size());
9418
9419 if (i_findAttachment(*mMediumAttachments.data(),
9420 medium))
9421 return setError(E_FAIL,
9422 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9423 medium->i_getLocationFull().c_str(),
9424 dev.uuid.raw(),
9425 mUserData->s.strName.c_str(),
9426 mData->m_strConfigFileFull.c_str());
9427
9428 break;
9429 }
9430
9431 default:
9432 return setError(E_FAIL,
9433 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9434 medium->i_getLocationFull().c_str(),
9435 mUserData->s.strName.c_str(),
9436 mData->m_strConfigFileFull.c_str());
9437 }
9438
9439 if (FAILED(rc))
9440 break;
9441
9442 /* Bandwidth groups are loaded at this point. */
9443 ComObjPtr<BandwidthGroup> pBwGroup;
9444
9445 if (!dev.strBwGroup.isEmpty())
9446 {
9447 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9448 if (FAILED(rc))
9449 return setError(E_FAIL,
9450 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9451 medium->i_getLocationFull().c_str(),
9452 dev.strBwGroup.c_str(),
9453 mUserData->s.strName.c_str(),
9454 mData->m_strConfigFileFull.c_str());
9455 pBwGroup->i_reference();
9456 }
9457
9458 const Utf8Str controllerName = aStorageController->i_getName();
9459 ComObjPtr<MediumAttachment> pAttachment;
9460 pAttachment.createObject();
9461 rc = pAttachment->init(this,
9462 medium,
9463 controllerName,
9464 dev.lPort,
9465 dev.lDevice,
9466 dev.deviceType,
9467 false,
9468 dev.fPassThrough,
9469 dev.fTempEject,
9470 dev.fNonRotational,
9471 dev.fDiscard,
9472 dev.fHotPluggable,
9473 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9474 if (FAILED(rc)) break;
9475
9476 /* associate the medium with this machine and snapshot */
9477 if (!medium.isNull())
9478 {
9479 AutoCaller medCaller(medium);
9480 if (FAILED(medCaller.rc())) return medCaller.rc();
9481 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9482
9483 if (i_isSnapshotMachine())
9484 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9485 else
9486 rc = medium->i_addBackReference(mData->mUuid);
9487 /* If the medium->addBackReference fails it sets an appropriate
9488 * error message, so no need to do any guesswork here. */
9489
9490 if (puuidRegistry)
9491 // caller wants registry ID to be set on all attached media (OVF import case)
9492 medium->i_addRegistry(*puuidRegistry);
9493 }
9494
9495 if (FAILED(rc))
9496 break;
9497
9498 /* back up mMediumAttachments to let registeredInit() properly rollback
9499 * on failure (= limited accessibility) */
9500 i_setModified(IsModified_Storage);
9501 mMediumAttachments.backup();
9502 mMediumAttachments->push_back(pAttachment);
9503 }
9504
9505 return rc;
9506}
9507
9508/**
9509 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9510 *
9511 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9512 * @param aSnapshot where to return the found snapshot
9513 * @param aSetError true to set extended error info on failure
9514 */
9515HRESULT Machine::i_findSnapshotById(const Guid &aId,
9516 ComObjPtr<Snapshot> &aSnapshot,
9517 bool aSetError /* = false */)
9518{
9519 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9520
9521 if (!mData->mFirstSnapshot)
9522 {
9523 if (aSetError)
9524 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9525 return E_FAIL;
9526 }
9527
9528 if (aId.isZero())
9529 aSnapshot = mData->mFirstSnapshot;
9530 else
9531 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9532
9533 if (!aSnapshot)
9534 {
9535 if (aSetError)
9536 return setError(E_FAIL,
9537 tr("Could not find a snapshot with UUID {%s}"),
9538 aId.toString().c_str());
9539 return E_FAIL;
9540 }
9541
9542 return S_OK;
9543}
9544
9545/**
9546 * Returns the snapshot with the given name or fails of no such snapshot.
9547 *
9548 * @param strName snapshot name to find
9549 * @param aSnapshot where to return the found snapshot
9550 * @param aSetError true to set extended error info on failure
9551 */
9552HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9553 ComObjPtr<Snapshot> &aSnapshot,
9554 bool aSetError /* = false */)
9555{
9556 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9557
9558 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9559
9560 if (!mData->mFirstSnapshot)
9561 {
9562 if (aSetError)
9563 return setError(VBOX_E_OBJECT_NOT_FOUND,
9564 tr("This machine does not have any snapshots"));
9565 return VBOX_E_OBJECT_NOT_FOUND;
9566 }
9567
9568 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9569
9570 if (!aSnapshot)
9571 {
9572 if (aSetError)
9573 return setError(VBOX_E_OBJECT_NOT_FOUND,
9574 tr("Could not find a snapshot named '%s'"), strName.c_str());
9575 return VBOX_E_OBJECT_NOT_FOUND;
9576 }
9577
9578 return S_OK;
9579}
9580
9581/**
9582 * Returns a storage controller object with the given name.
9583 *
9584 * @param aName storage controller name to find
9585 * @param aStorageController where to return the found storage controller
9586 * @param aSetError true to set extended error info on failure
9587 */
9588HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9589 ComObjPtr<StorageController> &aStorageController,
9590 bool aSetError /* = false */)
9591{
9592 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9593
9594 for (StorageControllerList::const_iterator
9595 it = mStorageControllers->begin();
9596 it != mStorageControllers->end();
9597 ++it)
9598 {
9599 if ((*it)->i_getName() == aName)
9600 {
9601 aStorageController = (*it);
9602 return S_OK;
9603 }
9604 }
9605
9606 if (aSetError)
9607 return setError(VBOX_E_OBJECT_NOT_FOUND,
9608 tr("Could not find a storage controller named '%s'"),
9609 aName.c_str());
9610 return VBOX_E_OBJECT_NOT_FOUND;
9611}
9612
9613/**
9614 * Returns a USB controller object with the given name.
9615 *
9616 * @param aName USB controller name to find
9617 * @param aUSBController where to return the found USB controller
9618 * @param aSetError true to set extended error info on failure
9619 */
9620HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9621 ComObjPtr<USBController> &aUSBController,
9622 bool aSetError /* = false */)
9623{
9624 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9625
9626 for (USBControllerList::const_iterator
9627 it = mUSBControllers->begin();
9628 it != mUSBControllers->end();
9629 ++it)
9630 {
9631 if ((*it)->i_getName() == aName)
9632 {
9633 aUSBController = (*it);
9634 return S_OK;
9635 }
9636 }
9637
9638 if (aSetError)
9639 return setError(VBOX_E_OBJECT_NOT_FOUND,
9640 tr("Could not find a storage controller named '%s'"),
9641 aName.c_str());
9642 return VBOX_E_OBJECT_NOT_FOUND;
9643}
9644
9645/**
9646 * Returns the number of USB controller instance of the given type.
9647 *
9648 * @param enmType USB controller type.
9649 */
9650ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9651{
9652 ULONG cCtrls = 0;
9653
9654 for (USBControllerList::const_iterator
9655 it = mUSBControllers->begin();
9656 it != mUSBControllers->end();
9657 ++it)
9658 {
9659 if ((*it)->i_getControllerType() == enmType)
9660 cCtrls++;
9661 }
9662
9663 return cCtrls;
9664}
9665
9666HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9667 MediumAttachmentList &atts)
9668{
9669 AutoCaller autoCaller(this);
9670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9671
9672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9673
9674 for (MediumAttachmentList::const_iterator
9675 it = mMediumAttachments->begin();
9676 it != mMediumAttachments->end();
9677 ++it)
9678 {
9679 const ComObjPtr<MediumAttachment> &pAtt = *it;
9680 // should never happen, but deal with NULL pointers in the list.
9681 AssertContinue(!pAtt.isNull());
9682
9683 // getControllerName() needs caller+read lock
9684 AutoCaller autoAttCaller(pAtt);
9685 if (FAILED(autoAttCaller.rc()))
9686 {
9687 atts.clear();
9688 return autoAttCaller.rc();
9689 }
9690 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9691
9692 if (pAtt->i_getControllerName() == aName)
9693 atts.push_back(pAtt);
9694 }
9695
9696 return S_OK;
9697}
9698
9699
9700/**
9701 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9702 * file if the machine name was changed and about creating a new settings file
9703 * if this is a new machine.
9704 *
9705 * @note Must be never called directly but only from #saveSettings().
9706 */
9707HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9708{
9709 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9710
9711 HRESULT rc = S_OK;
9712
9713 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9714
9715 /// @todo need to handle primary group change, too
9716
9717 /* attempt to rename the settings file if machine name is changed */
9718 if ( mUserData->s.fNameSync
9719 && mUserData.isBackedUp()
9720 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9721 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9722 )
9723 {
9724 bool dirRenamed = false;
9725 bool fileRenamed = false;
9726
9727 Utf8Str configFile, newConfigFile;
9728 Utf8Str configFilePrev, newConfigFilePrev;
9729 Utf8Str configDir, newConfigDir;
9730
9731 do
9732 {
9733 int vrc = VINF_SUCCESS;
9734
9735 Utf8Str name = mUserData.backedUpData()->s.strName;
9736 Utf8Str newName = mUserData->s.strName;
9737 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9738 if (group == "/")
9739 group.setNull();
9740 Utf8Str newGroup = mUserData->s.llGroups.front();
9741 if (newGroup == "/")
9742 newGroup.setNull();
9743
9744 configFile = mData->m_strConfigFileFull;
9745
9746 /* first, rename the directory if it matches the group and machine name */
9747 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9748 group.c_str(), RTPATH_DELIMITER, name.c_str());
9749 /** @todo hack, make somehow use of ComposeMachineFilename */
9750 if (mUserData->s.fDirectoryIncludesUUID)
9751 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9752 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9753 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9754 /** @todo hack, make somehow use of ComposeMachineFilename */
9755 if (mUserData->s.fDirectoryIncludesUUID)
9756 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9757 configDir = configFile;
9758 configDir.stripFilename();
9759 newConfigDir = configDir;
9760 if ( configDir.length() >= groupPlusName.length()
9761 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9762 groupPlusName.c_str()))
9763 {
9764 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9765 Utf8Str newConfigBaseDir(newConfigDir);
9766 newConfigDir.append(newGroupPlusName);
9767 /* consistency: use \ if appropriate on the platform */
9768 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9769 /* new dir and old dir cannot be equal here because of 'if'
9770 * above and because name != newName */
9771 Assert(configDir != newConfigDir);
9772 if (!fSettingsFileIsNew)
9773 {
9774 /* perform real rename only if the machine is not new */
9775 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9776 if ( vrc == VERR_FILE_NOT_FOUND
9777 || vrc == VERR_PATH_NOT_FOUND)
9778 {
9779 /* create the parent directory, then retry renaming */
9780 Utf8Str parent(newConfigDir);
9781 parent.stripFilename();
9782 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9783 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9784 }
9785 if (RT_FAILURE(vrc))
9786 {
9787 rc = setErrorBoth(E_FAIL, vrc,
9788 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9789 configDir.c_str(),
9790 newConfigDir.c_str(),
9791 vrc);
9792 break;
9793 }
9794 /* delete subdirectories which are no longer needed */
9795 Utf8Str dir(configDir);
9796 dir.stripFilename();
9797 while (dir != newConfigBaseDir && dir != ".")
9798 {
9799 vrc = RTDirRemove(dir.c_str());
9800 if (RT_FAILURE(vrc))
9801 break;
9802 dir.stripFilename();
9803 }
9804 dirRenamed = true;
9805 }
9806 }
9807
9808 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9809 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9810
9811 /* then try to rename the settings file itself */
9812 if (newConfigFile != configFile)
9813 {
9814 /* get the path to old settings file in renamed directory */
9815 configFile = Utf8StrFmt("%s%c%s",
9816 newConfigDir.c_str(),
9817 RTPATH_DELIMITER,
9818 RTPathFilename(configFile.c_str()));
9819 if (!fSettingsFileIsNew)
9820 {
9821 /* perform real rename only if the machine is not new */
9822 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9823 if (RT_FAILURE(vrc))
9824 {
9825 rc = setErrorBoth(E_FAIL, vrc,
9826 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9827 configFile.c_str(),
9828 newConfigFile.c_str(),
9829 vrc);
9830 break;
9831 }
9832 fileRenamed = true;
9833 configFilePrev = configFile;
9834 configFilePrev += "-prev";
9835 newConfigFilePrev = newConfigFile;
9836 newConfigFilePrev += "-prev";
9837 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9838 }
9839 }
9840
9841 // update m_strConfigFileFull amd mConfigFile
9842 mData->m_strConfigFileFull = newConfigFile;
9843 // compute the relative path too
9844 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9845
9846 // store the old and new so that VirtualBox::i_saveSettings() can update
9847 // the media registry
9848 if ( mData->mRegistered
9849 && (configDir != newConfigDir || configFile != newConfigFile))
9850 {
9851 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9852
9853 if (pfNeedsGlobalSaveSettings)
9854 *pfNeedsGlobalSaveSettings = true;
9855 }
9856
9857 // in the saved state file path, replace the old directory with the new directory
9858 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9859 {
9860 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9861 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9862 }
9863
9864 // and do the same thing for the saved state file paths of all the online snapshots
9865 if (mData->mFirstSnapshot)
9866 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9867 newConfigDir.c_str());
9868 }
9869 while (0);
9870
9871 if (FAILED(rc))
9872 {
9873 /* silently try to rename everything back */
9874 if (fileRenamed)
9875 {
9876 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9877 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9878 }
9879 if (dirRenamed)
9880 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9881 }
9882
9883 if (FAILED(rc)) return rc;
9884 }
9885
9886 if (fSettingsFileIsNew)
9887 {
9888 /* create a virgin config file */
9889 int vrc = VINF_SUCCESS;
9890
9891 /* ensure the settings directory exists */
9892 Utf8Str path(mData->m_strConfigFileFull);
9893 path.stripFilename();
9894 if (!RTDirExists(path.c_str()))
9895 {
9896 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9897 if (RT_FAILURE(vrc))
9898 {
9899 return setErrorBoth(E_FAIL, vrc,
9900 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9901 path.c_str(),
9902 vrc);
9903 }
9904 }
9905
9906 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9907 path = Utf8Str(mData->m_strConfigFileFull);
9908 RTFILE f = NIL_RTFILE;
9909 vrc = RTFileOpen(&f, path.c_str(),
9910 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9911 if (RT_FAILURE(vrc))
9912 return setErrorBoth(E_FAIL, vrc,
9913 tr("Could not create the settings file '%s' (%Rrc)"),
9914 path.c_str(),
9915 vrc);
9916 RTFileClose(f);
9917 }
9918
9919 return rc;
9920}
9921
9922/**
9923 * Saves and commits machine data, user data and hardware data.
9924 *
9925 * Note that on failure, the data remains uncommitted.
9926 *
9927 * @a aFlags may combine the following flags:
9928 *
9929 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9930 * Used when saving settings after an operation that makes them 100%
9931 * correspond to the settings from the current snapshot.
9932 * - SaveS_Force: settings will be saved without doing a deep compare of the
9933 * settings structures. This is used when this is called because snapshots
9934 * have changed to avoid the overhead of the deep compare.
9935 *
9936 * @note Must be called from under this object's write lock. Locks children for
9937 * writing.
9938 *
9939 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9940 * initialized to false and that will be set to true by this function if
9941 * the caller must invoke VirtualBox::i_saveSettings() because the global
9942 * settings have changed. This will happen if a machine rename has been
9943 * saved and the global machine and media registries will therefore need
9944 * updating.
9945 * @param aFlags Flags.
9946 */
9947HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9948 int aFlags /*= 0*/)
9949{
9950 LogFlowThisFuncEnter();
9951
9952 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9953
9954 /* make sure child objects are unable to modify the settings while we are
9955 * saving them */
9956 i_ensureNoStateDependencies();
9957
9958 AssertReturn(!i_isSnapshotMachine(),
9959 E_FAIL);
9960
9961 if (!mData->mAccessible)
9962 return setError(VBOX_E_INVALID_VM_STATE,
9963 tr("The machine is not accessible, so cannot save settings"));
9964
9965 HRESULT rc = S_OK;
9966 bool fNeedsWrite = false;
9967
9968 /* First, prepare to save settings. It will care about renaming the
9969 * settings directory and file if the machine name was changed and about
9970 * creating a new settings file if this is a new machine. */
9971 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9972 if (FAILED(rc)) return rc;
9973
9974 // keep a pointer to the current settings structures
9975 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9976 settings::MachineConfigFile *pNewConfig = NULL;
9977
9978 try
9979 {
9980 // make a fresh one to have everyone write stuff into
9981 pNewConfig = new settings::MachineConfigFile(NULL);
9982 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9983
9984 // now go and copy all the settings data from COM to the settings structures
9985 // (this calls i_saveSettings() on all the COM objects in the machine)
9986 i_copyMachineDataToSettings(*pNewConfig);
9987
9988 if (aFlags & SaveS_ResetCurStateModified)
9989 {
9990 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9991 mData->mCurrentStateModified = FALSE;
9992 fNeedsWrite = true; // always, no need to compare
9993 }
9994 else if (aFlags & SaveS_Force)
9995 {
9996 fNeedsWrite = true; // always, no need to compare
9997 }
9998 else
9999 {
10000 if (!mData->mCurrentStateModified)
10001 {
10002 // do a deep compare of the settings that we just saved with the settings
10003 // previously stored in the config file; this invokes MachineConfigFile::operator==
10004 // which does a deep compare of all the settings, which is expensive but less expensive
10005 // than writing out XML in vain
10006 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10007
10008 // could still be modified if any settings changed
10009 mData->mCurrentStateModified = fAnySettingsChanged;
10010
10011 fNeedsWrite = fAnySettingsChanged;
10012 }
10013 else
10014 fNeedsWrite = true;
10015 }
10016
10017 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10018
10019 if (fNeedsWrite)
10020 // now spit it all out!
10021 pNewConfig->write(mData->m_strConfigFileFull);
10022
10023 mData->pMachineConfigFile = pNewConfig;
10024 delete pOldConfig;
10025 i_commit();
10026
10027 // after saving settings, we are no longer different from the XML on disk
10028 mData->flModifications = 0;
10029 }
10030 catch (HRESULT err)
10031 {
10032 // we assume that error info is set by the thrower
10033 rc = err;
10034
10035 // restore old config
10036 delete pNewConfig;
10037 mData->pMachineConfigFile = pOldConfig;
10038 }
10039 catch (...)
10040 {
10041 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10042 }
10043
10044 if (fNeedsWrite)
10045 {
10046 /* Fire the data change event, even on failure (since we've already
10047 * committed all data). This is done only for SessionMachines because
10048 * mutable Machine instances are always not registered (i.e. private
10049 * to the client process that creates them) and thus don't need to
10050 * inform callbacks. */
10051 if (i_isSessionMachine())
10052 mParent->i_onMachineDataChange(mData->mUuid);
10053 }
10054
10055 LogFlowThisFunc(("rc=%08X\n", rc));
10056 LogFlowThisFuncLeave();
10057 return rc;
10058}
10059
10060/**
10061 * Implementation for saving the machine settings into the given
10062 * settings::MachineConfigFile instance. This copies machine extradata
10063 * from the previous machine config file in the instance data, if any.
10064 *
10065 * This gets called from two locations:
10066 *
10067 * -- Machine::i_saveSettings(), during the regular XML writing;
10068 *
10069 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10070 * exported to OVF and we write the VirtualBox proprietary XML
10071 * into a <vbox:Machine> tag.
10072 *
10073 * This routine fills all the fields in there, including snapshots, *except*
10074 * for the following:
10075 *
10076 * -- fCurrentStateModified. There is some special logic associated with that.
10077 *
10078 * The caller can then call MachineConfigFile::write() or do something else
10079 * with it.
10080 *
10081 * Caller must hold the machine lock!
10082 *
10083 * This throws XML errors and HRESULT, so the caller must have a catch block!
10084 */
10085void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10086{
10087 // deep copy extradata, being extra careful with self assignment (the STL
10088 // map assignment on Mac OS X clang based Xcode isn't checking)
10089 if (&config != mData->pMachineConfigFile)
10090 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10091
10092 config.uuid = mData->mUuid;
10093
10094 // copy name, description, OS type, teleport, UTC etc.
10095 config.machineUserData = mUserData->s;
10096
10097 if ( mData->mMachineState == MachineState_Saved
10098 || mData->mMachineState == MachineState_Restoring
10099 // when doing certain snapshot operations we may or may not have
10100 // a saved state in the current state, so keep everything as is
10101 || ( ( mData->mMachineState == MachineState_Snapshotting
10102 || mData->mMachineState == MachineState_DeletingSnapshot
10103 || mData->mMachineState == MachineState_RestoringSnapshot)
10104 && (!mSSData->strStateFilePath.isEmpty())
10105 )
10106 )
10107 {
10108 Assert(!mSSData->strStateFilePath.isEmpty());
10109 /* try to make the file name relative to the settings file dir */
10110 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10111 }
10112 else
10113 {
10114 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10115 config.strStateFile.setNull();
10116 }
10117
10118 if (mData->mCurrentSnapshot)
10119 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10120 else
10121 config.uuidCurrentSnapshot.clear();
10122
10123 config.timeLastStateChange = mData->mLastStateChange;
10124 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10125 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10126
10127 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10128 if (FAILED(rc)) throw rc;
10129
10130 // save machine's media registry if this is VirtualBox 4.0 or later
10131 if (config.canHaveOwnMediaRegistry())
10132 {
10133 // determine machine folder
10134 Utf8Str strMachineFolder = i_getSettingsFileFull();
10135 strMachineFolder.stripFilename();
10136 mParent->i_saveMediaRegistry(config.mediaRegistry,
10137 i_getId(), // only media with registry ID == machine UUID
10138 strMachineFolder);
10139 // this throws HRESULT
10140 }
10141
10142 // save snapshots
10143 rc = i_saveAllSnapshots(config);
10144 if (FAILED(rc)) throw rc;
10145}
10146
10147/**
10148 * Saves all snapshots of the machine into the given machine config file. Called
10149 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10150 * @param config
10151 * @return
10152 */
10153HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10154{
10155 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10156
10157 HRESULT rc = S_OK;
10158
10159 try
10160 {
10161 config.llFirstSnapshot.clear();
10162
10163 if (mData->mFirstSnapshot)
10164 {
10165 // the settings use a list for "the first snapshot"
10166 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10167
10168 // get reference to the snapshot on the list and work on that
10169 // element straight in the list to avoid excessive copying later
10170 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10171 if (FAILED(rc)) throw rc;
10172 }
10173
10174// if (mType == IsSessionMachine)
10175// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10176
10177 }
10178 catch (HRESULT err)
10179 {
10180 /* we assume that error info is set by the thrower */
10181 rc = err;
10182 }
10183 catch (...)
10184 {
10185 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10186 }
10187
10188 return rc;
10189}
10190
10191/**
10192 * Saves the VM hardware configuration. It is assumed that the
10193 * given node is empty.
10194 *
10195 * @param data Reference to the settings object for the hardware config.
10196 * @param pDbg Pointer to the settings object for the debugging config
10197 * which happens to live in mHWData.
10198 * @param pAutostart Pointer to the settings object for the autostart config
10199 * which happens to live in mHWData.
10200 */
10201HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10202 settings::Autostart *pAutostart)
10203{
10204 HRESULT rc = S_OK;
10205
10206 try
10207 {
10208 /* The hardware version attribute (optional).
10209 Automatically upgrade from 1 to current default hardware version
10210 when there is no saved state. (ugly!) */
10211 if ( mHWData->mHWVersion == "1"
10212 && mSSData->strStateFilePath.isEmpty()
10213 )
10214 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10215
10216 data.strVersion = mHWData->mHWVersion;
10217 data.uuid = mHWData->mHardwareUUID;
10218
10219 // CPU
10220 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10221 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10222 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10223 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10224 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10225 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10226 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10227 data.fPAE = !!mHWData->mPAEEnabled;
10228 data.enmLongMode = mHWData->mLongMode;
10229 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10230 data.fAPIC = !!mHWData->mAPIC;
10231 data.fX2APIC = !!mHWData->mX2APIC;
10232 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10233 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10234 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10235 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10236 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10237 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10238 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10239 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10240 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10241 data.cCPUs = mHWData->mCPUCount;
10242 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10243 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10244 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10245 data.strCpuProfile = mHWData->mCpuProfile;
10246
10247 data.llCpus.clear();
10248 if (data.fCpuHotPlug)
10249 {
10250 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10251 {
10252 if (mHWData->mCPUAttached[idx])
10253 {
10254 settings::Cpu cpu;
10255 cpu.ulId = idx;
10256 data.llCpus.push_back(cpu);
10257 }
10258 }
10259 }
10260
10261 /* Standard and Extended CPUID leafs. */
10262 data.llCpuIdLeafs.clear();
10263 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10264
10265 // memory
10266 data.ulMemorySizeMB = mHWData->mMemorySize;
10267 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10268
10269 // firmware
10270 data.firmwareType = mHWData->mFirmwareType;
10271
10272 // HID
10273 data.pointingHIDType = mHWData->mPointingHIDType;
10274 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10275
10276 // chipset
10277 data.chipsetType = mHWData->mChipsetType;
10278
10279 // paravirt
10280 data.paravirtProvider = mHWData->mParavirtProvider;
10281 data.strParavirtDebug = mHWData->mParavirtDebug;
10282
10283 // emulated USB card reader
10284 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10285
10286 // HPET
10287 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10288
10289 // boot order
10290 data.mapBootOrder.clear();
10291 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10292 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10293
10294 // display
10295 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10296 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10297 data.cMonitors = mHWData->mMonitorCount;
10298 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10299 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10300
10301 /* VRDEServer settings (optional) */
10302 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10303 if (FAILED(rc)) throw rc;
10304
10305 /* BIOS settings (required) */
10306 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10307 if (FAILED(rc)) throw rc;
10308
10309 /* Recording settings (required) */
10310 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10311 if (FAILED(rc)) throw rc;
10312
10313 /* USB Controller (required) */
10314 data.usbSettings.llUSBControllers.clear();
10315 for (USBControllerList::const_iterator
10316 it = mUSBControllers->begin();
10317 it != mUSBControllers->end();
10318 ++it)
10319 {
10320 ComObjPtr<USBController> ctrl = *it;
10321 settings::USBController settingsCtrl;
10322
10323 settingsCtrl.strName = ctrl->i_getName();
10324 settingsCtrl.enmType = ctrl->i_getControllerType();
10325
10326 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10327 }
10328
10329 /* USB device filters (required) */
10330 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10331 if (FAILED(rc)) throw rc;
10332
10333 /* Network adapters (required) */
10334 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10335 data.llNetworkAdapters.clear();
10336 /* Write out only the nominal number of network adapters for this
10337 * chipset type. Since Machine::commit() hasn't been called there
10338 * may be extra NIC settings in the vector. */
10339 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10340 {
10341 settings::NetworkAdapter nic;
10342 nic.ulSlot = (uint32_t)slot;
10343 /* paranoia check... must not be NULL, but must not crash either. */
10344 if (mNetworkAdapters[slot])
10345 {
10346 if (mNetworkAdapters[slot]->i_hasDefaults())
10347 continue;
10348
10349 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10350 if (FAILED(rc)) throw rc;
10351
10352 data.llNetworkAdapters.push_back(nic);
10353 }
10354 }
10355
10356 /* Serial ports */
10357 data.llSerialPorts.clear();
10358 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10359 {
10360 if (mSerialPorts[slot]->i_hasDefaults())
10361 continue;
10362
10363 settings::SerialPort s;
10364 s.ulSlot = slot;
10365 rc = mSerialPorts[slot]->i_saveSettings(s);
10366 if (FAILED(rc)) return rc;
10367
10368 data.llSerialPorts.push_back(s);
10369 }
10370
10371 /* Parallel ports */
10372 data.llParallelPorts.clear();
10373 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10374 {
10375 if (mParallelPorts[slot]->i_hasDefaults())
10376 continue;
10377
10378 settings::ParallelPort p;
10379 p.ulSlot = slot;
10380 rc = mParallelPorts[slot]->i_saveSettings(p);
10381 if (FAILED(rc)) return rc;
10382
10383 data.llParallelPorts.push_back(p);
10384 }
10385
10386 /* Audio adapter */
10387 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10388 if (FAILED(rc)) return rc;
10389
10390 rc = i_saveStorageControllers(data.storage);
10391 if (FAILED(rc)) return rc;
10392
10393 /* Shared folders */
10394 data.llSharedFolders.clear();
10395 for (HWData::SharedFolderList::const_iterator
10396 it = mHWData->mSharedFolders.begin();
10397 it != mHWData->mSharedFolders.end();
10398 ++it)
10399 {
10400 SharedFolder *pSF = *it;
10401 AutoCaller sfCaller(pSF);
10402 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10403 settings::SharedFolder sf;
10404 sf.strName = pSF->i_getName();
10405 sf.strHostPath = pSF->i_getHostPath();
10406 sf.fWritable = !!pSF->i_isWritable();
10407 sf.fAutoMount = !!pSF->i_isAutoMounted();
10408 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10409
10410 data.llSharedFolders.push_back(sf);
10411 }
10412
10413 // clipboard
10414 data.clipboardMode = mHWData->mClipboardMode;
10415 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10416
10417 // drag'n'drop
10418 data.dndMode = mHWData->mDnDMode;
10419
10420 /* Guest */
10421 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10422
10423 // IO settings
10424 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10425 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10426
10427 /* BandwidthControl (required) */
10428 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10429 if (FAILED(rc)) throw rc;
10430
10431 /* Host PCI devices */
10432 data.pciAttachments.clear();
10433 for (HWData::PCIDeviceAssignmentList::const_iterator
10434 it = mHWData->mPCIDeviceAssignments.begin();
10435 it != mHWData->mPCIDeviceAssignments.end();
10436 ++it)
10437 {
10438 ComObjPtr<PCIDeviceAttachment> pda = *it;
10439 settings::HostPCIDeviceAttachment hpda;
10440
10441 rc = pda->i_saveSettings(hpda);
10442 if (FAILED(rc)) throw rc;
10443
10444 data.pciAttachments.push_back(hpda);
10445 }
10446
10447 // guest properties
10448 data.llGuestProperties.clear();
10449#ifdef VBOX_WITH_GUEST_PROPS
10450 for (HWData::GuestPropertyMap::const_iterator
10451 it = mHWData->mGuestProperties.begin();
10452 it != mHWData->mGuestProperties.end();
10453 ++it)
10454 {
10455 HWData::GuestProperty property = it->second;
10456
10457 /* Remove transient guest properties at shutdown unless we
10458 * are saving state. Note that restoring snapshot intentionally
10459 * keeps them, they will be removed if appropriate once the final
10460 * machine state is set (as crashes etc. need to work). */
10461 if ( ( mData->mMachineState == MachineState_PoweredOff
10462 || mData->mMachineState == MachineState_Aborted
10463 || mData->mMachineState == MachineState_Teleported)
10464 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10465 continue;
10466 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10467 prop.strName = it->first;
10468 prop.strValue = property.strValue;
10469 prop.timestamp = property.mTimestamp;
10470 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10471 GuestPropWriteFlags(property.mFlags, szFlags);
10472 prop.strFlags = szFlags;
10473
10474 data.llGuestProperties.push_back(prop);
10475 }
10476
10477 /* I presume this doesn't require a backup(). */
10478 mData->mGuestPropertiesModified = FALSE;
10479#endif /* VBOX_WITH_GUEST_PROPS defined */
10480
10481 *pDbg = mHWData->mDebugging;
10482 *pAutostart = mHWData->mAutostart;
10483
10484 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10485 }
10486 catch (std::bad_alloc &)
10487 {
10488 return E_OUTOFMEMORY;
10489 }
10490
10491 AssertComRC(rc);
10492 return rc;
10493}
10494
10495/**
10496 * Saves the storage controller configuration.
10497 *
10498 * @param data storage settings.
10499 */
10500HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10501{
10502 data.llStorageControllers.clear();
10503
10504 for (StorageControllerList::const_iterator
10505 it = mStorageControllers->begin();
10506 it != mStorageControllers->end();
10507 ++it)
10508 {
10509 HRESULT rc;
10510 ComObjPtr<StorageController> pCtl = *it;
10511
10512 settings::StorageController ctl;
10513 ctl.strName = pCtl->i_getName();
10514 ctl.controllerType = pCtl->i_getControllerType();
10515 ctl.storageBus = pCtl->i_getStorageBus();
10516 ctl.ulInstance = pCtl->i_getInstance();
10517 ctl.fBootable = pCtl->i_getBootable();
10518
10519 /* Save the port count. */
10520 ULONG portCount;
10521 rc = pCtl->COMGETTER(PortCount)(&portCount);
10522 ComAssertComRCRet(rc, rc);
10523 ctl.ulPortCount = portCount;
10524
10525 /* Save fUseHostIOCache */
10526 BOOL fUseHostIOCache;
10527 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10528 ComAssertComRCRet(rc, rc);
10529 ctl.fUseHostIOCache = !!fUseHostIOCache;
10530
10531 /* save the devices now. */
10532 rc = i_saveStorageDevices(pCtl, ctl);
10533 ComAssertComRCRet(rc, rc);
10534
10535 data.llStorageControllers.push_back(ctl);
10536 }
10537
10538 return S_OK;
10539}
10540
10541/**
10542 * Saves the hard disk configuration.
10543 */
10544HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10545 settings::StorageController &data)
10546{
10547 MediumAttachmentList atts;
10548
10549 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10550 if (FAILED(rc)) return rc;
10551
10552 data.llAttachedDevices.clear();
10553 for (MediumAttachmentList::const_iterator
10554 it = atts.begin();
10555 it != atts.end();
10556 ++it)
10557 {
10558 settings::AttachedDevice dev;
10559 IMediumAttachment *iA = *it;
10560 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10561 Medium *pMedium = pAttach->i_getMedium();
10562
10563 dev.deviceType = pAttach->i_getType();
10564 dev.lPort = pAttach->i_getPort();
10565 dev.lDevice = pAttach->i_getDevice();
10566 dev.fPassThrough = pAttach->i_getPassthrough();
10567 dev.fHotPluggable = pAttach->i_getHotPluggable();
10568 if (pMedium)
10569 {
10570 if (pMedium->i_isHostDrive())
10571 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10572 else
10573 dev.uuid = pMedium->i_getId();
10574 dev.fTempEject = pAttach->i_getTempEject();
10575 dev.fNonRotational = pAttach->i_getNonRotational();
10576 dev.fDiscard = pAttach->i_getDiscard();
10577 }
10578
10579 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10580
10581 data.llAttachedDevices.push_back(dev);
10582 }
10583
10584 return S_OK;
10585}
10586
10587/**
10588 * Saves machine state settings as defined by aFlags
10589 * (SaveSTS_* values).
10590 *
10591 * @param aFlags Combination of SaveSTS_* flags.
10592 *
10593 * @note Locks objects for writing.
10594 */
10595HRESULT Machine::i_saveStateSettings(int aFlags)
10596{
10597 if (aFlags == 0)
10598 return S_OK;
10599
10600 AutoCaller autoCaller(this);
10601 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10602
10603 /* This object's write lock is also necessary to serialize file access
10604 * (prevent concurrent reads and writes) */
10605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10606
10607 HRESULT rc = S_OK;
10608
10609 Assert(mData->pMachineConfigFile);
10610
10611 try
10612 {
10613 if (aFlags & SaveSTS_CurStateModified)
10614 mData->pMachineConfigFile->fCurrentStateModified = true;
10615
10616 if (aFlags & SaveSTS_StateFilePath)
10617 {
10618 if (!mSSData->strStateFilePath.isEmpty())
10619 /* try to make the file name relative to the settings file dir */
10620 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10621 else
10622 mData->pMachineConfigFile->strStateFile.setNull();
10623 }
10624
10625 if (aFlags & SaveSTS_StateTimeStamp)
10626 {
10627 Assert( mData->mMachineState != MachineState_Aborted
10628 || mSSData->strStateFilePath.isEmpty());
10629
10630 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10631
10632 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10633/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10634 }
10635
10636 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10637 }
10638 catch (...)
10639 {
10640 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10641 }
10642
10643 return rc;
10644}
10645
10646/**
10647 * Ensures that the given medium is added to a media registry. If this machine
10648 * was created with 4.0 or later, then the machine registry is used. Otherwise
10649 * the global VirtualBox media registry is used.
10650 *
10651 * Caller must NOT hold machine lock, media tree or any medium locks!
10652 *
10653 * @param pMedium
10654 */
10655void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10656{
10657 /* Paranoia checks: do not hold machine or media tree locks. */
10658 AssertReturnVoid(!isWriteLockOnCurrentThread());
10659 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10660
10661 ComObjPtr<Medium> pBase;
10662 {
10663 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10664 pBase = pMedium->i_getBase();
10665 }
10666
10667 /* Paranoia checks: do not hold medium locks. */
10668 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10669 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10670
10671 // decide which medium registry to use now that the medium is attached:
10672 Guid uuid;
10673 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10674 if (fCanHaveOwnMediaRegistry)
10675 // machine XML is VirtualBox 4.0 or higher:
10676 uuid = i_getId(); // machine UUID
10677 else
10678 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10679
10680 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10681 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10682 if (pMedium->i_addRegistry(uuid))
10683 mParent->i_markRegistryModified(uuid);
10684
10685 /* For more complex hard disk structures it can happen that the base
10686 * medium isn't yet associated with any medium registry. Do that now. */
10687 if (pMedium != pBase)
10688 {
10689 /* Tree lock needed by Medium::addRegistry when recursing. */
10690 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10691 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10692 {
10693 treeLock.release();
10694 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10695 treeLock.acquire();
10696 }
10697 if (pBase->i_addRegistryRecursive(uuid))
10698 {
10699 treeLock.release();
10700 mParent->i_markRegistryModified(uuid);
10701 }
10702 }
10703}
10704
10705/**
10706 * Creates differencing hard disks for all normal hard disks attached to this
10707 * machine and a new set of attachments to refer to created disks.
10708 *
10709 * Used when taking a snapshot or when deleting the current state. Gets called
10710 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10711 *
10712 * This method assumes that mMediumAttachments contains the original hard disk
10713 * attachments it needs to create diffs for. On success, these attachments will
10714 * be replaced with the created diffs.
10715 *
10716 * Attachments with non-normal hard disks are left as is.
10717 *
10718 * If @a aOnline is @c false then the original hard disks that require implicit
10719 * diffs will be locked for reading. Otherwise it is assumed that they are
10720 * already locked for writing (when the VM was started). Note that in the latter
10721 * case it is responsibility of the caller to lock the newly created diffs for
10722 * writing if this method succeeds.
10723 *
10724 * @param aProgress Progress object to run (must contain at least as
10725 * many operations left as the number of hard disks
10726 * attached).
10727 * @param aWeight Weight of this operation.
10728 * @param aOnline Whether the VM was online prior to this operation.
10729 *
10730 * @note The progress object is not marked as completed, neither on success nor
10731 * on failure. This is a responsibility of the caller.
10732 *
10733 * @note Locks this object and the media tree for writing.
10734 */
10735HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10736 ULONG aWeight,
10737 bool aOnline)
10738{
10739 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10740
10741 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10742 AssertReturn(!!pProgressControl, E_INVALIDARG);
10743
10744 AutoCaller autoCaller(this);
10745 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10746
10747 AutoMultiWriteLock2 alock(this->lockHandle(),
10748 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10749
10750 /* must be in a protective state because we release the lock below */
10751 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10752 || mData->mMachineState == MachineState_OnlineSnapshotting
10753 || mData->mMachineState == MachineState_LiveSnapshotting
10754 || mData->mMachineState == MachineState_RestoringSnapshot
10755 || mData->mMachineState == MachineState_DeletingSnapshot
10756 , E_FAIL);
10757
10758 HRESULT rc = S_OK;
10759
10760 // use appropriate locked media map (online or offline)
10761 MediumLockListMap lockedMediaOffline;
10762 MediumLockListMap *lockedMediaMap;
10763 if (aOnline)
10764 lockedMediaMap = &mData->mSession.mLockedMedia;
10765 else
10766 lockedMediaMap = &lockedMediaOffline;
10767
10768 try
10769 {
10770 if (!aOnline)
10771 {
10772 /* lock all attached hard disks early to detect "in use"
10773 * situations before creating actual diffs */
10774 for (MediumAttachmentList::const_iterator
10775 it = mMediumAttachments->begin();
10776 it != mMediumAttachments->end();
10777 ++it)
10778 {
10779 MediumAttachment *pAtt = *it;
10780 if (pAtt->i_getType() == DeviceType_HardDisk)
10781 {
10782 Medium *pMedium = pAtt->i_getMedium();
10783 Assert(pMedium);
10784
10785 MediumLockList *pMediumLockList(new MediumLockList());
10786 alock.release();
10787 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10788 NULL /* pToLockWrite */,
10789 false /* fMediumLockWriteAll */,
10790 NULL,
10791 *pMediumLockList);
10792 alock.acquire();
10793 if (FAILED(rc))
10794 {
10795 delete pMediumLockList;
10796 throw rc;
10797 }
10798 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10799 if (FAILED(rc))
10800 {
10801 throw setError(rc,
10802 tr("Collecting locking information for all attached media failed"));
10803 }
10804 }
10805 }
10806
10807 /* Now lock all media. If this fails, nothing is locked. */
10808 alock.release();
10809 rc = lockedMediaMap->Lock();
10810 alock.acquire();
10811 if (FAILED(rc))
10812 {
10813 throw setError(rc,
10814 tr("Locking of attached media failed"));
10815 }
10816 }
10817
10818 /* remember the current list (note that we don't use backup() since
10819 * mMediumAttachments may be already backed up) */
10820 MediumAttachmentList atts = *mMediumAttachments.data();
10821
10822 /* start from scratch */
10823 mMediumAttachments->clear();
10824
10825 /* go through remembered attachments and create diffs for normal hard
10826 * disks and attach them */
10827 for (MediumAttachmentList::const_iterator
10828 it = atts.begin();
10829 it != atts.end();
10830 ++it)
10831 {
10832 MediumAttachment *pAtt = *it;
10833
10834 DeviceType_T devType = pAtt->i_getType();
10835 Medium *pMedium = pAtt->i_getMedium();
10836
10837 if ( devType != DeviceType_HardDisk
10838 || pMedium == NULL
10839 || pMedium->i_getType() != MediumType_Normal)
10840 {
10841 /* copy the attachment as is */
10842
10843 /** @todo the progress object created in SessionMachine::TakeSnaphot
10844 * only expects operations for hard disks. Later other
10845 * device types need to show up in the progress as well. */
10846 if (devType == DeviceType_HardDisk)
10847 {
10848 if (pMedium == NULL)
10849 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10850 aWeight); // weight
10851 else
10852 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10853 pMedium->i_getBase()->i_getName().c_str()).raw(),
10854 aWeight); // weight
10855 }
10856
10857 mMediumAttachments->push_back(pAtt);
10858 continue;
10859 }
10860
10861 /* need a diff */
10862 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10863 pMedium->i_getBase()->i_getName().c_str()).raw(),
10864 aWeight); // weight
10865
10866 Utf8Str strFullSnapshotFolder;
10867 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10868
10869 ComObjPtr<Medium> diff;
10870 diff.createObject();
10871 // store the diff in the same registry as the parent
10872 // (this cannot fail here because we can't create implicit diffs for
10873 // unregistered images)
10874 Guid uuidRegistryParent;
10875 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10876 Assert(fInRegistry); NOREF(fInRegistry);
10877 rc = diff->init(mParent,
10878 pMedium->i_getPreferredDiffFormat(),
10879 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10880 uuidRegistryParent,
10881 DeviceType_HardDisk);
10882 if (FAILED(rc)) throw rc;
10883
10884 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10885 * the push_back? Looks like we're going to release medium with the
10886 * wrong kind of lock (general issue with if we fail anywhere at all)
10887 * and an orphaned VDI in the snapshots folder. */
10888
10889 /* update the appropriate lock list */
10890 MediumLockList *pMediumLockList;
10891 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10892 AssertComRCThrowRC(rc);
10893 if (aOnline)
10894 {
10895 alock.release();
10896 /* The currently attached medium will be read-only, change
10897 * the lock type to read. */
10898 rc = pMediumLockList->Update(pMedium, false);
10899 alock.acquire();
10900 AssertComRCThrowRC(rc);
10901 }
10902
10903 /* release the locks before the potentially lengthy operation */
10904 alock.release();
10905 rc = pMedium->i_createDiffStorage(diff,
10906 pMedium->i_getPreferredDiffVariant(),
10907 pMediumLockList,
10908 NULL /* aProgress */,
10909 true /* aWait */,
10910 false /* aNotify */);
10911 alock.acquire();
10912 if (FAILED(rc)) throw rc;
10913
10914 /* actual lock list update is done in Machine::i_commitMedia */
10915
10916 rc = diff->i_addBackReference(mData->mUuid);
10917 AssertComRCThrowRC(rc);
10918
10919 /* add a new attachment */
10920 ComObjPtr<MediumAttachment> attachment;
10921 attachment.createObject();
10922 rc = attachment->init(this,
10923 diff,
10924 pAtt->i_getControllerName(),
10925 pAtt->i_getPort(),
10926 pAtt->i_getDevice(),
10927 DeviceType_HardDisk,
10928 true /* aImplicit */,
10929 false /* aPassthrough */,
10930 false /* aTempEject */,
10931 pAtt->i_getNonRotational(),
10932 pAtt->i_getDiscard(),
10933 pAtt->i_getHotPluggable(),
10934 pAtt->i_getBandwidthGroup());
10935 if (FAILED(rc)) throw rc;
10936
10937 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10938 AssertComRCThrowRC(rc);
10939 mMediumAttachments->push_back(attachment);
10940 }
10941 }
10942 catch (HRESULT aRC) { rc = aRC; }
10943
10944 /* unlock all hard disks we locked when there is no VM */
10945 if (!aOnline)
10946 {
10947 ErrorInfoKeeper eik;
10948
10949 HRESULT rc1 = lockedMediaMap->Clear();
10950 AssertComRC(rc1);
10951 }
10952
10953 return rc;
10954}
10955
10956/**
10957 * Deletes implicit differencing hard disks created either by
10958 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10959 * mMediumAttachments.
10960 *
10961 * Note that to delete hard disks created by #attachDevice() this method is
10962 * called from #i_rollbackMedia() when the changes are rolled back.
10963 *
10964 * @note Locks this object and the media tree for writing.
10965 */
10966HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10967{
10968 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10969
10970 AutoCaller autoCaller(this);
10971 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10972
10973 AutoMultiWriteLock2 alock(this->lockHandle(),
10974 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10975
10976 /* We absolutely must have backed up state. */
10977 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10978
10979 /* Check if there are any implicitly created diff images. */
10980 bool fImplicitDiffs = false;
10981 for (MediumAttachmentList::const_iterator
10982 it = mMediumAttachments->begin();
10983 it != mMediumAttachments->end();
10984 ++it)
10985 {
10986 const ComObjPtr<MediumAttachment> &pAtt = *it;
10987 if (pAtt->i_isImplicit())
10988 {
10989 fImplicitDiffs = true;
10990 break;
10991 }
10992 }
10993 /* If there is nothing to do, leave early. This saves lots of image locking
10994 * effort. It also avoids a MachineStateChanged event without real reason.
10995 * This is important e.g. when loading a VM config, because there should be
10996 * no events. Otherwise API clients can become thoroughly confused for
10997 * inaccessible VMs (the code for loading VM configs uses this method for
10998 * cleanup if the config makes no sense), as they take such events as an
10999 * indication that the VM is alive, and they would force the VM config to
11000 * be reread, leading to an endless loop. */
11001 if (!fImplicitDiffs)
11002 return S_OK;
11003
11004 HRESULT rc = S_OK;
11005 MachineState_T oldState = mData->mMachineState;
11006
11007 /* will release the lock before the potentially lengthy operation,
11008 * so protect with the special state (unless already protected) */
11009 if ( oldState != MachineState_Snapshotting
11010 && oldState != MachineState_OnlineSnapshotting
11011 && oldState != MachineState_LiveSnapshotting
11012 && oldState != MachineState_RestoringSnapshot
11013 && oldState != MachineState_DeletingSnapshot
11014 && oldState != MachineState_DeletingSnapshotOnline
11015 && oldState != MachineState_DeletingSnapshotPaused
11016 )
11017 i_setMachineState(MachineState_SettingUp);
11018
11019 // use appropriate locked media map (online or offline)
11020 MediumLockListMap lockedMediaOffline;
11021 MediumLockListMap *lockedMediaMap;
11022 if (aOnline)
11023 lockedMediaMap = &mData->mSession.mLockedMedia;
11024 else
11025 lockedMediaMap = &lockedMediaOffline;
11026
11027 try
11028 {
11029 if (!aOnline)
11030 {
11031 /* lock all attached hard disks early to detect "in use"
11032 * situations before deleting actual diffs */
11033 for (MediumAttachmentList::const_iterator
11034 it = mMediumAttachments->begin();
11035 it != mMediumAttachments->end();
11036 ++it)
11037 {
11038 MediumAttachment *pAtt = *it;
11039 if (pAtt->i_getType() == DeviceType_HardDisk)
11040 {
11041 Medium *pMedium = pAtt->i_getMedium();
11042 Assert(pMedium);
11043
11044 MediumLockList *pMediumLockList(new MediumLockList());
11045 alock.release();
11046 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11047 NULL /* pToLockWrite */,
11048 false /* fMediumLockWriteAll */,
11049 NULL,
11050 *pMediumLockList);
11051 alock.acquire();
11052
11053 if (FAILED(rc))
11054 {
11055 delete pMediumLockList;
11056 throw rc;
11057 }
11058
11059 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11060 if (FAILED(rc))
11061 throw rc;
11062 }
11063 }
11064
11065 if (FAILED(rc))
11066 throw rc;
11067 } // end of offline
11068
11069 /* Lock lists are now up to date and include implicitly created media */
11070
11071 /* Go through remembered attachments and delete all implicitly created
11072 * diffs and fix up the attachment information */
11073 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11074 MediumAttachmentList implicitAtts;
11075 for (MediumAttachmentList::const_iterator
11076 it = mMediumAttachments->begin();
11077 it != mMediumAttachments->end();
11078 ++it)
11079 {
11080 ComObjPtr<MediumAttachment> pAtt = *it;
11081 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11082 if (pMedium.isNull())
11083 continue;
11084
11085 // Implicit attachments go on the list for deletion and back references are removed.
11086 if (pAtt->i_isImplicit())
11087 {
11088 /* Deassociate and mark for deletion */
11089 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11090 rc = pMedium->i_removeBackReference(mData->mUuid);
11091 if (FAILED(rc))
11092 throw rc;
11093 implicitAtts.push_back(pAtt);
11094 continue;
11095 }
11096
11097 /* Was this medium attached before? */
11098 if (!i_findAttachment(oldAtts, pMedium))
11099 {
11100 /* no: de-associate */
11101 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11102 rc = pMedium->i_removeBackReference(mData->mUuid);
11103 if (FAILED(rc))
11104 throw rc;
11105 continue;
11106 }
11107 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11108 }
11109
11110 /* If there are implicit attachments to delete, throw away the lock
11111 * map contents (which will unlock all media) since the medium
11112 * attachments will be rolled back. Below we need to completely
11113 * recreate the lock map anyway since it is infinitely complex to
11114 * do this incrementally (would need reconstructing each attachment
11115 * change, which would be extremely hairy). */
11116 if (implicitAtts.size() != 0)
11117 {
11118 ErrorInfoKeeper eik;
11119
11120 HRESULT rc1 = lockedMediaMap->Clear();
11121 AssertComRC(rc1);
11122 }
11123
11124 /* rollback hard disk changes */
11125 mMediumAttachments.rollback();
11126
11127 MultiResult mrc(S_OK);
11128
11129 // Delete unused implicit diffs.
11130 if (implicitAtts.size() != 0)
11131 {
11132 alock.release();
11133
11134 for (MediumAttachmentList::const_iterator
11135 it = implicitAtts.begin();
11136 it != implicitAtts.end();
11137 ++it)
11138 {
11139 // Remove medium associated with this attachment.
11140 ComObjPtr<MediumAttachment> pAtt = *it;
11141 Assert(pAtt);
11142 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11143 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11144 Assert(pMedium);
11145
11146 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11147 // continue on delete failure, just collect error messages
11148 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11149 pMedium->i_getLocationFull().c_str() ));
11150 mrc = rc;
11151 }
11152 // Clear the list of deleted implicit attachments now, while not
11153 // holding the lock, as it will ultimately trigger Medium::uninit()
11154 // calls which assume that the media tree lock isn't held.
11155 implicitAtts.clear();
11156
11157 alock.acquire();
11158
11159 /* if there is a VM recreate media lock map as mentioned above,
11160 * otherwise it is a waste of time and we leave things unlocked */
11161 if (aOnline)
11162 {
11163 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11164 /* must never be NULL, but better safe than sorry */
11165 if (!pMachine.isNull())
11166 {
11167 alock.release();
11168 rc = mData->mSession.mMachine->i_lockMedia();
11169 alock.acquire();
11170 if (FAILED(rc))
11171 throw rc;
11172 }
11173 }
11174 }
11175 }
11176 catch (HRESULT aRC) {rc = aRC;}
11177
11178 if (mData->mMachineState == MachineState_SettingUp)
11179 i_setMachineState(oldState);
11180
11181 /* unlock all hard disks we locked when there is no VM */
11182 if (!aOnline)
11183 {
11184 ErrorInfoKeeper eik;
11185
11186 HRESULT rc1 = lockedMediaMap->Clear();
11187 AssertComRC(rc1);
11188 }
11189
11190 return rc;
11191}
11192
11193
11194/**
11195 * Looks through the given list of media attachments for one with the given parameters
11196 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11197 * can be searched as well if needed.
11198 *
11199 * @param ll
11200 * @param aControllerName
11201 * @param aControllerPort
11202 * @param aDevice
11203 * @return
11204 */
11205MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11206 const Utf8Str &aControllerName,
11207 LONG aControllerPort,
11208 LONG aDevice)
11209{
11210 for (MediumAttachmentList::const_iterator
11211 it = ll.begin();
11212 it != ll.end();
11213 ++it)
11214 {
11215 MediumAttachment *pAttach = *it;
11216 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11217 return pAttach;
11218 }
11219
11220 return NULL;
11221}
11222
11223/**
11224 * Looks through the given list of media attachments for one with the given parameters
11225 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11226 * can be searched as well if needed.
11227 *
11228 * @param ll
11229 * @param pMedium
11230 * @return
11231 */
11232MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11233 ComObjPtr<Medium> pMedium)
11234{
11235 for (MediumAttachmentList::const_iterator
11236 it = ll.begin();
11237 it != ll.end();
11238 ++it)
11239 {
11240 MediumAttachment *pAttach = *it;
11241 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11242 if (pMediumThis == pMedium)
11243 return pAttach;
11244 }
11245
11246 return NULL;
11247}
11248
11249/**
11250 * Looks through the given list of media attachments for one with the given parameters
11251 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11252 * can be searched as well if needed.
11253 *
11254 * @param ll
11255 * @param id
11256 * @return
11257 */
11258MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11259 Guid &id)
11260{
11261 for (MediumAttachmentList::const_iterator
11262 it = ll.begin();
11263 it != ll.end();
11264 ++it)
11265 {
11266 MediumAttachment *pAttach = *it;
11267 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11268 if (pMediumThis->i_getId() == id)
11269 return pAttach;
11270 }
11271
11272 return NULL;
11273}
11274
11275/**
11276 * Main implementation for Machine::DetachDevice. This also gets called
11277 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11278 *
11279 * @param pAttach Medium attachment to detach.
11280 * @param writeLock Machine write lock which the caller must have locked once.
11281 * This may be released temporarily in here.
11282 * @param pSnapshot If NULL, then the detachment is for the current machine.
11283 * Otherwise this is for a SnapshotMachine, and this must be
11284 * its snapshot.
11285 * @return
11286 */
11287HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11288 AutoWriteLock &writeLock,
11289 Snapshot *pSnapshot)
11290{
11291 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11292 DeviceType_T mediumType = pAttach->i_getType();
11293
11294 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11295
11296 if (pAttach->i_isImplicit())
11297 {
11298 /* attempt to implicitly delete the implicitly created diff */
11299
11300 /// @todo move the implicit flag from MediumAttachment to Medium
11301 /// and forbid any hard disk operation when it is implicit. Or maybe
11302 /// a special media state for it to make it even more simple.
11303
11304 Assert(mMediumAttachments.isBackedUp());
11305
11306 /* will release the lock before the potentially lengthy operation, so
11307 * protect with the special state */
11308 MachineState_T oldState = mData->mMachineState;
11309 i_setMachineState(MachineState_SettingUp);
11310
11311 writeLock.release();
11312
11313 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11314 true /*aWait*/,
11315 false /*aNotify*/);
11316
11317 writeLock.acquire();
11318
11319 i_setMachineState(oldState);
11320
11321 if (FAILED(rc)) return rc;
11322 }
11323
11324 i_setModified(IsModified_Storage);
11325 mMediumAttachments.backup();
11326 mMediumAttachments->remove(pAttach);
11327
11328 if (!oldmedium.isNull())
11329 {
11330 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11331 if (pSnapshot)
11332 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11333 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11334 else if (mediumType != DeviceType_HardDisk)
11335 oldmedium->i_removeBackReference(mData->mUuid);
11336 }
11337
11338 return S_OK;
11339}
11340
11341/**
11342 * Goes thru all media of the given list and
11343 *
11344 * 1) calls i_detachDevice() on each of them for this machine and
11345 * 2) adds all Medium objects found in the process to the given list,
11346 * depending on cleanupMode.
11347 *
11348 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11349 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11350 * media to the list.
11351 *
11352 * This gets called from Machine::Unregister, both for the actual Machine and
11353 * the SnapshotMachine objects that might be found in the snapshots.
11354 *
11355 * Requires caller and locking. The machine lock must be passed in because it
11356 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11357 *
11358 * @param writeLock Machine lock from top-level caller; this gets passed to
11359 * i_detachDevice.
11360 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11361 * object if called for a SnapshotMachine.
11362 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11363 * added to llMedia; if Full, then all media get added;
11364 * otherwise no media get added.
11365 * @param llMedia Caller's list to receive Medium objects which got detached so
11366 * caller can close() them, depending on cleanupMode.
11367 * @return
11368 */
11369HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11370 Snapshot *pSnapshot,
11371 CleanupMode_T cleanupMode,
11372 MediaList &llMedia)
11373{
11374 Assert(isWriteLockOnCurrentThread());
11375
11376 HRESULT rc;
11377
11378 // make a temporary list because i_detachDevice invalidates iterators into
11379 // mMediumAttachments
11380 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11381
11382 for (MediumAttachmentList::iterator
11383 it = llAttachments2.begin();
11384 it != llAttachments2.end();
11385 ++it)
11386 {
11387 ComObjPtr<MediumAttachment> &pAttach = *it;
11388 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11389
11390 if (!pMedium.isNull())
11391 {
11392 AutoCaller mac(pMedium);
11393 if (FAILED(mac.rc())) return mac.rc();
11394 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11395 DeviceType_T devType = pMedium->i_getDeviceType();
11396 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11397 && devType == DeviceType_HardDisk)
11398 || (cleanupMode == CleanupMode_Full)
11399 )
11400 {
11401 llMedia.push_back(pMedium);
11402 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11403 /* Not allowed to keep this lock as below we need the parent
11404 * medium lock, and the lock order is parent to child. */
11405 lock.release();
11406 /*
11407 * Search for medias which are not attached to any machine, but
11408 * in the chain to an attached disk. Mediums are only consided
11409 * if they are:
11410 * - have only one child
11411 * - no references to any machines
11412 * - are of normal medium type
11413 */
11414 while (!pParent.isNull())
11415 {
11416 AutoCaller mac1(pParent);
11417 if (FAILED(mac1.rc())) return mac1.rc();
11418 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11419 if (pParent->i_getChildren().size() == 1)
11420 {
11421 if ( pParent->i_getMachineBackRefCount() == 0
11422 && pParent->i_getType() == MediumType_Normal
11423 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11424 llMedia.push_back(pParent);
11425 }
11426 else
11427 break;
11428 pParent = pParent->i_getParent();
11429 }
11430 }
11431 }
11432
11433 // real machine: then we need to use the proper method
11434 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11435
11436 if (FAILED(rc))
11437 return rc;
11438 }
11439
11440 return S_OK;
11441}
11442
11443/**
11444 * Perform deferred hard disk detachments.
11445 *
11446 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11447 * changed (not backed up).
11448 *
11449 * If @a aOnline is @c true then this method will also unlock the old hard
11450 * disks for which the new implicit diffs were created and will lock these new
11451 * diffs for writing.
11452 *
11453 * @param aOnline Whether the VM was online prior to this operation.
11454 *
11455 * @note Locks this object for writing!
11456 */
11457void Machine::i_commitMedia(bool aOnline /*= false*/)
11458{
11459 AutoCaller autoCaller(this);
11460 AssertComRCReturnVoid(autoCaller.rc());
11461
11462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11463
11464 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11465
11466 HRESULT rc = S_OK;
11467
11468 /* no attach/detach operations -- nothing to do */
11469 if (!mMediumAttachments.isBackedUp())
11470 return;
11471
11472 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11473 bool fMediaNeedsLocking = false;
11474
11475 /* enumerate new attachments */
11476 for (MediumAttachmentList::const_iterator
11477 it = mMediumAttachments->begin();
11478 it != mMediumAttachments->end();
11479 ++it)
11480 {
11481 MediumAttachment *pAttach = *it;
11482
11483 pAttach->i_commit();
11484
11485 Medium *pMedium = pAttach->i_getMedium();
11486 bool fImplicit = pAttach->i_isImplicit();
11487
11488 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11489 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11490 fImplicit));
11491
11492 /** @todo convert all this Machine-based voodoo to MediumAttachment
11493 * based commit logic. */
11494 if (fImplicit)
11495 {
11496 /* convert implicit attachment to normal */
11497 pAttach->i_setImplicit(false);
11498
11499 if ( aOnline
11500 && pMedium
11501 && pAttach->i_getType() == DeviceType_HardDisk
11502 )
11503 {
11504 /* update the appropriate lock list */
11505 MediumLockList *pMediumLockList;
11506 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11507 AssertComRC(rc);
11508 if (pMediumLockList)
11509 {
11510 /* unlock if there's a need to change the locking */
11511 if (!fMediaNeedsLocking)
11512 {
11513 rc = mData->mSession.mLockedMedia.Unlock();
11514 AssertComRC(rc);
11515 fMediaNeedsLocking = true;
11516 }
11517 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11518 AssertComRC(rc);
11519 rc = pMediumLockList->Append(pMedium, true);
11520 AssertComRC(rc);
11521 }
11522 }
11523
11524 continue;
11525 }
11526
11527 if (pMedium)
11528 {
11529 /* was this medium attached before? */
11530 for (MediumAttachmentList::iterator
11531 oldIt = oldAtts.begin();
11532 oldIt != oldAtts.end();
11533 ++oldIt)
11534 {
11535 MediumAttachment *pOldAttach = *oldIt;
11536 if (pOldAttach->i_getMedium() == pMedium)
11537 {
11538 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11539
11540 /* yes: remove from old to avoid de-association */
11541 oldAtts.erase(oldIt);
11542 break;
11543 }
11544 }
11545 }
11546 }
11547
11548 /* enumerate remaining old attachments and de-associate from the
11549 * current machine state */
11550 for (MediumAttachmentList::const_iterator
11551 it = oldAtts.begin();
11552 it != oldAtts.end();
11553 ++it)
11554 {
11555 MediumAttachment *pAttach = *it;
11556 Medium *pMedium = pAttach->i_getMedium();
11557
11558 /* Detach only hard disks, since DVD/floppy media is detached
11559 * instantly in MountMedium. */
11560 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11561 {
11562 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11563
11564 /* now de-associate from the current machine state */
11565 rc = pMedium->i_removeBackReference(mData->mUuid);
11566 AssertComRC(rc);
11567
11568 if (aOnline)
11569 {
11570 /* unlock since medium is not used anymore */
11571 MediumLockList *pMediumLockList;
11572 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11573 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11574 {
11575 /* this happens for online snapshots, there the attachment
11576 * is changing, but only to a diff image created under
11577 * the old one, so there is no separate lock list */
11578 Assert(!pMediumLockList);
11579 }
11580 else
11581 {
11582 AssertComRC(rc);
11583 if (pMediumLockList)
11584 {
11585 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11586 AssertComRC(rc);
11587 }
11588 }
11589 }
11590 }
11591 }
11592
11593 /* take media locks again so that the locking state is consistent */
11594 if (fMediaNeedsLocking)
11595 {
11596 Assert(aOnline);
11597 rc = mData->mSession.mLockedMedia.Lock();
11598 AssertComRC(rc);
11599 }
11600
11601 /* commit the hard disk changes */
11602 mMediumAttachments.commit();
11603
11604 if (i_isSessionMachine())
11605 {
11606 /*
11607 * Update the parent machine to point to the new owner.
11608 * This is necessary because the stored parent will point to the
11609 * session machine otherwise and cause crashes or errors later
11610 * when the session machine gets invalid.
11611 */
11612 /** @todo Change the MediumAttachment class to behave like any other
11613 * class in this regard by creating peer MediumAttachment
11614 * objects for session machines and share the data with the peer
11615 * machine.
11616 */
11617 for (MediumAttachmentList::const_iterator
11618 it = mMediumAttachments->begin();
11619 it != mMediumAttachments->end();
11620 ++it)
11621 (*it)->i_updateParentMachine(mPeer);
11622
11623 /* attach new data to the primary machine and reshare it */
11624 mPeer->mMediumAttachments.attach(mMediumAttachments);
11625 }
11626
11627 return;
11628}
11629
11630/**
11631 * Perform deferred deletion of implicitly created diffs.
11632 *
11633 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11634 * changed (not backed up).
11635 *
11636 * @note Locks this object for writing!
11637 */
11638void Machine::i_rollbackMedia()
11639{
11640 AutoCaller autoCaller(this);
11641 AssertComRCReturnVoid(autoCaller.rc());
11642
11643 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11644 LogFlowThisFunc(("Entering rollbackMedia\n"));
11645
11646 HRESULT rc = S_OK;
11647
11648 /* no attach/detach operations -- nothing to do */
11649 if (!mMediumAttachments.isBackedUp())
11650 return;
11651
11652 /* enumerate new attachments */
11653 for (MediumAttachmentList::const_iterator
11654 it = mMediumAttachments->begin();
11655 it != mMediumAttachments->end();
11656 ++it)
11657 {
11658 MediumAttachment *pAttach = *it;
11659 /* Fix up the backrefs for DVD/floppy media. */
11660 if (pAttach->i_getType() != DeviceType_HardDisk)
11661 {
11662 Medium *pMedium = pAttach->i_getMedium();
11663 if (pMedium)
11664 {
11665 rc = pMedium->i_removeBackReference(mData->mUuid);
11666 AssertComRC(rc);
11667 }
11668 }
11669
11670 (*it)->i_rollback();
11671
11672 pAttach = *it;
11673 /* Fix up the backrefs for DVD/floppy media. */
11674 if (pAttach->i_getType() != DeviceType_HardDisk)
11675 {
11676 Medium *pMedium = pAttach->i_getMedium();
11677 if (pMedium)
11678 {
11679 rc = pMedium->i_addBackReference(mData->mUuid);
11680 AssertComRC(rc);
11681 }
11682 }
11683 }
11684
11685 /** @todo convert all this Machine-based voodoo to MediumAttachment
11686 * based rollback logic. */
11687 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11688
11689 return;
11690}
11691
11692/**
11693 * Returns true if the settings file is located in the directory named exactly
11694 * as the machine; this means, among other things, that the machine directory
11695 * should be auto-renamed.
11696 *
11697 * @param aSettingsDir if not NULL, the full machine settings file directory
11698 * name will be assigned there.
11699 *
11700 * @note Doesn't lock anything.
11701 * @note Not thread safe (must be called from this object's lock).
11702 */
11703bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11704{
11705 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11706 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11707 if (aSettingsDir)
11708 *aSettingsDir = strMachineDirName;
11709 strMachineDirName.stripPath(); // vmname
11710 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11711 strConfigFileOnly.stripPath() // vmname.vbox
11712 .stripSuffix(); // vmname
11713 /** @todo hack, make somehow use of ComposeMachineFilename */
11714 if (mUserData->s.fDirectoryIncludesUUID)
11715 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11716
11717 AssertReturn(!strMachineDirName.isEmpty(), false);
11718 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11719
11720 return strMachineDirName == strConfigFileOnly;
11721}
11722
11723/**
11724 * Discards all changes to machine settings.
11725 *
11726 * @param aNotify Whether to notify the direct session about changes or not.
11727 *
11728 * @note Locks objects for writing!
11729 */
11730void Machine::i_rollback(bool aNotify)
11731{
11732 AutoCaller autoCaller(this);
11733 AssertComRCReturn(autoCaller.rc(), (void)0);
11734
11735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11736
11737 if (!mStorageControllers.isNull())
11738 {
11739 if (mStorageControllers.isBackedUp())
11740 {
11741 /* unitialize all new devices (absent in the backed up list). */
11742 StorageControllerList *backedList = mStorageControllers.backedUpData();
11743 for (StorageControllerList::const_iterator
11744 it = mStorageControllers->begin();
11745 it != mStorageControllers->end();
11746 ++it)
11747 {
11748 if ( std::find(backedList->begin(), backedList->end(), *it)
11749 == backedList->end()
11750 )
11751 {
11752 (*it)->uninit();
11753 }
11754 }
11755
11756 /* restore the list */
11757 mStorageControllers.rollback();
11758 }
11759
11760 /* rollback any changes to devices after restoring the list */
11761 if (mData->flModifications & IsModified_Storage)
11762 {
11763 for (StorageControllerList::const_iterator
11764 it = mStorageControllers->begin();
11765 it != mStorageControllers->end();
11766 ++it)
11767 {
11768 (*it)->i_rollback();
11769 }
11770 }
11771 }
11772
11773 if (!mUSBControllers.isNull())
11774 {
11775 if (mUSBControllers.isBackedUp())
11776 {
11777 /* unitialize all new devices (absent in the backed up list). */
11778 USBControllerList *backedList = mUSBControllers.backedUpData();
11779 for (USBControllerList::const_iterator
11780 it = mUSBControllers->begin();
11781 it != mUSBControllers->end();
11782 ++it)
11783 {
11784 if ( std::find(backedList->begin(), backedList->end(), *it)
11785 == backedList->end()
11786 )
11787 {
11788 (*it)->uninit();
11789 }
11790 }
11791
11792 /* restore the list */
11793 mUSBControllers.rollback();
11794 }
11795
11796 /* rollback any changes to devices after restoring the list */
11797 if (mData->flModifications & IsModified_USB)
11798 {
11799 for (USBControllerList::const_iterator
11800 it = mUSBControllers->begin();
11801 it != mUSBControllers->end();
11802 ++it)
11803 {
11804 (*it)->i_rollback();
11805 }
11806 }
11807 }
11808
11809 mUserData.rollback();
11810
11811 mHWData.rollback();
11812
11813 if (mData->flModifications & IsModified_Storage)
11814 i_rollbackMedia();
11815
11816 if (mBIOSSettings)
11817 mBIOSSettings->i_rollback();
11818
11819 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11820 mRecordingSettings->i_rollback();
11821
11822 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11823 mVRDEServer->i_rollback();
11824
11825 if (mAudioAdapter)
11826 mAudioAdapter->i_rollback();
11827
11828 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11829 mUSBDeviceFilters->i_rollback();
11830
11831 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11832 mBandwidthControl->i_rollback();
11833
11834 if (!mHWData.isNull())
11835 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11836 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11837 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11838 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11839
11840 if (mData->flModifications & IsModified_NetworkAdapters)
11841 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11842 if ( mNetworkAdapters[slot]
11843 && mNetworkAdapters[slot]->i_isModified())
11844 {
11845 mNetworkAdapters[slot]->i_rollback();
11846 networkAdapters[slot] = mNetworkAdapters[slot];
11847 }
11848
11849 if (mData->flModifications & IsModified_SerialPorts)
11850 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11851 if ( mSerialPorts[slot]
11852 && mSerialPorts[slot]->i_isModified())
11853 {
11854 mSerialPorts[slot]->i_rollback();
11855 serialPorts[slot] = mSerialPorts[slot];
11856 }
11857
11858 if (mData->flModifications & IsModified_ParallelPorts)
11859 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11860 if ( mParallelPorts[slot]
11861 && mParallelPorts[slot]->i_isModified())
11862 {
11863 mParallelPorts[slot]->i_rollback();
11864 parallelPorts[slot] = mParallelPorts[slot];
11865 }
11866
11867 if (aNotify)
11868 {
11869 /* inform the direct session about changes */
11870
11871 ComObjPtr<Machine> that = this;
11872 uint32_t flModifications = mData->flModifications;
11873 alock.release();
11874
11875 if (flModifications & IsModified_SharedFolders)
11876 that->i_onSharedFolderChange();
11877
11878 if (flModifications & IsModified_VRDEServer)
11879 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11880 if (flModifications & IsModified_USB)
11881 that->i_onUSBControllerChange();
11882
11883 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11884 if (networkAdapters[slot])
11885 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11886 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11887 if (serialPorts[slot])
11888 that->i_onSerialPortChange(serialPorts[slot]);
11889 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11890 if (parallelPorts[slot])
11891 that->i_onParallelPortChange(parallelPorts[slot]);
11892
11893 if (flModifications & IsModified_Storage)
11894 {
11895 for (StorageControllerList::const_iterator
11896 it = mStorageControllers->begin();
11897 it != mStorageControllers->end();
11898 ++it)
11899 {
11900 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11901 }
11902 }
11903
11904
11905#if 0
11906 if (flModifications & IsModified_BandwidthControl)
11907 that->onBandwidthControlChange();
11908#endif
11909 }
11910}
11911
11912/**
11913 * Commits all the changes to machine settings.
11914 *
11915 * Note that this operation is supposed to never fail.
11916 *
11917 * @note Locks this object and children for writing.
11918 */
11919void Machine::i_commit()
11920{
11921 AutoCaller autoCaller(this);
11922 AssertComRCReturnVoid(autoCaller.rc());
11923
11924 AutoCaller peerCaller(mPeer);
11925 AssertComRCReturnVoid(peerCaller.rc());
11926
11927 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11928
11929 /*
11930 * use safe commit to ensure Snapshot machines (that share mUserData)
11931 * will still refer to a valid memory location
11932 */
11933 mUserData.commitCopy();
11934
11935 mHWData.commit();
11936
11937 if (mMediumAttachments.isBackedUp())
11938 i_commitMedia(Global::IsOnline(mData->mMachineState));
11939
11940 mBIOSSettings->i_commit();
11941 mRecordingSettings->i_commit();
11942 mVRDEServer->i_commit();
11943 mAudioAdapter->i_commit();
11944 mUSBDeviceFilters->i_commit();
11945 mBandwidthControl->i_commit();
11946
11947 /* Since mNetworkAdapters is a list which might have been changed (resized)
11948 * without using the Backupable<> template we need to handle the copying
11949 * of the list entries manually, including the creation of peers for the
11950 * new objects. */
11951 bool commitNetworkAdapters = false;
11952 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11953 if (mPeer)
11954 {
11955 /* commit everything, even the ones which will go away */
11956 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11957 mNetworkAdapters[slot]->i_commit();
11958 /* copy over the new entries, creating a peer and uninit the original */
11959 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11960 for (size_t slot = 0; slot < newSize; slot++)
11961 {
11962 /* look if this adapter has a peer device */
11963 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11964 if (!peer)
11965 {
11966 /* no peer means the adapter is a newly created one;
11967 * create a peer owning data this data share it with */
11968 peer.createObject();
11969 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11970 }
11971 mPeer->mNetworkAdapters[slot] = peer;
11972 }
11973 /* uninit any no longer needed network adapters */
11974 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11975 mNetworkAdapters[slot]->uninit();
11976 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11977 {
11978 if (mPeer->mNetworkAdapters[slot])
11979 mPeer->mNetworkAdapters[slot]->uninit();
11980 }
11981 /* Keep the original network adapter count until this point, so that
11982 * discarding a chipset type change will not lose settings. */
11983 mNetworkAdapters.resize(newSize);
11984 mPeer->mNetworkAdapters.resize(newSize);
11985 }
11986 else
11987 {
11988 /* we have no peer (our parent is the newly created machine);
11989 * just commit changes to the network adapters */
11990 commitNetworkAdapters = true;
11991 }
11992 if (commitNetworkAdapters)
11993 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11994 mNetworkAdapters[slot]->i_commit();
11995
11996 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11997 mSerialPorts[slot]->i_commit();
11998 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11999 mParallelPorts[slot]->i_commit();
12000
12001 bool commitStorageControllers = false;
12002
12003 if (mStorageControllers.isBackedUp())
12004 {
12005 mStorageControllers.commit();
12006
12007 if (mPeer)
12008 {
12009 /* Commit all changes to new controllers (this will reshare data with
12010 * peers for those who have peers) */
12011 StorageControllerList *newList = new StorageControllerList();
12012 for (StorageControllerList::const_iterator
12013 it = mStorageControllers->begin();
12014 it != mStorageControllers->end();
12015 ++it)
12016 {
12017 (*it)->i_commit();
12018
12019 /* look if this controller has a peer device */
12020 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12021 if (!peer)
12022 {
12023 /* no peer means the device is a newly created one;
12024 * create a peer owning data this device share it with */
12025 peer.createObject();
12026 peer->init(mPeer, *it, true /* aReshare */);
12027 }
12028 else
12029 {
12030 /* remove peer from the old list */
12031 mPeer->mStorageControllers->remove(peer);
12032 }
12033 /* and add it to the new list */
12034 newList->push_back(peer);
12035 }
12036
12037 /* uninit old peer's controllers that are left */
12038 for (StorageControllerList::const_iterator
12039 it = mPeer->mStorageControllers->begin();
12040 it != mPeer->mStorageControllers->end();
12041 ++it)
12042 {
12043 (*it)->uninit();
12044 }
12045
12046 /* attach new list of controllers to our peer */
12047 mPeer->mStorageControllers.attach(newList);
12048 }
12049 else
12050 {
12051 /* we have no peer (our parent is the newly created machine);
12052 * just commit changes to devices */
12053 commitStorageControllers = true;
12054 }
12055 }
12056 else
12057 {
12058 /* the list of controllers itself is not changed,
12059 * just commit changes to controllers themselves */
12060 commitStorageControllers = true;
12061 }
12062
12063 if (commitStorageControllers)
12064 {
12065 for (StorageControllerList::const_iterator
12066 it = mStorageControllers->begin();
12067 it != mStorageControllers->end();
12068 ++it)
12069 {
12070 (*it)->i_commit();
12071 }
12072 }
12073
12074 bool commitUSBControllers = false;
12075
12076 if (mUSBControllers.isBackedUp())
12077 {
12078 mUSBControllers.commit();
12079
12080 if (mPeer)
12081 {
12082 /* Commit all changes to new controllers (this will reshare data with
12083 * peers for those who have peers) */
12084 USBControllerList *newList = new USBControllerList();
12085 for (USBControllerList::const_iterator
12086 it = mUSBControllers->begin();
12087 it != mUSBControllers->end();
12088 ++it)
12089 {
12090 (*it)->i_commit();
12091
12092 /* look if this controller has a peer device */
12093 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12094 if (!peer)
12095 {
12096 /* no peer means the device is a newly created one;
12097 * create a peer owning data this device share it with */
12098 peer.createObject();
12099 peer->init(mPeer, *it, true /* aReshare */);
12100 }
12101 else
12102 {
12103 /* remove peer from the old list */
12104 mPeer->mUSBControllers->remove(peer);
12105 }
12106 /* and add it to the new list */
12107 newList->push_back(peer);
12108 }
12109
12110 /* uninit old peer's controllers that are left */
12111 for (USBControllerList::const_iterator
12112 it = mPeer->mUSBControllers->begin();
12113 it != mPeer->mUSBControllers->end();
12114 ++it)
12115 {
12116 (*it)->uninit();
12117 }
12118
12119 /* attach new list of controllers to our peer */
12120 mPeer->mUSBControllers.attach(newList);
12121 }
12122 else
12123 {
12124 /* we have no peer (our parent is the newly created machine);
12125 * just commit changes to devices */
12126 commitUSBControllers = true;
12127 }
12128 }
12129 else
12130 {
12131 /* the list of controllers itself is not changed,
12132 * just commit changes to controllers themselves */
12133 commitUSBControllers = true;
12134 }
12135
12136 if (commitUSBControllers)
12137 {
12138 for (USBControllerList::const_iterator
12139 it = mUSBControllers->begin();
12140 it != mUSBControllers->end();
12141 ++it)
12142 {
12143 (*it)->i_commit();
12144 }
12145 }
12146
12147 if (i_isSessionMachine())
12148 {
12149 /* attach new data to the primary machine and reshare it */
12150 mPeer->mUserData.attach(mUserData);
12151 mPeer->mHWData.attach(mHWData);
12152 /* mmMediumAttachments is reshared by fixupMedia */
12153 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12154 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12155 }
12156}
12157
12158/**
12159 * Copies all the hardware data from the given machine.
12160 *
12161 * Currently, only called when the VM is being restored from a snapshot. In
12162 * particular, this implies that the VM is not running during this method's
12163 * call.
12164 *
12165 * @note This method must be called from under this object's lock.
12166 *
12167 * @note This method doesn't call #i_commit(), so all data remains backed up and
12168 * unsaved.
12169 */
12170void Machine::i_copyFrom(Machine *aThat)
12171{
12172 AssertReturnVoid(!i_isSnapshotMachine());
12173 AssertReturnVoid(aThat->i_isSnapshotMachine());
12174
12175 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12176
12177 mHWData.assignCopy(aThat->mHWData);
12178
12179 // create copies of all shared folders (mHWData after attaching a copy
12180 // contains just references to original objects)
12181 for (HWData::SharedFolderList::iterator
12182 it = mHWData->mSharedFolders.begin();
12183 it != mHWData->mSharedFolders.end();
12184 ++it)
12185 {
12186 ComObjPtr<SharedFolder> folder;
12187 folder.createObject();
12188 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12189 AssertComRC(rc);
12190 *it = folder;
12191 }
12192
12193 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12194 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12195 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12196 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12197 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12198 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12199
12200 /* create private copies of all controllers */
12201 mStorageControllers.backup();
12202 mStorageControllers->clear();
12203 for (StorageControllerList::const_iterator
12204 it = aThat->mStorageControllers->begin();
12205 it != aThat->mStorageControllers->end();
12206 ++it)
12207 {
12208 ComObjPtr<StorageController> ctrl;
12209 ctrl.createObject();
12210 ctrl->initCopy(this, *it);
12211 mStorageControllers->push_back(ctrl);
12212 }
12213
12214 /* create private copies of all USB controllers */
12215 mUSBControllers.backup();
12216 mUSBControllers->clear();
12217 for (USBControllerList::const_iterator
12218 it = aThat->mUSBControllers->begin();
12219 it != aThat->mUSBControllers->end();
12220 ++it)
12221 {
12222 ComObjPtr<USBController> ctrl;
12223 ctrl.createObject();
12224 ctrl->initCopy(this, *it);
12225 mUSBControllers->push_back(ctrl);
12226 }
12227
12228 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12229 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12230 {
12231 if (mNetworkAdapters[slot].isNotNull())
12232 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12233 else
12234 {
12235 unconst(mNetworkAdapters[slot]).createObject();
12236 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12237 }
12238 }
12239 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12240 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12241 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12242 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12243}
12244
12245/**
12246 * Returns whether the given storage controller is hotplug capable.
12247 *
12248 * @returns true if the controller supports hotplugging
12249 * false otherwise.
12250 * @param enmCtrlType The controller type to check for.
12251 */
12252bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12253{
12254 ComPtr<ISystemProperties> systemProperties;
12255 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12256 if (FAILED(rc))
12257 return false;
12258
12259 BOOL aHotplugCapable = FALSE;
12260 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12261
12262 return RT_BOOL(aHotplugCapable);
12263}
12264
12265#ifdef VBOX_WITH_RESOURCE_USAGE_API
12266
12267void Machine::i_getDiskList(MediaList &list)
12268{
12269 for (MediumAttachmentList::const_iterator
12270 it = mMediumAttachments->begin();
12271 it != mMediumAttachments->end();
12272 ++it)
12273 {
12274 MediumAttachment *pAttach = *it;
12275 /* just in case */
12276 AssertContinue(pAttach);
12277
12278 AutoCaller localAutoCallerA(pAttach);
12279 if (FAILED(localAutoCallerA.rc())) continue;
12280
12281 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12282
12283 if (pAttach->i_getType() == DeviceType_HardDisk)
12284 list.push_back(pAttach->i_getMedium());
12285 }
12286}
12287
12288void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12289{
12290 AssertReturnVoid(isWriteLockOnCurrentThread());
12291 AssertPtrReturnVoid(aCollector);
12292
12293 pm::CollectorHAL *hal = aCollector->getHAL();
12294 /* Create sub metrics */
12295 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12296 "Percentage of processor time spent in user mode by the VM process.");
12297 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12298 "Percentage of processor time spent in kernel mode by the VM process.");
12299 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12300 "Size of resident portion of VM process in memory.");
12301 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12302 "Actual size of all VM disks combined.");
12303 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12304 "Network receive rate.");
12305 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12306 "Network transmit rate.");
12307 /* Create and register base metrics */
12308 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12309 cpuLoadUser, cpuLoadKernel);
12310 aCollector->registerBaseMetric(cpuLoad);
12311 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12312 ramUsageUsed);
12313 aCollector->registerBaseMetric(ramUsage);
12314 MediaList disks;
12315 i_getDiskList(disks);
12316 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12317 diskUsageUsed);
12318 aCollector->registerBaseMetric(diskUsage);
12319
12320 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12321 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12322 new pm::AggregateAvg()));
12323 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12324 new pm::AggregateMin()));
12325 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12326 new pm::AggregateMax()));
12327 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12328 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12329 new pm::AggregateAvg()));
12330 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12331 new pm::AggregateMin()));
12332 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12333 new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12336 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12337 new pm::AggregateAvg()));
12338 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12339 new pm::AggregateMin()));
12340 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12341 new pm::AggregateMax()));
12342
12343 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12344 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12345 new pm::AggregateAvg()));
12346 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12347 new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12349 new pm::AggregateMax()));
12350
12351
12352 /* Guest metrics collector */
12353 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12354 aCollector->registerGuest(mCollectorGuest);
12355 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12356
12357 /* Create sub metrics */
12358 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12359 "Percentage of processor time spent in user mode as seen by the guest.");
12360 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12361 "Percentage of processor time spent in kernel mode as seen by the guest.");
12362 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12363 "Percentage of processor time spent idling as seen by the guest.");
12364
12365 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12366 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12367 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12368 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12369 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12370 pm::SubMetric *guestMemCache = new pm::SubMetric(
12371 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12372
12373 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12374 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12375
12376 /* Create and register base metrics */
12377 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12378 machineNetRx, machineNetTx);
12379 aCollector->registerBaseMetric(machineNetRate);
12380
12381 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12382 guestLoadUser, guestLoadKernel, guestLoadIdle);
12383 aCollector->registerBaseMetric(guestCpuLoad);
12384
12385 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12386 guestMemTotal, guestMemFree,
12387 guestMemBalloon, guestMemShared,
12388 guestMemCache, guestPagedTotal);
12389 aCollector->registerBaseMetric(guestCpuMem);
12390
12391 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12392 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12393 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12394 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12395
12396 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12397 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12398 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12399 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12400
12401 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12402 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12403 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12404 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12405
12406 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12407 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12408 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12409 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12410
12411 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12412 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12413 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12414 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12415
12416 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12417 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12418 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12419 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12420
12421 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12422 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12423 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12424 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12425
12426 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12427 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12428 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12429 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12430
12431 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12432 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12433 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12434 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12435
12436 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12437 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12438 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12439 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12440
12441 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12442 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12443 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12444 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12445}
12446
12447void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12448{
12449 AssertReturnVoid(isWriteLockOnCurrentThread());
12450
12451 if (aCollector)
12452 {
12453 aCollector->unregisterMetricsFor(aMachine);
12454 aCollector->unregisterBaseMetricsFor(aMachine);
12455 }
12456}
12457
12458#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12459
12460
12461////////////////////////////////////////////////////////////////////////////////
12462
12463DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12464
12465HRESULT SessionMachine::FinalConstruct()
12466{
12467 LogFlowThisFunc(("\n"));
12468
12469 mClientToken = NULL;
12470
12471 return BaseFinalConstruct();
12472}
12473
12474void SessionMachine::FinalRelease()
12475{
12476 LogFlowThisFunc(("\n"));
12477
12478 Assert(!mClientToken);
12479 /* paranoia, should not hang around any more */
12480 if (mClientToken)
12481 {
12482 delete mClientToken;
12483 mClientToken = NULL;
12484 }
12485
12486 uninit(Uninit::Unexpected);
12487
12488 BaseFinalRelease();
12489}
12490
12491/**
12492 * @note Must be called only by Machine::LockMachine() from its own write lock.
12493 */
12494HRESULT SessionMachine::init(Machine *aMachine)
12495{
12496 LogFlowThisFuncEnter();
12497 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12498
12499 AssertReturn(aMachine, E_INVALIDARG);
12500
12501 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12502
12503 /* Enclose the state transition NotReady->InInit->Ready */
12504 AutoInitSpan autoInitSpan(this);
12505 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12506
12507 HRESULT rc = S_OK;
12508
12509 RT_ZERO(mAuthLibCtx);
12510
12511 /* create the machine client token */
12512 try
12513 {
12514 mClientToken = new ClientToken(aMachine, this);
12515 if (!mClientToken->isReady())
12516 {
12517 delete mClientToken;
12518 mClientToken = NULL;
12519 rc = E_FAIL;
12520 }
12521 }
12522 catch (std::bad_alloc &)
12523 {
12524 rc = E_OUTOFMEMORY;
12525 }
12526 if (FAILED(rc))
12527 return rc;
12528
12529 /* memorize the peer Machine */
12530 unconst(mPeer) = aMachine;
12531 /* share the parent pointer */
12532 unconst(mParent) = aMachine->mParent;
12533
12534 /* take the pointers to data to share */
12535 mData.share(aMachine->mData);
12536 mSSData.share(aMachine->mSSData);
12537
12538 mUserData.share(aMachine->mUserData);
12539 mHWData.share(aMachine->mHWData);
12540 mMediumAttachments.share(aMachine->mMediumAttachments);
12541
12542 mStorageControllers.allocate();
12543 for (StorageControllerList::const_iterator
12544 it = aMachine->mStorageControllers->begin();
12545 it != aMachine->mStorageControllers->end();
12546 ++it)
12547 {
12548 ComObjPtr<StorageController> ctl;
12549 ctl.createObject();
12550 ctl->init(this, *it);
12551 mStorageControllers->push_back(ctl);
12552 }
12553
12554 mUSBControllers.allocate();
12555 for (USBControllerList::const_iterator
12556 it = aMachine->mUSBControllers->begin();
12557 it != aMachine->mUSBControllers->end();
12558 ++it)
12559 {
12560 ComObjPtr<USBController> ctl;
12561 ctl.createObject();
12562 ctl->init(this, *it);
12563 mUSBControllers->push_back(ctl);
12564 }
12565
12566 unconst(mBIOSSettings).createObject();
12567 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12568 unconst(mRecordingSettings).createObject();
12569 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12570 /* create another VRDEServer object that will be mutable */
12571 unconst(mVRDEServer).createObject();
12572 mVRDEServer->init(this, aMachine->mVRDEServer);
12573 /* create another audio adapter object that will be mutable */
12574 unconst(mAudioAdapter).createObject();
12575 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12576 /* create a list of serial ports that will be mutable */
12577 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12578 {
12579 unconst(mSerialPorts[slot]).createObject();
12580 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12581 }
12582 /* create a list of parallel ports that will be mutable */
12583 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12584 {
12585 unconst(mParallelPorts[slot]).createObject();
12586 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12587 }
12588
12589 /* create another USB device filters object that will be mutable */
12590 unconst(mUSBDeviceFilters).createObject();
12591 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12592
12593 /* create a list of network adapters that will be mutable */
12594 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12595 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12596 {
12597 unconst(mNetworkAdapters[slot]).createObject();
12598 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12599 }
12600
12601 /* create another bandwidth control object that will be mutable */
12602 unconst(mBandwidthControl).createObject();
12603 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12604
12605 /* default is to delete saved state on Saved -> PoweredOff transition */
12606 mRemoveSavedState = true;
12607
12608 /* Confirm a successful initialization when it's the case */
12609 autoInitSpan.setSucceeded();
12610
12611 miNATNetworksStarted = 0;
12612
12613 LogFlowThisFuncLeave();
12614 return rc;
12615}
12616
12617/**
12618 * Uninitializes this session object. If the reason is other than
12619 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12620 * or the client watcher code.
12621 *
12622 * @param aReason uninitialization reason
12623 *
12624 * @note Locks mParent + this object for writing.
12625 */
12626void SessionMachine::uninit(Uninit::Reason aReason)
12627{
12628 LogFlowThisFuncEnter();
12629 LogFlowThisFunc(("reason=%d\n", aReason));
12630
12631 /*
12632 * Strongly reference ourselves to prevent this object deletion after
12633 * mData->mSession.mMachine.setNull() below (which can release the last
12634 * reference and call the destructor). Important: this must be done before
12635 * accessing any members (and before AutoUninitSpan that does it as well).
12636 * This self reference will be released as the very last step on return.
12637 */
12638 ComObjPtr<SessionMachine> selfRef;
12639 if (aReason != Uninit::Unexpected)
12640 selfRef = this;
12641
12642 /* Enclose the state transition Ready->InUninit->NotReady */
12643 AutoUninitSpan autoUninitSpan(this);
12644 if (autoUninitSpan.uninitDone())
12645 {
12646 LogFlowThisFunc(("Already uninitialized\n"));
12647 LogFlowThisFuncLeave();
12648 return;
12649 }
12650
12651 if (autoUninitSpan.initFailed())
12652 {
12653 /* We've been called by init() because it's failed. It's not really
12654 * necessary (nor it's safe) to perform the regular uninit sequence
12655 * below, the following is enough.
12656 */
12657 LogFlowThisFunc(("Initialization failed.\n"));
12658 /* destroy the machine client token */
12659 if (mClientToken)
12660 {
12661 delete mClientToken;
12662 mClientToken = NULL;
12663 }
12664 uninitDataAndChildObjects();
12665 mData.free();
12666 unconst(mParent) = NULL;
12667 unconst(mPeer) = NULL;
12668 LogFlowThisFuncLeave();
12669 return;
12670 }
12671
12672 MachineState_T lastState;
12673 {
12674 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12675 lastState = mData->mMachineState;
12676 }
12677 NOREF(lastState);
12678
12679#ifdef VBOX_WITH_USB
12680 // release all captured USB devices, but do this before requesting the locks below
12681 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12682 {
12683 /* Console::captureUSBDevices() is called in the VM process only after
12684 * setting the machine state to Starting or Restoring.
12685 * Console::detachAllUSBDevices() will be called upon successful
12686 * termination. So, we need to release USB devices only if there was
12687 * an abnormal termination of a running VM.
12688 *
12689 * This is identical to SessionMachine::DetachAllUSBDevices except
12690 * for the aAbnormal argument. */
12691 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12692 AssertComRC(rc);
12693 NOREF(rc);
12694
12695 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12696 if (service)
12697 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12698 }
12699#endif /* VBOX_WITH_USB */
12700
12701 // we need to lock this object in uninit() because the lock is shared
12702 // with mPeer (as well as data we modify below). mParent lock is needed
12703 // by several calls to it.
12704 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12705
12706#ifdef VBOX_WITH_RESOURCE_USAGE_API
12707 /*
12708 * It is safe to call Machine::i_unregisterMetrics() here because
12709 * PerformanceCollector::samplerCallback no longer accesses guest methods
12710 * holding the lock.
12711 */
12712 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12713 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12714 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12715 if (mCollectorGuest)
12716 {
12717 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12718 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12719 mCollectorGuest = NULL;
12720 }
12721#endif
12722
12723 if (aReason == Uninit::Abnormal)
12724 {
12725 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12726
12727 /* reset the state to Aborted */
12728 if (mData->mMachineState != MachineState_Aborted)
12729 i_setMachineState(MachineState_Aborted);
12730 }
12731
12732 // any machine settings modified?
12733 if (mData->flModifications)
12734 {
12735 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12736 i_rollback(false /* aNotify */);
12737 }
12738
12739 mData->mSession.mPID = NIL_RTPROCESS;
12740
12741 if (aReason == Uninit::Unexpected)
12742 {
12743 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12744 * client watcher thread to update the set of machines that have open
12745 * sessions. */
12746 mParent->i_updateClientWatcher();
12747 }
12748
12749 /* uninitialize all remote controls */
12750 if (mData->mSession.mRemoteControls.size())
12751 {
12752 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12753 mData->mSession.mRemoteControls.size()));
12754
12755 /* Always restart a the beginning, since the iterator is invalidated
12756 * by using erase(). */
12757 for (Data::Session::RemoteControlList::iterator
12758 it = mData->mSession.mRemoteControls.begin();
12759 it != mData->mSession.mRemoteControls.end();
12760 it = mData->mSession.mRemoteControls.begin())
12761 {
12762 ComPtr<IInternalSessionControl> pControl = *it;
12763 mData->mSession.mRemoteControls.erase(it);
12764 multilock.release();
12765 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12766 HRESULT rc = pControl->Uninitialize();
12767 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12768 if (FAILED(rc))
12769 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12770 multilock.acquire();
12771 }
12772 mData->mSession.mRemoteControls.clear();
12773 }
12774
12775 /* Remove all references to the NAT network service. The service will stop
12776 * if all references (also from other VMs) are removed. */
12777 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12778 {
12779 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12780 {
12781 BOOL enabled;
12782 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12783 if ( FAILED(hrc)
12784 || !enabled)
12785 continue;
12786
12787 NetworkAttachmentType_T type;
12788 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12789 if ( SUCCEEDED(hrc)
12790 && type == NetworkAttachmentType_NATNetwork)
12791 {
12792 Bstr name;
12793 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12794 if (SUCCEEDED(hrc))
12795 {
12796 multilock.release();
12797 Utf8Str strName(name);
12798 LogRel(("VM '%s' stops using NAT network '%s'\n",
12799 mUserData->s.strName.c_str(), strName.c_str()));
12800 mParent->i_natNetworkRefDec(strName);
12801 multilock.acquire();
12802 }
12803 }
12804 }
12805 }
12806
12807 /*
12808 * An expected uninitialization can come only from #i_checkForDeath().
12809 * Otherwise it means that something's gone really wrong (for example,
12810 * the Session implementation has released the VirtualBox reference
12811 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12812 * etc). However, it's also possible, that the client releases the IPC
12813 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12814 * but the VirtualBox release event comes first to the server process.
12815 * This case is practically possible, so we should not assert on an
12816 * unexpected uninit, just log a warning.
12817 */
12818
12819 if (aReason == Uninit::Unexpected)
12820 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12821
12822 if (aReason != Uninit::Normal)
12823 {
12824 mData->mSession.mDirectControl.setNull();
12825 }
12826 else
12827 {
12828 /* this must be null here (see #OnSessionEnd()) */
12829 Assert(mData->mSession.mDirectControl.isNull());
12830 Assert(mData->mSession.mState == SessionState_Unlocking);
12831 Assert(!mData->mSession.mProgress.isNull());
12832 }
12833 if (mData->mSession.mProgress)
12834 {
12835 if (aReason == Uninit::Normal)
12836 mData->mSession.mProgress->i_notifyComplete(S_OK);
12837 else
12838 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12839 COM_IIDOF(ISession),
12840 getComponentName(),
12841 tr("The VM session was aborted"));
12842 mData->mSession.mProgress.setNull();
12843 }
12844
12845 if (mConsoleTaskData.mProgress)
12846 {
12847 Assert(aReason == Uninit::Abnormal);
12848 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12849 COM_IIDOF(ISession),
12850 getComponentName(),
12851 tr("The VM session was aborted"));
12852 mConsoleTaskData.mProgress.setNull();
12853 }
12854
12855 /* remove the association between the peer machine and this session machine */
12856 Assert( (SessionMachine*)mData->mSession.mMachine == this
12857 || aReason == Uninit::Unexpected);
12858
12859 /* reset the rest of session data */
12860 mData->mSession.mLockType = LockType_Null;
12861 mData->mSession.mMachine.setNull();
12862 mData->mSession.mState = SessionState_Unlocked;
12863 mData->mSession.mName.setNull();
12864
12865 /* destroy the machine client token before leaving the exclusive lock */
12866 if (mClientToken)
12867 {
12868 delete mClientToken;
12869 mClientToken = NULL;
12870 }
12871
12872 /* fire an event */
12873 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12874
12875 uninitDataAndChildObjects();
12876
12877 /* free the essential data structure last */
12878 mData.free();
12879
12880 /* release the exclusive lock before setting the below two to NULL */
12881 multilock.release();
12882
12883 unconst(mParent) = NULL;
12884 unconst(mPeer) = NULL;
12885
12886 AuthLibUnload(&mAuthLibCtx);
12887
12888 LogFlowThisFuncLeave();
12889}
12890
12891// util::Lockable interface
12892////////////////////////////////////////////////////////////////////////////////
12893
12894/**
12895 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12896 * with the primary Machine instance (mPeer).
12897 */
12898RWLockHandle *SessionMachine::lockHandle() const
12899{
12900 AssertReturn(mPeer != NULL, NULL);
12901 return mPeer->lockHandle();
12902}
12903
12904// IInternalMachineControl methods
12905////////////////////////////////////////////////////////////////////////////////
12906
12907/**
12908 * Passes collected guest statistics to performance collector object
12909 */
12910HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12911 ULONG aCpuKernel, ULONG aCpuIdle,
12912 ULONG aMemTotal, ULONG aMemFree,
12913 ULONG aMemBalloon, ULONG aMemShared,
12914 ULONG aMemCache, ULONG aPageTotal,
12915 ULONG aAllocVMM, ULONG aFreeVMM,
12916 ULONG aBalloonedVMM, ULONG aSharedVMM,
12917 ULONG aVmNetRx, ULONG aVmNetTx)
12918{
12919#ifdef VBOX_WITH_RESOURCE_USAGE_API
12920 if (mCollectorGuest)
12921 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12922 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12923 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12924 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12925
12926 return S_OK;
12927#else
12928 NOREF(aValidStats);
12929 NOREF(aCpuUser);
12930 NOREF(aCpuKernel);
12931 NOREF(aCpuIdle);
12932 NOREF(aMemTotal);
12933 NOREF(aMemFree);
12934 NOREF(aMemBalloon);
12935 NOREF(aMemShared);
12936 NOREF(aMemCache);
12937 NOREF(aPageTotal);
12938 NOREF(aAllocVMM);
12939 NOREF(aFreeVMM);
12940 NOREF(aBalloonedVMM);
12941 NOREF(aSharedVMM);
12942 NOREF(aVmNetRx);
12943 NOREF(aVmNetTx);
12944 return E_NOTIMPL;
12945#endif
12946}
12947
12948////////////////////////////////////////////////////////////////////////////////
12949//
12950// SessionMachine task records
12951//
12952////////////////////////////////////////////////////////////////////////////////
12953
12954/**
12955 * Task record for saving the machine state.
12956 */
12957class SessionMachine::SaveStateTask
12958 : public Machine::Task
12959{
12960public:
12961 SaveStateTask(SessionMachine *m,
12962 Progress *p,
12963 const Utf8Str &t,
12964 Reason_T enmReason,
12965 const Utf8Str &strStateFilePath)
12966 : Task(m, p, t),
12967 m_enmReason(enmReason),
12968 m_strStateFilePath(strStateFilePath)
12969 {}
12970
12971private:
12972 void handler()
12973 {
12974 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12975 }
12976
12977 Reason_T m_enmReason;
12978 Utf8Str m_strStateFilePath;
12979
12980 friend class SessionMachine;
12981};
12982
12983/**
12984 * Task thread implementation for SessionMachine::SaveState(), called from
12985 * SessionMachine::taskHandler().
12986 *
12987 * @note Locks this object for writing.
12988 *
12989 * @param task
12990 * @return
12991 */
12992void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12993{
12994 LogFlowThisFuncEnter();
12995
12996 AutoCaller autoCaller(this);
12997 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12998 if (FAILED(autoCaller.rc()))
12999 {
13000 /* we might have been uninitialized because the session was accidentally
13001 * closed by the client, so don't assert */
13002 HRESULT rc = setError(E_FAIL,
13003 tr("The session has been accidentally closed"));
13004 task.m_pProgress->i_notifyComplete(rc);
13005 LogFlowThisFuncLeave();
13006 return;
13007 }
13008
13009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13010
13011 HRESULT rc = S_OK;
13012
13013 try
13014 {
13015 ComPtr<IInternalSessionControl> directControl;
13016 if (mData->mSession.mLockType == LockType_VM)
13017 directControl = mData->mSession.mDirectControl;
13018 if (directControl.isNull())
13019 throw setError(VBOX_E_INVALID_VM_STATE,
13020 tr("Trying to save state without a running VM"));
13021 alock.release();
13022 BOOL fSuspendedBySave;
13023 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13024 Assert(!fSuspendedBySave);
13025 alock.acquire();
13026
13027 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13028 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13029 throw E_FAIL);
13030
13031 if (SUCCEEDED(rc))
13032 {
13033 mSSData->strStateFilePath = task.m_strStateFilePath;
13034
13035 /* save all VM settings */
13036 rc = i_saveSettings(NULL);
13037 // no need to check whether VirtualBox.xml needs saving also since
13038 // we can't have a name change pending at this point
13039 }
13040 else
13041 {
13042 // On failure, set the state to the state we had at the beginning.
13043 i_setMachineState(task.m_machineStateBackup);
13044 i_updateMachineStateOnClient();
13045
13046 // Delete the saved state file (might have been already created).
13047 // No need to check whether this is shared with a snapshot here
13048 // because we certainly created a fresh saved state file here.
13049 RTFileDelete(task.m_strStateFilePath.c_str());
13050 }
13051 }
13052 catch (HRESULT aRC) { rc = aRC; }
13053
13054 task.m_pProgress->i_notifyComplete(rc);
13055
13056 LogFlowThisFuncLeave();
13057}
13058
13059/**
13060 * @note Locks this object for writing.
13061 */
13062HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13063{
13064 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13065}
13066
13067HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13068{
13069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13070
13071 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13072 if (FAILED(rc)) return rc;
13073
13074 if ( mData->mMachineState != MachineState_Running
13075 && mData->mMachineState != MachineState_Paused
13076 )
13077 return setError(VBOX_E_INVALID_VM_STATE,
13078 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13079 Global::stringifyMachineState(mData->mMachineState));
13080
13081 ComObjPtr<Progress> pProgress;
13082 pProgress.createObject();
13083 rc = pProgress->init(i_getVirtualBox(),
13084 static_cast<IMachine *>(this) /* aInitiator */,
13085 tr("Saving the execution state of the virtual machine"),
13086 FALSE /* aCancelable */);
13087 if (FAILED(rc))
13088 return rc;
13089
13090 Utf8Str strStateFilePath;
13091 i_composeSavedStateFilename(strStateFilePath);
13092
13093 /* create and start the task on a separate thread (note that it will not
13094 * start working until we release alock) */
13095 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13096 rc = pTask->createThread();
13097 if (FAILED(rc))
13098 return rc;
13099
13100 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13101 i_setMachineState(MachineState_Saving);
13102 i_updateMachineStateOnClient();
13103
13104 pProgress.queryInterfaceTo(aProgress.asOutParam());
13105
13106 return S_OK;
13107}
13108
13109/**
13110 * @note Locks this object for writing.
13111 */
13112HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13113{
13114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13115
13116 HRESULT rc = i_checkStateDependency(MutableStateDep);
13117 if (FAILED(rc)) return rc;
13118
13119 if ( mData->mMachineState != MachineState_PoweredOff
13120 && mData->mMachineState != MachineState_Teleported
13121 && mData->mMachineState != MachineState_Aborted
13122 )
13123 return setError(VBOX_E_INVALID_VM_STATE,
13124 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13125 Global::stringifyMachineState(mData->mMachineState));
13126
13127 com::Utf8Str stateFilePathFull;
13128 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13129 if (RT_FAILURE(vrc))
13130 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13131 tr("Invalid saved state file path '%s' (%Rrc)"),
13132 aSavedStateFile.c_str(),
13133 vrc);
13134
13135 mSSData->strStateFilePath = stateFilePathFull;
13136
13137 /* The below i_setMachineState() will detect the state transition and will
13138 * update the settings file */
13139
13140 return i_setMachineState(MachineState_Saved);
13141}
13142
13143/**
13144 * @note Locks this object for writing.
13145 */
13146HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13147{
13148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13149
13150 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13151 if (FAILED(rc)) return rc;
13152
13153 if (mData->mMachineState != MachineState_Saved)
13154 return setError(VBOX_E_INVALID_VM_STATE,
13155 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13156 Global::stringifyMachineState(mData->mMachineState));
13157
13158 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13159
13160 /*
13161 * Saved -> PoweredOff transition will be detected in the SessionMachine
13162 * and properly handled.
13163 */
13164 rc = i_setMachineState(MachineState_PoweredOff);
13165 return rc;
13166}
13167
13168
13169/**
13170 * @note Locks the same as #i_setMachineState() does.
13171 */
13172HRESULT SessionMachine::updateState(MachineState_T aState)
13173{
13174 return i_setMachineState(aState);
13175}
13176
13177/**
13178 * @note Locks this object for writing.
13179 */
13180HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13181{
13182 IProgress *pProgress(aProgress);
13183
13184 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13185
13186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13187
13188 if (mData->mSession.mState != SessionState_Locked)
13189 return VBOX_E_INVALID_OBJECT_STATE;
13190
13191 if (!mData->mSession.mProgress.isNull())
13192 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13193
13194 /* If we didn't reference the NAT network service yet, add a reference to
13195 * force a start */
13196 if (miNATNetworksStarted < 1)
13197 {
13198 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13199 {
13200 BOOL enabled;
13201 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13202 if ( FAILED(hrc)
13203 || !enabled)
13204 continue;
13205
13206 NetworkAttachmentType_T type;
13207 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13208 if ( SUCCEEDED(hrc)
13209 && type == NetworkAttachmentType_NATNetwork)
13210 {
13211 Bstr name;
13212 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13213 if (SUCCEEDED(hrc))
13214 {
13215 Utf8Str strName(name);
13216 LogRel(("VM '%s' starts using NAT network '%s'\n",
13217 mUserData->s.strName.c_str(), strName.c_str()));
13218 mPeer->lockHandle()->unlockWrite();
13219 mParent->i_natNetworkRefInc(strName);
13220#ifdef RT_LOCK_STRICT
13221 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13222#else
13223 mPeer->lockHandle()->lockWrite();
13224#endif
13225 }
13226 }
13227 }
13228 miNATNetworksStarted++;
13229 }
13230
13231 LogFlowThisFunc(("returns S_OK.\n"));
13232 return S_OK;
13233}
13234
13235/**
13236 * @note Locks this object for writing.
13237 */
13238HRESULT SessionMachine::endPowerUp(LONG aResult)
13239{
13240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13241
13242 if (mData->mSession.mState != SessionState_Locked)
13243 return VBOX_E_INVALID_OBJECT_STATE;
13244
13245 /* Finalize the LaunchVMProcess progress object. */
13246 if (mData->mSession.mProgress)
13247 {
13248 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13249 mData->mSession.mProgress.setNull();
13250 }
13251
13252 if (SUCCEEDED((HRESULT)aResult))
13253 {
13254#ifdef VBOX_WITH_RESOURCE_USAGE_API
13255 /* The VM has been powered up successfully, so it makes sense
13256 * now to offer the performance metrics for a running machine
13257 * object. Doing it earlier wouldn't be safe. */
13258 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13259 mData->mSession.mPID);
13260#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13261 }
13262
13263 return S_OK;
13264}
13265
13266/**
13267 * @note Locks this object for writing.
13268 */
13269HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13270{
13271 LogFlowThisFuncEnter();
13272
13273#ifdef VBOX_WITH_CLOUD_NET
13274 mPeer->i_disconnectFromCloudNetwork();
13275#endif /* VBOX_WITH_CLOUD_NET */
13276
13277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13278
13279 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13280 E_FAIL);
13281
13282 /* create a progress object to track operation completion */
13283 ComObjPtr<Progress> pProgress;
13284 pProgress.createObject();
13285 pProgress->init(i_getVirtualBox(),
13286 static_cast<IMachine *>(this) /* aInitiator */,
13287 tr("Stopping the virtual machine"),
13288 FALSE /* aCancelable */);
13289
13290 /* fill in the console task data */
13291 mConsoleTaskData.mLastState = mData->mMachineState;
13292 mConsoleTaskData.mProgress = pProgress;
13293
13294 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13295 i_setMachineState(MachineState_Stopping);
13296
13297 pProgress.queryInterfaceTo(aProgress.asOutParam());
13298
13299 return S_OK;
13300}
13301
13302/**
13303 * @note Locks this object for writing.
13304 */
13305HRESULT SessionMachine::endPoweringDown(LONG aResult,
13306 const com::Utf8Str &aErrMsg)
13307{
13308 LogFlowThisFuncEnter();
13309
13310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13311
13312 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13313 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13314 && mConsoleTaskData.mLastState != MachineState_Null,
13315 E_FAIL);
13316
13317 /*
13318 * On failure, set the state to the state we had when BeginPoweringDown()
13319 * was called (this is expected by Console::PowerDown() and the associated
13320 * task). On success the VM process already changed the state to
13321 * MachineState_PoweredOff, so no need to do anything.
13322 */
13323 if (FAILED(aResult))
13324 i_setMachineState(mConsoleTaskData.mLastState);
13325
13326 /* notify the progress object about operation completion */
13327 Assert(mConsoleTaskData.mProgress);
13328 if (SUCCEEDED(aResult))
13329 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13330 else
13331 {
13332 if (aErrMsg.length())
13333 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13334 COM_IIDOF(ISession),
13335 getComponentName(),
13336 aErrMsg.c_str());
13337 else
13338 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13339 }
13340
13341 /* clear out the temporary saved state data */
13342 mConsoleTaskData.mLastState = MachineState_Null;
13343 mConsoleTaskData.mProgress.setNull();
13344
13345 LogFlowThisFuncLeave();
13346 return S_OK;
13347}
13348
13349
13350/**
13351 * Goes through the USB filters of the given machine to see if the given
13352 * device matches any filter or not.
13353 *
13354 * @note Locks the same as USBController::hasMatchingFilter() does.
13355 */
13356HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13357 BOOL *aMatched,
13358 ULONG *aMaskedInterfaces)
13359{
13360 LogFlowThisFunc(("\n"));
13361
13362#ifdef VBOX_WITH_USB
13363 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13364#else
13365 NOREF(aDevice);
13366 NOREF(aMaskedInterfaces);
13367 *aMatched = FALSE;
13368#endif
13369
13370 return S_OK;
13371}
13372
13373/**
13374 * @note Locks the same as Host::captureUSBDevice() does.
13375 */
13376HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13377{
13378 LogFlowThisFunc(("\n"));
13379
13380#ifdef VBOX_WITH_USB
13381 /* if captureDeviceForVM() fails, it must have set extended error info */
13382 clearError();
13383 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13384 if (FAILED(rc)) return rc;
13385
13386 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13387 AssertReturn(service, E_FAIL);
13388 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13389#else
13390 NOREF(aId);
13391 return E_NOTIMPL;
13392#endif
13393}
13394
13395/**
13396 * @note Locks the same as Host::detachUSBDevice() does.
13397 */
13398HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13399 BOOL aDone)
13400{
13401 LogFlowThisFunc(("\n"));
13402
13403#ifdef VBOX_WITH_USB
13404 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13405 AssertReturn(service, E_FAIL);
13406 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13407#else
13408 NOREF(aId);
13409 NOREF(aDone);
13410 return E_NOTIMPL;
13411#endif
13412}
13413
13414/**
13415 * Inserts all machine filters to the USB proxy service and then calls
13416 * Host::autoCaptureUSBDevices().
13417 *
13418 * Called by Console from the VM process upon VM startup.
13419 *
13420 * @note Locks what called methods lock.
13421 */
13422HRESULT SessionMachine::autoCaptureUSBDevices()
13423{
13424 LogFlowThisFunc(("\n"));
13425
13426#ifdef VBOX_WITH_USB
13427 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13428 AssertComRC(rc);
13429 NOREF(rc);
13430
13431 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13432 AssertReturn(service, E_FAIL);
13433 return service->autoCaptureDevicesForVM(this);
13434#else
13435 return S_OK;
13436#endif
13437}
13438
13439/**
13440 * Removes all machine filters from the USB proxy service and then calls
13441 * Host::detachAllUSBDevices().
13442 *
13443 * Called by Console from the VM process upon normal VM termination or by
13444 * SessionMachine::uninit() upon abnormal VM termination (from under the
13445 * Machine/SessionMachine lock).
13446 *
13447 * @note Locks what called methods lock.
13448 */
13449HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13450{
13451 LogFlowThisFunc(("\n"));
13452
13453#ifdef VBOX_WITH_USB
13454 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13455 AssertComRC(rc);
13456 NOREF(rc);
13457
13458 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13459 AssertReturn(service, E_FAIL);
13460 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13461#else
13462 NOREF(aDone);
13463 return S_OK;
13464#endif
13465}
13466
13467/**
13468 * @note Locks this object for writing.
13469 */
13470HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13471 ComPtr<IProgress> &aProgress)
13472{
13473 LogFlowThisFuncEnter();
13474
13475 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13476 /*
13477 * We don't assert below because it might happen that a non-direct session
13478 * informs us it is closed right after we've been uninitialized -- it's ok.
13479 */
13480
13481 /* get IInternalSessionControl interface */
13482 ComPtr<IInternalSessionControl> control(aSession);
13483
13484 ComAssertRet(!control.isNull(), E_INVALIDARG);
13485
13486 /* Creating a Progress object requires the VirtualBox lock, and
13487 * thus locking it here is required by the lock order rules. */
13488 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13489
13490 if (control == mData->mSession.mDirectControl)
13491 {
13492 /* The direct session is being normally closed by the client process
13493 * ----------------------------------------------------------------- */
13494
13495 /* go to the closing state (essential for all open*Session() calls and
13496 * for #i_checkForDeath()) */
13497 Assert(mData->mSession.mState == SessionState_Locked);
13498 mData->mSession.mState = SessionState_Unlocking;
13499
13500 /* set direct control to NULL to release the remote instance */
13501 mData->mSession.mDirectControl.setNull();
13502 LogFlowThisFunc(("Direct control is set to NULL\n"));
13503
13504 if (mData->mSession.mProgress)
13505 {
13506 /* finalize the progress, someone might wait if a frontend
13507 * closes the session before powering on the VM. */
13508 mData->mSession.mProgress->notifyComplete(E_FAIL,
13509 COM_IIDOF(ISession),
13510 getComponentName(),
13511 tr("The VM session was closed before any attempt to power it on"));
13512 mData->mSession.mProgress.setNull();
13513 }
13514
13515 /* Create the progress object the client will use to wait until
13516 * #i_checkForDeath() is called to uninitialize this session object after
13517 * it releases the IPC semaphore.
13518 * Note! Because we're "reusing" mProgress here, this must be a proxy
13519 * object just like for LaunchVMProcess. */
13520 Assert(mData->mSession.mProgress.isNull());
13521 ComObjPtr<ProgressProxy> progress;
13522 progress.createObject();
13523 ComPtr<IUnknown> pPeer(mPeer);
13524 progress->init(mParent, pPeer,
13525 Bstr(tr("Closing session")).raw(),
13526 FALSE /* aCancelable */);
13527 progress.queryInterfaceTo(aProgress.asOutParam());
13528 mData->mSession.mProgress = progress;
13529 }
13530 else
13531 {
13532 /* the remote session is being normally closed */
13533 bool found = false;
13534 for (Data::Session::RemoteControlList::iterator
13535 it = mData->mSession.mRemoteControls.begin();
13536 it != mData->mSession.mRemoteControls.end();
13537 ++it)
13538 {
13539 if (control == *it)
13540 {
13541 found = true;
13542 // This MUST be erase(it), not remove(*it) as the latter
13543 // triggers a very nasty use after free due to the place where
13544 // the value "lives".
13545 mData->mSession.mRemoteControls.erase(it);
13546 break;
13547 }
13548 }
13549 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13550 E_INVALIDARG);
13551 }
13552
13553 /* signal the client watcher thread, because the client is going away */
13554 mParent->i_updateClientWatcher();
13555
13556 LogFlowThisFuncLeave();
13557 return S_OK;
13558}
13559
13560HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13561{
13562#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13563 ULONG uID;
13564 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13565 if (RT_SUCCESS(rc))
13566 {
13567 if (aID)
13568 *aID = uID;
13569 return S_OK;
13570 }
13571 return E_FAIL;
13572#else
13573 RT_NOREF(aParms, aID);
13574 ReturnComNotImplemented();
13575#endif
13576}
13577
13578HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13579{
13580#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13581 return mParent->i_onClipboardAreaUnregister(aID);
13582#else
13583 RT_NOREF(aID);
13584 ReturnComNotImplemented();
13585#endif
13586}
13587
13588HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13589{
13590#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13591 return mParent->i_onClipboardAreaAttach(aID);
13592#else
13593 RT_NOREF(aID);
13594 ReturnComNotImplemented();
13595#endif
13596}
13597HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13598{
13599#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13600 return mParent->i_onClipboardAreaDetach(aID);
13601#else
13602 RT_NOREF(aID);
13603 ReturnComNotImplemented();
13604#endif
13605}
13606
13607HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13608{
13609#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13610 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13611 if (aID)
13612 *aID = uID;
13613 return S_OK;
13614#else
13615 RT_NOREF(aID);
13616 ReturnComNotImplemented();
13617#endif
13618}
13619
13620HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13621{
13622#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13623 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13624 if (aRefCount)
13625 *aRefCount = uRefCount;
13626 return S_OK;
13627#else
13628 RT_NOREF(aID, aRefCount);
13629 ReturnComNotImplemented();
13630#endif
13631}
13632
13633HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13634 std::vector<com::Utf8Str> &aValues,
13635 std::vector<LONG64> &aTimestamps,
13636 std::vector<com::Utf8Str> &aFlags)
13637{
13638 LogFlowThisFunc(("\n"));
13639
13640#ifdef VBOX_WITH_GUEST_PROPS
13641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13642
13643 size_t cEntries = mHWData->mGuestProperties.size();
13644 aNames.resize(cEntries);
13645 aValues.resize(cEntries);
13646 aTimestamps.resize(cEntries);
13647 aFlags.resize(cEntries);
13648
13649 size_t i = 0;
13650 for (HWData::GuestPropertyMap::const_iterator
13651 it = mHWData->mGuestProperties.begin();
13652 it != mHWData->mGuestProperties.end();
13653 ++it, ++i)
13654 {
13655 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13656 aNames[i] = it->first;
13657 aValues[i] = it->second.strValue;
13658 aTimestamps[i] = it->second.mTimestamp;
13659
13660 /* If it is NULL, keep it NULL. */
13661 if (it->second.mFlags)
13662 {
13663 GuestPropWriteFlags(it->second.mFlags, szFlags);
13664 aFlags[i] = szFlags;
13665 }
13666 else
13667 aFlags[i] = "";
13668 }
13669 return S_OK;
13670#else
13671 ReturnComNotImplemented();
13672#endif
13673}
13674
13675HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13676 const com::Utf8Str &aValue,
13677 LONG64 aTimestamp,
13678 const com::Utf8Str &aFlags)
13679{
13680 LogFlowThisFunc(("\n"));
13681
13682#ifdef VBOX_WITH_GUEST_PROPS
13683 try
13684 {
13685 /*
13686 * Convert input up front.
13687 */
13688 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13689 if (aFlags.length())
13690 {
13691 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13692 AssertRCReturn(vrc, E_INVALIDARG);
13693 }
13694
13695 /*
13696 * Now grab the object lock, validate the state and do the update.
13697 */
13698
13699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13700
13701 if (!Global::IsOnline(mData->mMachineState))
13702 {
13703 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13704 VBOX_E_INVALID_VM_STATE);
13705 }
13706
13707 i_setModified(IsModified_MachineData);
13708 mHWData.backup();
13709
13710 bool fDelete = !aValue.length();
13711 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13712 if (it != mHWData->mGuestProperties.end())
13713 {
13714 if (!fDelete)
13715 {
13716 it->second.strValue = aValue;
13717 it->second.mTimestamp = aTimestamp;
13718 it->second.mFlags = fFlags;
13719 }
13720 else
13721 mHWData->mGuestProperties.erase(it);
13722
13723 mData->mGuestPropertiesModified = TRUE;
13724 }
13725 else if (!fDelete)
13726 {
13727 HWData::GuestProperty prop;
13728 prop.strValue = aValue;
13729 prop.mTimestamp = aTimestamp;
13730 prop.mFlags = fFlags;
13731
13732 mHWData->mGuestProperties[aName] = prop;
13733 mData->mGuestPropertiesModified = TRUE;
13734 }
13735
13736 alock.release();
13737
13738 mParent->i_onGuestPropertyChange(mData->mUuid,
13739 Bstr(aName).raw(),
13740 Bstr(aValue).raw(),
13741 Bstr(aFlags).raw());
13742 }
13743 catch (...)
13744 {
13745 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13746 }
13747 return S_OK;
13748#else
13749 ReturnComNotImplemented();
13750#endif
13751}
13752
13753
13754HRESULT SessionMachine::lockMedia()
13755{
13756 AutoMultiWriteLock2 alock(this->lockHandle(),
13757 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13758
13759 AssertReturn( mData->mMachineState == MachineState_Starting
13760 || mData->mMachineState == MachineState_Restoring
13761 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13762
13763 clearError();
13764 alock.release();
13765 return i_lockMedia();
13766}
13767
13768HRESULT SessionMachine::unlockMedia()
13769{
13770 HRESULT hrc = i_unlockMedia();
13771 return hrc;
13772}
13773
13774HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13775 ComPtr<IMediumAttachment> &aNewAttachment)
13776{
13777 // request the host lock first, since might be calling Host methods for getting host drives;
13778 // next, protect the media tree all the while we're in here, as well as our member variables
13779 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13780 this->lockHandle(),
13781 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13782
13783 IMediumAttachment *iAttach = aAttachment;
13784 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13785
13786 Utf8Str ctrlName;
13787 LONG lPort;
13788 LONG lDevice;
13789 bool fTempEject;
13790 {
13791 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13792
13793 /* Need to query the details first, as the IMediumAttachment reference
13794 * might be to the original settings, which we are going to change. */
13795 ctrlName = pAttach->i_getControllerName();
13796 lPort = pAttach->i_getPort();
13797 lDevice = pAttach->i_getDevice();
13798 fTempEject = pAttach->i_getTempEject();
13799 }
13800
13801 if (!fTempEject)
13802 {
13803 /* Remember previously mounted medium. The medium before taking the
13804 * backup is not necessarily the same thing. */
13805 ComObjPtr<Medium> oldmedium;
13806 oldmedium = pAttach->i_getMedium();
13807
13808 i_setModified(IsModified_Storage);
13809 mMediumAttachments.backup();
13810
13811 // The backup operation makes the pAttach reference point to the
13812 // old settings. Re-get the correct reference.
13813 pAttach = i_findAttachment(*mMediumAttachments.data(),
13814 ctrlName,
13815 lPort,
13816 lDevice);
13817
13818 {
13819 AutoCaller autoAttachCaller(this);
13820 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13821
13822 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13823 if (!oldmedium.isNull())
13824 oldmedium->i_removeBackReference(mData->mUuid);
13825
13826 pAttach->i_updateMedium(NULL);
13827 pAttach->i_updateEjected();
13828 }
13829
13830 i_setModified(IsModified_Storage);
13831 }
13832 else
13833 {
13834 {
13835 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13836 pAttach->i_updateEjected();
13837 }
13838 }
13839
13840 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13841
13842 return S_OK;
13843}
13844
13845HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13846 com::Utf8Str &aResult)
13847{
13848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13849
13850 HRESULT hr = S_OK;
13851
13852 if (!mAuthLibCtx.hAuthLibrary)
13853 {
13854 /* Load the external authentication library. */
13855 Bstr authLibrary;
13856 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13857
13858 Utf8Str filename = authLibrary;
13859
13860 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13861 if (RT_FAILURE(vrc))
13862 hr = setErrorBoth(E_FAIL, vrc,
13863 tr("Could not load the external authentication library '%s' (%Rrc)"),
13864 filename.c_str(), vrc);
13865 }
13866
13867 /* The auth library might need the machine lock. */
13868 alock.release();
13869
13870 if (FAILED(hr))
13871 return hr;
13872
13873 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13874 {
13875 enum VRDEAuthParams
13876 {
13877 parmUuid = 1,
13878 parmGuestJudgement,
13879 parmUser,
13880 parmPassword,
13881 parmDomain,
13882 parmClientId
13883 };
13884
13885 AuthResult result = AuthResultAccessDenied;
13886
13887 Guid uuid(aAuthParams[parmUuid]);
13888 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13889 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13890
13891 result = AuthLibAuthenticate(&mAuthLibCtx,
13892 uuid.raw(), guestJudgement,
13893 aAuthParams[parmUser].c_str(),
13894 aAuthParams[parmPassword].c_str(),
13895 aAuthParams[parmDomain].c_str(),
13896 u32ClientId);
13897
13898 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13899 size_t cbPassword = aAuthParams[parmPassword].length();
13900 if (cbPassword)
13901 {
13902 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13903 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13904 }
13905
13906 if (result == AuthResultAccessGranted)
13907 aResult = "granted";
13908 else
13909 aResult = "denied";
13910
13911 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13912 aAuthParams[parmUser].c_str(), aResult.c_str()));
13913 }
13914 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13915 {
13916 enum VRDEAuthDisconnectParams
13917 {
13918 parmUuid = 1,
13919 parmClientId
13920 };
13921
13922 Guid uuid(aAuthParams[parmUuid]);
13923 uint32_t u32ClientId = 0;
13924 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13925 }
13926 else
13927 {
13928 hr = E_INVALIDARG;
13929 }
13930
13931 return hr;
13932}
13933
13934// public methods only for internal purposes
13935/////////////////////////////////////////////////////////////////////////////
13936
13937#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13938/**
13939 * Called from the client watcher thread to check for expected or unexpected
13940 * death of the client process that has a direct session to this machine.
13941 *
13942 * On Win32 and on OS/2, this method is called only when we've got the
13943 * mutex (i.e. the client has either died or terminated normally) so it always
13944 * returns @c true (the client is terminated, the session machine is
13945 * uninitialized).
13946 *
13947 * On other platforms, the method returns @c true if the client process has
13948 * terminated normally or abnormally and the session machine was uninitialized,
13949 * and @c false if the client process is still alive.
13950 *
13951 * @note Locks this object for writing.
13952 */
13953bool SessionMachine::i_checkForDeath()
13954{
13955 Uninit::Reason reason;
13956 bool terminated = false;
13957
13958 /* Enclose autoCaller with a block because calling uninit() from under it
13959 * will deadlock. */
13960 {
13961 AutoCaller autoCaller(this);
13962 if (!autoCaller.isOk())
13963 {
13964 /* return true if not ready, to cause the client watcher to exclude
13965 * the corresponding session from watching */
13966 LogFlowThisFunc(("Already uninitialized!\n"));
13967 return true;
13968 }
13969
13970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13971
13972 /* Determine the reason of death: if the session state is Closing here,
13973 * everything is fine. Otherwise it means that the client did not call
13974 * OnSessionEnd() before it released the IPC semaphore. This may happen
13975 * either because the client process has abnormally terminated, or
13976 * because it simply forgot to call ISession::Close() before exiting. We
13977 * threat the latter also as an abnormal termination (see
13978 * Session::uninit() for details). */
13979 reason = mData->mSession.mState == SessionState_Unlocking ?
13980 Uninit::Normal :
13981 Uninit::Abnormal;
13982
13983 if (mClientToken)
13984 terminated = mClientToken->release();
13985 } /* AutoCaller block */
13986
13987 if (terminated)
13988 uninit(reason);
13989
13990 return terminated;
13991}
13992
13993void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13994{
13995 LogFlowThisFunc(("\n"));
13996
13997 strTokenId.setNull();
13998
13999 AutoCaller autoCaller(this);
14000 AssertComRCReturnVoid(autoCaller.rc());
14001
14002 Assert(mClientToken);
14003 if (mClientToken)
14004 mClientToken->getId(strTokenId);
14005}
14006#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14007IToken *SessionMachine::i_getToken()
14008{
14009 LogFlowThisFunc(("\n"));
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturn(autoCaller.rc(), NULL);
14013
14014 Assert(mClientToken);
14015 if (mClientToken)
14016 return mClientToken->getToken();
14017 else
14018 return NULL;
14019}
14020#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14021
14022Machine::ClientToken *SessionMachine::i_getClientToken()
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), NULL);
14028
14029 return mClientToken;
14030}
14031
14032
14033/**
14034 * @note Locks this object for reading.
14035 */
14036HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14037{
14038 LogFlowThisFunc(("\n"));
14039
14040 AutoCaller autoCaller(this);
14041 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14042
14043 ComPtr<IInternalSessionControl> directControl;
14044 {
14045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14046 if (mData->mSession.mLockType == LockType_VM)
14047 directControl = mData->mSession.mDirectControl;
14048 }
14049
14050 /* ignore notifications sent after #OnSessionEnd() is called */
14051 if (!directControl)
14052 return S_OK;
14053
14054 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14055}
14056
14057/**
14058 * @note Locks this object for reading.
14059 */
14060HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14061 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14062 IN_BSTR aGuestIp, LONG aGuestPort)
14063{
14064 LogFlowThisFunc(("\n"));
14065
14066 AutoCaller autoCaller(this);
14067 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14068
14069 ComPtr<IInternalSessionControl> directControl;
14070 {
14071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14072 if (mData->mSession.mLockType == LockType_VM)
14073 directControl = mData->mSession.mDirectControl;
14074 }
14075
14076 /* ignore notifications sent after #OnSessionEnd() is called */
14077 if (!directControl)
14078 return S_OK;
14079 /*
14080 * instead acting like callback we ask IVirtualBox deliver corresponding event
14081 */
14082
14083 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14084 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14085 return S_OK;
14086}
14087
14088/**
14089 * @note Locks this object for reading.
14090 */
14091HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14092{
14093 LogFlowThisFunc(("\n"));
14094
14095 AutoCaller autoCaller(this);
14096 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14097
14098 ComPtr<IInternalSessionControl> directControl;
14099 {
14100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14101 if (mData->mSession.mLockType == LockType_VM)
14102 directControl = mData->mSession.mDirectControl;
14103 }
14104
14105 /* ignore notifications sent after #OnSessionEnd() is called */
14106 if (!directControl)
14107 return S_OK;
14108
14109 return directControl->OnAudioAdapterChange(audioAdapter);
14110}
14111
14112/**
14113 * @note Locks this object for reading.
14114 */
14115HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14116{
14117 LogFlowThisFunc(("\n"));
14118
14119 AutoCaller autoCaller(this);
14120 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14121
14122 ComPtr<IInternalSessionControl> directControl;
14123 {
14124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14125 if (mData->mSession.mLockType == LockType_VM)
14126 directControl = mData->mSession.mDirectControl;
14127 }
14128
14129 /* ignore notifications sent after #OnSessionEnd() is called */
14130 if (!directControl)
14131 return S_OK;
14132
14133 return directControl->OnSerialPortChange(serialPort);
14134}
14135
14136/**
14137 * @note Locks this object for reading.
14138 */
14139HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14145
14146 ComPtr<IInternalSessionControl> directControl;
14147 {
14148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14149 if (mData->mSession.mLockType == LockType_VM)
14150 directControl = mData->mSession.mDirectControl;
14151 }
14152
14153 /* ignore notifications sent after #OnSessionEnd() is called */
14154 if (!directControl)
14155 return S_OK;
14156
14157 return directControl->OnParallelPortChange(parallelPort);
14158}
14159
14160/**
14161 * @note Locks this object for reading.
14162 */
14163HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14164{
14165 LogFlowThisFunc(("\n"));
14166
14167 AutoCaller autoCaller(this);
14168 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14169
14170 ComPtr<IInternalSessionControl> directControl;
14171 {
14172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14173 if (mData->mSession.mLockType == LockType_VM)
14174 directControl = mData->mSession.mDirectControl;
14175 }
14176
14177 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14178
14179 /* ignore notifications sent after #OnSessionEnd() is called */
14180 if (!directControl)
14181 return S_OK;
14182
14183 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14184}
14185
14186/**
14187 * @note Locks this object for reading.
14188 */
14189HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14190{
14191 LogFlowThisFunc(("\n"));
14192
14193 AutoCaller autoCaller(this);
14194 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14195
14196 ComPtr<IInternalSessionControl> directControl;
14197 {
14198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14199 if (mData->mSession.mLockType == LockType_VM)
14200 directControl = mData->mSession.mDirectControl;
14201 }
14202
14203 mParent->i_onMediumChanged(aAttachment);
14204
14205 /* ignore notifications sent after #OnSessionEnd() is called */
14206 if (!directControl)
14207 return S_OK;
14208
14209 return directControl->OnMediumChange(aAttachment, aForce);
14210}
14211
14212HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14213{
14214 LogFlowThisFunc(("\n"));
14215
14216 AutoCaller autoCaller(this);
14217 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14218
14219 ComPtr<IInternalSessionControl> directControl;
14220 {
14221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14222 if (mData->mSession.mLockType == LockType_VM)
14223 directControl = mData->mSession.mDirectControl;
14224 }
14225
14226 /* ignore notifications sent after #OnSessionEnd() is called */
14227 if (!directControl)
14228 return S_OK;
14229
14230 return directControl->OnVMProcessPriorityChange(aPriority);
14231}
14232
14233/**
14234 * @note Locks this object for reading.
14235 */
14236HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14237{
14238 LogFlowThisFunc(("\n"));
14239
14240 AutoCaller autoCaller(this);
14241 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14242
14243 ComPtr<IInternalSessionControl> directControl;
14244 {
14245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14246 if (mData->mSession.mLockType == LockType_VM)
14247 directControl = mData->mSession.mDirectControl;
14248 }
14249
14250 /* ignore notifications sent after #OnSessionEnd() is called */
14251 if (!directControl)
14252 return S_OK;
14253
14254 return directControl->OnCPUChange(aCPU, aRemove);
14255}
14256
14257HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14267 if (mData->mSession.mLockType == LockType_VM)
14268 directControl = mData->mSession.mDirectControl;
14269 }
14270
14271 /* ignore notifications sent after #OnSessionEnd() is called */
14272 if (!directControl)
14273 return S_OK;
14274
14275 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14276}
14277
14278/**
14279 * @note Locks this object for reading.
14280 */
14281HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14282{
14283 LogFlowThisFunc(("\n"));
14284
14285 AutoCaller autoCaller(this);
14286 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14287
14288 ComPtr<IInternalSessionControl> directControl;
14289 {
14290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14291 if (mData->mSession.mLockType == LockType_VM)
14292 directControl = mData->mSession.mDirectControl;
14293 }
14294
14295 /* ignore notifications sent after #OnSessionEnd() is called */
14296 if (!directControl)
14297 return S_OK;
14298
14299 return directControl->OnVRDEServerChange(aRestart);
14300}
14301
14302/**
14303 * @note Locks this object for reading.
14304 */
14305HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14306{
14307 LogFlowThisFunc(("\n"));
14308
14309 AutoCaller autoCaller(this);
14310 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14311
14312 ComPtr<IInternalSessionControl> directControl;
14313 {
14314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14315 if (mData->mSession.mLockType == LockType_VM)
14316 directControl = mData->mSession.mDirectControl;
14317 }
14318
14319 /* ignore notifications sent after #OnSessionEnd() is called */
14320 if (!directControl)
14321 return S_OK;
14322
14323 return directControl->OnRecordingChange(aEnable);
14324}
14325
14326/**
14327 * @note Locks this object for reading.
14328 */
14329HRESULT SessionMachine::i_onUSBControllerChange()
14330{
14331 LogFlowThisFunc(("\n"));
14332
14333 AutoCaller autoCaller(this);
14334 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14335
14336 ComPtr<IInternalSessionControl> directControl;
14337 {
14338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14339 if (mData->mSession.mLockType == LockType_VM)
14340 directControl = mData->mSession.mDirectControl;
14341 }
14342
14343 /* ignore notifications sent after #OnSessionEnd() is called */
14344 if (!directControl)
14345 return S_OK;
14346
14347 return directControl->OnUSBControllerChange();
14348}
14349
14350/**
14351 * @note Locks this object for reading.
14352 */
14353HRESULT SessionMachine::i_onSharedFolderChange()
14354{
14355 LogFlowThisFunc(("\n"));
14356
14357 AutoCaller autoCaller(this);
14358 AssertComRCReturnRC(autoCaller.rc());
14359
14360 ComPtr<IInternalSessionControl> directControl;
14361 {
14362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14363 if (mData->mSession.mLockType == LockType_VM)
14364 directControl = mData->mSession.mDirectControl;
14365 }
14366
14367 /* ignore notifications sent after #OnSessionEnd() is called */
14368 if (!directControl)
14369 return S_OK;
14370
14371 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14372}
14373
14374/**
14375 * @note Locks this object for reading.
14376 */
14377HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14378{
14379 LogFlowThisFunc(("\n"));
14380
14381 AutoCaller autoCaller(this);
14382 AssertComRCReturnRC(autoCaller.rc());
14383
14384 ComPtr<IInternalSessionControl> directControl;
14385 {
14386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14387 if (mData->mSession.mLockType == LockType_VM)
14388 directControl = mData->mSession.mDirectControl;
14389 }
14390
14391 /* ignore notifications sent after #OnSessionEnd() is called */
14392 if (!directControl)
14393 return S_OK;
14394
14395 return directControl->OnClipboardModeChange(aClipboardMode);
14396}
14397
14398/**
14399 * @note Locks this object for reading.
14400 */
14401HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14402{
14403 LogFlowThisFunc(("\n"));
14404
14405 AutoCaller autoCaller(this);
14406 AssertComRCReturnRC(autoCaller.rc());
14407
14408 ComPtr<IInternalSessionControl> directControl;
14409 {
14410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14411 if (mData->mSession.mLockType == LockType_VM)
14412 directControl = mData->mSession.mDirectControl;
14413 }
14414
14415 /* ignore notifications sent after #OnSessionEnd() is called */
14416 if (!directControl)
14417 return S_OK;
14418
14419 return directControl->OnClipboardFileTransferModeChange(aEnable);
14420}
14421
14422/**
14423 * @note Locks this object for reading.
14424 */
14425HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14426{
14427 LogFlowThisFunc(("\n"));
14428
14429 AutoCaller autoCaller(this);
14430 AssertComRCReturnRC(autoCaller.rc());
14431
14432 ComPtr<IInternalSessionControl> directControl;
14433 {
14434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14435 if (mData->mSession.mLockType == LockType_VM)
14436 directControl = mData->mSession.mDirectControl;
14437 }
14438
14439 /* ignore notifications sent after #OnSessionEnd() is called */
14440 if (!directControl)
14441 return S_OK;
14442
14443 return directControl->OnDnDModeChange(aDnDMode);
14444}
14445
14446/**
14447 * @note Locks this object for reading.
14448 */
14449HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14450{
14451 LogFlowThisFunc(("\n"));
14452
14453 AutoCaller autoCaller(this);
14454 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14455
14456 ComPtr<IInternalSessionControl> directControl;
14457 {
14458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14459 if (mData->mSession.mLockType == LockType_VM)
14460 directControl = mData->mSession.mDirectControl;
14461 }
14462
14463 /* ignore notifications sent after #OnSessionEnd() is called */
14464 if (!directControl)
14465 return S_OK;
14466
14467 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14468}
14469
14470/**
14471 * @note Locks this object for reading.
14472 */
14473HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14474{
14475 LogFlowThisFunc(("\n"));
14476
14477 AutoCaller autoCaller(this);
14478 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14479
14480 ComPtr<IInternalSessionControl> directControl;
14481 {
14482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14483 if (mData->mSession.mLockType == LockType_VM)
14484 directControl = mData->mSession.mDirectControl;
14485 }
14486
14487 /* ignore notifications sent after #OnSessionEnd() is called */
14488 if (!directControl)
14489 return S_OK;
14490
14491 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14492}
14493
14494/**
14495 * Returns @c true if this machine's USB controller reports it has a matching
14496 * filter for the given USB device and @c false otherwise.
14497 *
14498 * @note locks this object for reading.
14499 */
14500bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14501{
14502 AutoCaller autoCaller(this);
14503 /* silently return if not ready -- this method may be called after the
14504 * direct machine session has been called */
14505 if (!autoCaller.isOk())
14506 return false;
14507
14508#ifdef VBOX_WITH_USB
14509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14510
14511 switch (mData->mMachineState)
14512 {
14513 case MachineState_Starting:
14514 case MachineState_Restoring:
14515 case MachineState_TeleportingIn:
14516 case MachineState_Paused:
14517 case MachineState_Running:
14518 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14519 * elsewhere... */
14520 alock.release();
14521 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14522 default: break;
14523 }
14524#else
14525 NOREF(aDevice);
14526 NOREF(aMaskedIfs);
14527#endif
14528 return false;
14529}
14530
14531/**
14532 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14533 */
14534HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14535 IVirtualBoxErrorInfo *aError,
14536 ULONG aMaskedIfs,
14537 const com::Utf8Str &aCaptureFilename)
14538{
14539 LogFlowThisFunc(("\n"));
14540
14541 AutoCaller autoCaller(this);
14542
14543 /* This notification may happen after the machine object has been
14544 * uninitialized (the session was closed), so don't assert. */
14545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14546
14547 ComPtr<IInternalSessionControl> directControl;
14548 {
14549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14550 if (mData->mSession.mLockType == LockType_VM)
14551 directControl = mData->mSession.mDirectControl;
14552 }
14553
14554 /* fail on notifications sent after #OnSessionEnd() is called, it is
14555 * expected by the caller */
14556 if (!directControl)
14557 return E_FAIL;
14558
14559 /* No locks should be held at this point. */
14560 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14561 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14562
14563 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14564}
14565
14566/**
14567 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14568 */
14569HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14570 IVirtualBoxErrorInfo *aError)
14571{
14572 LogFlowThisFunc(("\n"));
14573
14574 AutoCaller autoCaller(this);
14575
14576 /* This notification may happen after the machine object has been
14577 * uninitialized (the session was closed), so don't assert. */
14578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14579
14580 ComPtr<IInternalSessionControl> directControl;
14581 {
14582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14583 if (mData->mSession.mLockType == LockType_VM)
14584 directControl = mData->mSession.mDirectControl;
14585 }
14586
14587 /* fail on notifications sent after #OnSessionEnd() is called, it is
14588 * expected by the caller */
14589 if (!directControl)
14590 return E_FAIL;
14591
14592 /* No locks should be held at this point. */
14593 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14594 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14595
14596 return directControl->OnUSBDeviceDetach(aId, aError);
14597}
14598
14599// protected methods
14600/////////////////////////////////////////////////////////////////////////////
14601
14602/**
14603 * Deletes the given file if it is no longer in use by either the current machine state
14604 * (if the machine is "saved") or any of the machine's snapshots.
14605 *
14606 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14607 * but is different for each SnapshotMachine. When calling this, the order of calling this
14608 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14609 * is therefore critical. I know, it's all rather messy.
14610 *
14611 * @param strStateFile
14612 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14613 * the test for whether the saved state file is in use.
14614 */
14615void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14616 Snapshot *pSnapshotToIgnore)
14617{
14618 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14619 if ( (strStateFile.isNotEmpty())
14620 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14621 )
14622 // ... and it must also not be shared with other snapshots
14623 if ( !mData->mFirstSnapshot
14624 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14625 // this checks the SnapshotMachine's state file paths
14626 )
14627 RTFileDelete(strStateFile.c_str());
14628}
14629
14630/**
14631 * Locks the attached media.
14632 *
14633 * All attached hard disks are locked for writing and DVD/floppy are locked for
14634 * reading. Parents of attached hard disks (if any) are locked for reading.
14635 *
14636 * This method also performs accessibility check of all media it locks: if some
14637 * media is inaccessible, the method will return a failure and a bunch of
14638 * extended error info objects per each inaccessible medium.
14639 *
14640 * Note that this method is atomic: if it returns a success, all media are
14641 * locked as described above; on failure no media is locked at all (all
14642 * succeeded individual locks will be undone).
14643 *
14644 * The caller is responsible for doing the necessary state sanity checks.
14645 *
14646 * The locks made by this method must be undone by calling #unlockMedia() when
14647 * no more needed.
14648 */
14649HRESULT SessionMachine::i_lockMedia()
14650{
14651 AutoCaller autoCaller(this);
14652 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14653
14654 AutoMultiWriteLock2 alock(this->lockHandle(),
14655 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14656
14657 /* bail out if trying to lock things with already set up locking */
14658 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14659
14660 MultiResult mrc(S_OK);
14661
14662 /* Collect locking information for all medium objects attached to the VM. */
14663 for (MediumAttachmentList::const_iterator
14664 it = mMediumAttachments->begin();
14665 it != mMediumAttachments->end();
14666 ++it)
14667 {
14668 MediumAttachment *pAtt = *it;
14669 DeviceType_T devType = pAtt->i_getType();
14670 Medium *pMedium = pAtt->i_getMedium();
14671
14672 MediumLockList *pMediumLockList(new MediumLockList());
14673 // There can be attachments without a medium (floppy/dvd), and thus
14674 // it's impossible to create a medium lock list. It still makes sense
14675 // to have the empty medium lock list in the map in case a medium is
14676 // attached later.
14677 if (pMedium != NULL)
14678 {
14679 MediumType_T mediumType = pMedium->i_getType();
14680 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14681 || mediumType == MediumType_Shareable;
14682 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14683
14684 alock.release();
14685 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14686 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14687 false /* fMediumLockWriteAll */,
14688 NULL,
14689 *pMediumLockList);
14690 alock.acquire();
14691 if (FAILED(mrc))
14692 {
14693 delete pMediumLockList;
14694 mData->mSession.mLockedMedia.Clear();
14695 break;
14696 }
14697 }
14698
14699 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14700 if (FAILED(rc))
14701 {
14702 mData->mSession.mLockedMedia.Clear();
14703 mrc = setError(rc,
14704 tr("Collecting locking information for all attached media failed"));
14705 break;
14706 }
14707 }
14708
14709 if (SUCCEEDED(mrc))
14710 {
14711 /* Now lock all media. If this fails, nothing is locked. */
14712 alock.release();
14713 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14714 alock.acquire();
14715 if (FAILED(rc))
14716 {
14717 mrc = setError(rc,
14718 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14719 }
14720 }
14721
14722 return mrc;
14723}
14724
14725/**
14726 * Undoes the locks made by by #lockMedia().
14727 */
14728HRESULT SessionMachine::i_unlockMedia()
14729{
14730 AutoCaller autoCaller(this);
14731 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14732
14733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14734
14735 /* we may be holding important error info on the current thread;
14736 * preserve it */
14737 ErrorInfoKeeper eik;
14738
14739 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14740 AssertComRC(rc);
14741 return rc;
14742}
14743
14744/**
14745 * Helper to change the machine state (reimplementation).
14746 *
14747 * @note Locks this object for writing.
14748 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14749 * it can cause crashes in random places due to unexpectedly committing
14750 * the current settings. The caller is responsible for that. The call
14751 * to saveStateSettings is fine, because this method does not commit.
14752 */
14753HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14754{
14755 LogFlowThisFuncEnter();
14756 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14757
14758 AutoCaller autoCaller(this);
14759 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14760
14761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14762
14763 MachineState_T oldMachineState = mData->mMachineState;
14764
14765 AssertMsgReturn(oldMachineState != aMachineState,
14766 ("oldMachineState=%s, aMachineState=%s\n",
14767 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14768 E_FAIL);
14769
14770 HRESULT rc = S_OK;
14771
14772 int stsFlags = 0;
14773 bool deleteSavedState = false;
14774
14775 /* detect some state transitions */
14776
14777 if ( ( oldMachineState == MachineState_Saved
14778 && aMachineState == MachineState_Restoring)
14779 || ( ( oldMachineState == MachineState_PoweredOff
14780 || oldMachineState == MachineState_Teleported
14781 || oldMachineState == MachineState_Aborted
14782 )
14783 && ( aMachineState == MachineState_TeleportingIn
14784 || aMachineState == MachineState_Starting
14785 )
14786 )
14787 )
14788 {
14789 /* The EMT thread is about to start */
14790
14791 /* Nothing to do here for now... */
14792
14793 /// @todo NEWMEDIA don't let mDVDDrive and other children
14794 /// change anything when in the Starting/Restoring state
14795 }
14796 else if ( ( oldMachineState == MachineState_Running
14797 || oldMachineState == MachineState_Paused
14798 || oldMachineState == MachineState_Teleporting
14799 || oldMachineState == MachineState_OnlineSnapshotting
14800 || oldMachineState == MachineState_LiveSnapshotting
14801 || oldMachineState == MachineState_Stuck
14802 || oldMachineState == MachineState_Starting
14803 || oldMachineState == MachineState_Stopping
14804 || oldMachineState == MachineState_Saving
14805 || oldMachineState == MachineState_Restoring
14806 || oldMachineState == MachineState_TeleportingPausedVM
14807 || oldMachineState == MachineState_TeleportingIn
14808 )
14809 && ( aMachineState == MachineState_PoweredOff
14810 || aMachineState == MachineState_Saved
14811 || aMachineState == MachineState_Teleported
14812 || aMachineState == MachineState_Aborted
14813 )
14814 )
14815 {
14816 /* The EMT thread has just stopped, unlock attached media. Note that as
14817 * opposed to locking that is done from Console, we do unlocking here
14818 * because the VM process may have aborted before having a chance to
14819 * properly unlock all media it locked. */
14820
14821 unlockMedia();
14822 }
14823
14824 if (oldMachineState == MachineState_Restoring)
14825 {
14826 if (aMachineState != MachineState_Saved)
14827 {
14828 /*
14829 * delete the saved state file once the machine has finished
14830 * restoring from it (note that Console sets the state from
14831 * Restoring to Saved if the VM couldn't restore successfully,
14832 * to give the user an ability to fix an error and retry --
14833 * we keep the saved state file in this case)
14834 */
14835 deleteSavedState = true;
14836 }
14837 }
14838 else if ( oldMachineState == MachineState_Saved
14839 && ( aMachineState == MachineState_PoweredOff
14840 || aMachineState == MachineState_Aborted
14841 || aMachineState == MachineState_Teleported
14842 )
14843 )
14844 {
14845 /*
14846 * delete the saved state after SessionMachine::ForgetSavedState() is called
14847 * or if the VM process (owning a direct VM session) crashed while the
14848 * VM was Saved
14849 */
14850
14851 /// @todo (dmik)
14852 // Not sure that deleting the saved state file just because of the
14853 // client death before it attempted to restore the VM is a good
14854 // thing. But when it crashes we need to go to the Aborted state
14855 // which cannot have the saved state file associated... The only
14856 // way to fix this is to make the Aborted condition not a VM state
14857 // but a bool flag: i.e., when a crash occurs, set it to true and
14858 // change the state to PoweredOff or Saved depending on the
14859 // saved state presence.
14860
14861 deleteSavedState = true;
14862 mData->mCurrentStateModified = TRUE;
14863 stsFlags |= SaveSTS_CurStateModified;
14864 }
14865
14866 if ( aMachineState == MachineState_Starting
14867 || aMachineState == MachineState_Restoring
14868 || aMachineState == MachineState_TeleportingIn
14869 )
14870 {
14871 /* set the current state modified flag to indicate that the current
14872 * state is no more identical to the state in the
14873 * current snapshot */
14874 if (!mData->mCurrentSnapshot.isNull())
14875 {
14876 mData->mCurrentStateModified = TRUE;
14877 stsFlags |= SaveSTS_CurStateModified;
14878 }
14879 }
14880
14881 if (deleteSavedState)
14882 {
14883 if (mRemoveSavedState)
14884 {
14885 Assert(!mSSData->strStateFilePath.isEmpty());
14886
14887 // it is safe to delete the saved state file if ...
14888 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14889 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14890 // ... none of the snapshots share the saved state file
14891 )
14892 RTFileDelete(mSSData->strStateFilePath.c_str());
14893 }
14894
14895 mSSData->strStateFilePath.setNull();
14896 stsFlags |= SaveSTS_StateFilePath;
14897 }
14898
14899 /* redirect to the underlying peer machine */
14900 mPeer->i_setMachineState(aMachineState);
14901
14902 if ( oldMachineState != MachineState_RestoringSnapshot
14903 && ( aMachineState == MachineState_PoweredOff
14904 || aMachineState == MachineState_Teleported
14905 || aMachineState == MachineState_Aborted
14906 || aMachineState == MachineState_Saved))
14907 {
14908 /* the machine has stopped execution
14909 * (or the saved state file was adopted) */
14910 stsFlags |= SaveSTS_StateTimeStamp;
14911 }
14912
14913 if ( ( oldMachineState == MachineState_PoweredOff
14914 || oldMachineState == MachineState_Aborted
14915 || oldMachineState == MachineState_Teleported
14916 )
14917 && aMachineState == MachineState_Saved)
14918 {
14919 /* the saved state file was adopted */
14920 Assert(!mSSData->strStateFilePath.isEmpty());
14921 stsFlags |= SaveSTS_StateFilePath;
14922 }
14923
14924#ifdef VBOX_WITH_GUEST_PROPS
14925 if ( aMachineState == MachineState_PoweredOff
14926 || aMachineState == MachineState_Aborted
14927 || aMachineState == MachineState_Teleported)
14928 {
14929 /* Make sure any transient guest properties get removed from the
14930 * property store on shutdown. */
14931 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14932
14933 /* remove it from the settings representation */
14934 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14935 for (settings::GuestPropertiesList::iterator
14936 it = llGuestProperties.begin();
14937 it != llGuestProperties.end();
14938 /*nothing*/)
14939 {
14940 const settings::GuestProperty &prop = *it;
14941 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14942 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14943 {
14944 it = llGuestProperties.erase(it);
14945 fNeedsSaving = true;
14946 }
14947 else
14948 {
14949 ++it;
14950 }
14951 }
14952
14953 /* Additionally remove it from the HWData representation. Required to
14954 * keep everything in sync, as this is what the API keeps using. */
14955 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14956 for (HWData::GuestPropertyMap::iterator
14957 it = llHWGuestProperties.begin();
14958 it != llHWGuestProperties.end();
14959 /*nothing*/)
14960 {
14961 uint32_t fFlags = it->second.mFlags;
14962 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14963 {
14964 /* iterator where we need to continue after the erase call
14965 * (C++03 is a fact still, and it doesn't return the iterator
14966 * which would allow continuing) */
14967 HWData::GuestPropertyMap::iterator it2 = it;
14968 ++it2;
14969 llHWGuestProperties.erase(it);
14970 it = it2;
14971 fNeedsSaving = true;
14972 }
14973 else
14974 {
14975 ++it;
14976 }
14977 }
14978
14979 if (fNeedsSaving)
14980 {
14981 mData->mCurrentStateModified = TRUE;
14982 stsFlags |= SaveSTS_CurStateModified;
14983 }
14984 }
14985#endif /* VBOX_WITH_GUEST_PROPS */
14986
14987 rc = i_saveStateSettings(stsFlags);
14988
14989 if ( ( oldMachineState != MachineState_PoweredOff
14990 && oldMachineState != MachineState_Aborted
14991 && oldMachineState != MachineState_Teleported
14992 )
14993 && ( aMachineState == MachineState_PoweredOff
14994 || aMachineState == MachineState_Aborted
14995 || aMachineState == MachineState_Teleported
14996 )
14997 )
14998 {
14999 /* we've been shut down for any reason */
15000 /* no special action so far */
15001 }
15002
15003 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15004 LogFlowThisFuncLeave();
15005 return rc;
15006}
15007
15008/**
15009 * Sends the current machine state value to the VM process.
15010 *
15011 * @note Locks this object for reading, then calls a client process.
15012 */
15013HRESULT SessionMachine::i_updateMachineStateOnClient()
15014{
15015 AutoCaller autoCaller(this);
15016 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15017
15018 ComPtr<IInternalSessionControl> directControl;
15019 {
15020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15021 AssertReturn(!!mData, E_FAIL);
15022 if (mData->mSession.mLockType == LockType_VM)
15023 directControl = mData->mSession.mDirectControl;
15024
15025 /* directControl may be already set to NULL here in #OnSessionEnd()
15026 * called too early by the direct session process while there is still
15027 * some operation (like deleting the snapshot) in progress. The client
15028 * process in this case is waiting inside Session::close() for the
15029 * "end session" process object to complete, while #uninit() called by
15030 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15031 * operation to complete. For now, we accept this inconsistent behavior
15032 * and simply do nothing here. */
15033
15034 if (mData->mSession.mState == SessionState_Unlocking)
15035 return S_OK;
15036 }
15037
15038 /* ignore notifications sent after #OnSessionEnd() is called */
15039 if (!directControl)
15040 return S_OK;
15041
15042 return directControl->UpdateMachineState(mData->mMachineState);
15043}
15044
15045
15046/*static*/
15047HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15048{
15049 va_list args;
15050 va_start(args, pcszMsg);
15051 HRESULT rc = setErrorInternal(aResultCode,
15052 getStaticClassIID(),
15053 getStaticComponentName(),
15054 Utf8Str(pcszMsg, args),
15055 false /* aWarning */,
15056 true /* aLogIt */);
15057 va_end(args);
15058 return rc;
15059}
15060
15061
15062HRESULT Machine::updateState(MachineState_T aState)
15063{
15064 NOREF(aState);
15065 ReturnComNotImplemented();
15066}
15067
15068HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15069{
15070 NOREF(aProgress);
15071 ReturnComNotImplemented();
15072}
15073
15074HRESULT Machine::endPowerUp(LONG aResult)
15075{
15076 NOREF(aResult);
15077 ReturnComNotImplemented();
15078}
15079
15080HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15081{
15082 NOREF(aProgress);
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::endPoweringDown(LONG aResult,
15087 const com::Utf8Str &aErrMsg)
15088{
15089 NOREF(aResult);
15090 NOREF(aErrMsg);
15091 ReturnComNotImplemented();
15092}
15093
15094HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15095 BOOL *aMatched,
15096 ULONG *aMaskedInterfaces)
15097{
15098 NOREF(aDevice);
15099 NOREF(aMatched);
15100 NOREF(aMaskedInterfaces);
15101 ReturnComNotImplemented();
15102
15103}
15104
15105HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15106{
15107 NOREF(aId); NOREF(aCaptureFilename);
15108 ReturnComNotImplemented();
15109}
15110
15111HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15112 BOOL aDone)
15113{
15114 NOREF(aId);
15115 NOREF(aDone);
15116 ReturnComNotImplemented();
15117}
15118
15119HRESULT Machine::autoCaptureUSBDevices()
15120{
15121 ReturnComNotImplemented();
15122}
15123
15124HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15125{
15126 NOREF(aDone);
15127 ReturnComNotImplemented();
15128}
15129
15130HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15131 ComPtr<IProgress> &aProgress)
15132{
15133 NOREF(aSession);
15134 NOREF(aProgress);
15135 ReturnComNotImplemented();
15136}
15137
15138HRESULT Machine::finishOnlineMergeMedium()
15139{
15140 ReturnComNotImplemented();
15141}
15142
15143HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
15144{
15145 RT_NOREF(aParms, aID);
15146 ReturnComNotImplemented();
15147}
15148
15149HRESULT Machine::clipboardAreaUnregister(ULONG aID)
15150{
15151 RT_NOREF(aID);
15152 ReturnComNotImplemented();
15153}
15154
15155HRESULT Machine::clipboardAreaAttach(ULONG aID)
15156{
15157 RT_NOREF(aID);
15158 ReturnComNotImplemented();
15159}
15160HRESULT Machine::clipboardAreaDetach(ULONG aID)
15161{
15162 RT_NOREF(aID);
15163 ReturnComNotImplemented();
15164}
15165
15166HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
15167{
15168 RT_NOREF(aID);
15169 ReturnComNotImplemented();
15170}
15171
15172HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
15173{
15174 RT_NOREF(aID, aRefCount);
15175 ReturnComNotImplemented();
15176}
15177
15178HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15179 std::vector<com::Utf8Str> &aValues,
15180 std::vector<LONG64> &aTimestamps,
15181 std::vector<com::Utf8Str> &aFlags)
15182{
15183 NOREF(aNames);
15184 NOREF(aValues);
15185 NOREF(aTimestamps);
15186 NOREF(aFlags);
15187 ReturnComNotImplemented();
15188}
15189
15190HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15191 const com::Utf8Str &aValue,
15192 LONG64 aTimestamp,
15193 const com::Utf8Str &aFlags)
15194{
15195 NOREF(aName);
15196 NOREF(aValue);
15197 NOREF(aTimestamp);
15198 NOREF(aFlags);
15199 ReturnComNotImplemented();
15200}
15201
15202HRESULT Machine::lockMedia()
15203{
15204 ReturnComNotImplemented();
15205}
15206
15207HRESULT Machine::unlockMedia()
15208{
15209 ReturnComNotImplemented();
15210}
15211
15212HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15213 ComPtr<IMediumAttachment> &aNewAttachment)
15214{
15215 NOREF(aAttachment);
15216 NOREF(aNewAttachment);
15217 ReturnComNotImplemented();
15218}
15219
15220HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15221 ULONG aCpuUser,
15222 ULONG aCpuKernel,
15223 ULONG aCpuIdle,
15224 ULONG aMemTotal,
15225 ULONG aMemFree,
15226 ULONG aMemBalloon,
15227 ULONG aMemShared,
15228 ULONG aMemCache,
15229 ULONG aPagedTotal,
15230 ULONG aMemAllocTotal,
15231 ULONG aMemFreeTotal,
15232 ULONG aMemBalloonTotal,
15233 ULONG aMemSharedTotal,
15234 ULONG aVmNetRx,
15235 ULONG aVmNetTx)
15236{
15237 NOREF(aValidStats);
15238 NOREF(aCpuUser);
15239 NOREF(aCpuKernel);
15240 NOREF(aCpuIdle);
15241 NOREF(aMemTotal);
15242 NOREF(aMemFree);
15243 NOREF(aMemBalloon);
15244 NOREF(aMemShared);
15245 NOREF(aMemCache);
15246 NOREF(aPagedTotal);
15247 NOREF(aMemAllocTotal);
15248 NOREF(aMemFreeTotal);
15249 NOREF(aMemBalloonTotal);
15250 NOREF(aMemSharedTotal);
15251 NOREF(aVmNetRx);
15252 NOREF(aVmNetTx);
15253 ReturnComNotImplemented();
15254}
15255
15256HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15257 com::Utf8Str &aResult)
15258{
15259 NOREF(aAuthParams);
15260 NOREF(aResult);
15261 ReturnComNotImplemented();
15262}
15263
15264com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15265{
15266 com::Utf8Str strControllerName = "Unknown";
15267 switch (aBusType)
15268 {
15269 case StorageBus_IDE:
15270 {
15271 strControllerName = "IDE";
15272 break;
15273 }
15274 case StorageBus_SATA:
15275 {
15276 strControllerName = "SATA";
15277 break;
15278 }
15279 case StorageBus_SCSI:
15280 {
15281 strControllerName = "SCSI";
15282 break;
15283 }
15284 case StorageBus_Floppy:
15285 {
15286 strControllerName = "Floppy";
15287 break;
15288 }
15289 case StorageBus_SAS:
15290 {
15291 strControllerName = "SAS";
15292 break;
15293 }
15294 case StorageBus_USB:
15295 {
15296 strControllerName = "USB";
15297 break;
15298 }
15299 default:
15300 break;
15301 }
15302 return strControllerName;
15303}
15304
15305HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15306{
15307 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15308
15309 AutoCaller autoCaller(this);
15310 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15311
15312 HRESULT rc = S_OK;
15313
15314 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15315 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15316 rc = getUSBDeviceFilters(usbDeviceFilters);
15317 if (FAILED(rc)) return rc;
15318
15319 NOREF(aFlags);
15320 com::Utf8Str osTypeId;
15321 ComObjPtr<GuestOSType> osType = NULL;
15322
15323 /* Get the guest os type as a string from the VB. */
15324 rc = getOSTypeId(osTypeId);
15325 if (FAILED(rc)) return rc;
15326
15327 /* Get the os type obj that coresponds, can be used to get
15328 * the defaults for this guest OS. */
15329 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15330 if (FAILED(rc)) return rc;
15331
15332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15333
15334 /* Let the OS type select 64-bit ness. */
15335 mHWData->mLongMode = osType->i_is64Bit()
15336 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15337
15338 /* Let the OS type enable the X2APIC */
15339 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15340
15341 /* This one covers IOAPICEnabled. */
15342 mBIOSSettings->i_applyDefaults(osType);
15343
15344 /* Initialize default record settings. */
15345 mRecordingSettings->i_applyDefaults();
15346
15347 /* Initialize default BIOS settings here */
15348 /* Hardware virtualization must be ON by default */
15349 mHWData->mAPIC = true;
15350 mHWData->mHWVirtExEnabled = true;
15351
15352 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15353 if (FAILED(rc)) return rc;
15354
15355 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15356 if (FAILED(rc)) return rc;
15357
15358 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15359 if (FAILED(rc)) return rc;
15360
15361 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15362 if (FAILED(rc)) return rc;
15363
15364 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15365 if (FAILED(rc)) return rc;
15366
15367 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15368 if (FAILED(rc)) return rc;
15369
15370 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15371 if (FAILED(rc)) return rc;
15372
15373 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15374 if (FAILED(rc)) return rc;
15375
15376 BOOL mRTCUseUTC;
15377 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15378 if (FAILED(rc)) return rc;
15379
15380 setRTCUseUTC(mRTCUseUTC);
15381 if (FAILED(rc)) return rc;
15382
15383 /* the setter does more than just the assignment, so use it */
15384 ChipsetType_T enmChipsetType;
15385 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15386 if (FAILED(rc)) return rc;
15387
15388 rc = COMSETTER(ChipsetType)(enmChipsetType);
15389 if (FAILED(rc)) return rc;
15390
15391 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15392 if (FAILED(rc)) return rc;
15393
15394 /* Apply network adapters defaults */
15395 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15396 mNetworkAdapters[slot]->i_applyDefaults(osType);
15397
15398 /* Apply serial port defaults */
15399 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15400 mSerialPorts[slot]->i_applyDefaults(osType);
15401
15402 /* Apply parallel port defaults - not OS dependent*/
15403 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15404 mParallelPorts[slot]->i_applyDefaults();
15405
15406 /* Audio stuff. */
15407 AudioControllerType_T audioController;
15408 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15409 if (FAILED(rc)) return rc;
15410
15411 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15412 if (FAILED(rc)) return rc;
15413
15414 AudioCodecType_T audioCodec;
15415 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15416 if (FAILED(rc)) return rc;
15417
15418 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15419 if (FAILED(rc)) return rc;
15420
15421 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15422 if (FAILED(rc)) return rc;
15423
15424 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15425 if (FAILED(rc)) return rc;
15426
15427 /* Storage Controllers */
15428 StorageControllerType_T hdStorageControllerType;
15429 StorageBus_T hdStorageBusType;
15430 StorageControllerType_T dvdStorageControllerType;
15431 StorageBus_T dvdStorageBusType;
15432 BOOL recommendedFloppy;
15433 ComPtr<IStorageController> floppyController;
15434 ComPtr<IStorageController> hdController;
15435 ComPtr<IStorageController> dvdController;
15436 Utf8Str strFloppyName, strDVDName, strHDName;
15437
15438 /* GUI auto generates controller names using bus type. Do the same*/
15439 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15440
15441 /* Floppy recommended? add one. */
15442 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15443 if (FAILED(rc)) return rc;
15444 if (recommendedFloppy)
15445 {
15446 rc = addStorageController(strFloppyName,
15447 StorageBus_Floppy,
15448 floppyController);
15449 if (FAILED(rc)) return rc;
15450 }
15451
15452 /* Setup one DVD storage controller. */
15453 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15454 if (FAILED(rc)) return rc;
15455
15456 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15457 if (FAILED(rc)) return rc;
15458
15459 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15460
15461 rc = addStorageController(strDVDName,
15462 dvdStorageBusType,
15463 dvdController);
15464 if (FAILED(rc)) return rc;
15465
15466 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15467 if (FAILED(rc)) return rc;
15468
15469 /* Setup one HDD storage controller. */
15470 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15471 if (FAILED(rc)) return rc;
15472
15473 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15474 if (FAILED(rc)) return rc;
15475
15476 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15477
15478 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15479 {
15480 rc = addStorageController(strHDName,
15481 hdStorageBusType,
15482 hdController);
15483 if (FAILED(rc)) return rc;
15484
15485 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15486 if (FAILED(rc)) return rc;
15487 }
15488 else
15489 {
15490 /* The HD controller is the same as DVD: */
15491 hdController = dvdController;
15492 }
15493
15494 /* Limit the AHCI port count if it's used because windows has trouble with
15495 * too many ports and other guest (OS X in particular) may take extra long
15496 * boot: */
15497
15498 // pParent = static_cast<Medium*>(aP)
15499 IStorageController *temp = hdController;
15500 ComObjPtr<StorageController> storageController;
15501 storageController = static_cast<StorageController *>(temp);
15502
15503 // tempHDController = aHDController;
15504 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15505 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15506 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15507 storageController->COMSETTER(PortCount)(1);
15508
15509 /* USB stuff */
15510
15511 bool ohciEnabled = false;
15512
15513 ComPtr<IUSBController> usbController;
15514 BOOL recommendedUSB3;
15515 BOOL recommendedUSB;
15516 BOOL usbProxyAvailable;
15517
15518 getUSBProxyAvailable(&usbProxyAvailable);
15519 if (FAILED(rc)) return rc;
15520
15521 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15522 if (FAILED(rc)) return rc;
15523 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15524 if (FAILED(rc)) return rc;
15525
15526 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15527 {
15528#ifdef VBOX_WITH_EXTPACK
15529 /* USB 3.0 is only available if the proper ExtPack is installed. */
15530 ExtPackManager *aManager = mParent->i_getExtPackManager();
15531 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15532 {
15533 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15534 if (FAILED(rc)) return rc;
15535
15536 /* xHci includes OHCI */
15537 ohciEnabled = true;
15538 }
15539#endif
15540 }
15541 if ( !ohciEnabled
15542 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15543 {
15544 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15545 if (FAILED(rc)) return rc;
15546 ohciEnabled = true;
15547
15548#ifdef VBOX_WITH_EXTPACK
15549 /* USB 2.0 is only available if the proper ExtPack is installed.
15550 * Note. Configuring EHCI here and providing messages about
15551 * the missing extpack isn't exactly clean, but it is a
15552 * necessary evil to patch over legacy compatability issues
15553 * introduced by the new distribution model. */
15554 ExtPackManager *manager = mParent->i_getExtPackManager();
15555 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15556 {
15557 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15558 if (FAILED(rc)) return rc;
15559 }
15560#endif
15561 }
15562
15563 /* Set recommended human interface device types: */
15564 BOOL recommendedUSBHID;
15565 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15566 if (FAILED(rc)) return rc;
15567
15568 if (recommendedUSBHID)
15569 {
15570 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15571 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15572 if (!ohciEnabled && !usbDeviceFilters.isNull())
15573 {
15574 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15575 if (FAILED(rc)) return rc;
15576 }
15577 }
15578
15579 BOOL recommendedUSBTablet;
15580 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15581 if (FAILED(rc)) return rc;
15582
15583 if (recommendedUSBTablet)
15584 {
15585 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15586 if (!ohciEnabled && !usbDeviceFilters.isNull())
15587 {
15588 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15589 if (FAILED(rc)) return rc;
15590 }
15591 }
15592 return S_OK;
15593}
15594
15595/* This isn't handled entirely by the wrapper generator yet. */
15596#ifdef VBOX_WITH_XPCOM
15597NS_DECL_CLASSINFO(SessionMachine)
15598NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15599
15600NS_DECL_CLASSINFO(SnapshotMachine)
15601NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15602#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