VirtualBox

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

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

Main,FE/VBoxManage: Add the necessary Main API bits to control the trusted platform module settings as well as implementing support in VBoxManage, bugref:10075

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 538.2 KB
Line 
1/* $Id: MachineImpl.cpp 91213 2021-09-10 17:58:08Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185 mHWVirtExVirtVmsaveVmload = true;
186#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
187 mPAEEnabled = true;
188#else
189 mPAEEnabled = false;
190#endif
191 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
192 mTripleFaultReset = false;
193 mAPIC = true;
194 mX2APIC = false;
195 mIBPBOnVMExit = false;
196 mIBPBOnVMEntry = false;
197 mSpecCtrl = false;
198 mSpecCtrlByHost = false;
199 mL1DFlushOnSched = true;
200 mL1DFlushOnVMEntry = false;
201 mMDSClearOnSched = true;
202 mMDSClearOnVMEntry = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mClipboardFileTransfersEnabled = FALSE;
218
219 mDnDMode = DnDMode_Disabled;
220
221 mFirmwareType = FirmwareType_BIOS;
222 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
223 mPointingHIDType = PointingHIDType_PS2Mouse;
224 mChipsetType = ChipsetType_PIIX3;
225 mIommuType = IommuType_None;
226 mParavirtProvider = ParavirtProvider_Default;
227 mEmulatedUSBCardReaderEnabled = FALSE;
228
229 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
230 mCPUAttached[i] = false;
231
232 mIOCacheEnabled = true;
233 mIOCacheSize = 5; /* 5MB */
234}
235
236Machine::HWData::~HWData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine() :
248#ifdef VBOX_WITH_RESOURCE_USAGE_API
249 mCollectorGuest(NULL),
250#endif
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param strOsType OS Type string (stored as is if aOsType is NULL).
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
288 * scheme (includes the UUID).
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 const Utf8Str &strOsType,
297 GuestOSType *aOsType,
298 const Guid &aId,
299 bool fForceOverwrite,
300 bool fDirectoryIncludesUUID)
301{
302 LogFlowThisFuncEnter();
303 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
304
305 /* Enclose the state transition NotReady->InInit->Ready */
306 AutoInitSpan autoInitSpan(this);
307 AssertReturn(autoInitSpan.isOk(), E_FAIL);
308
309 HRESULT rc = initImpl(aParent, strConfigFile);
310 if (FAILED(rc)) return rc;
311
312 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
313 if (FAILED(rc)) return rc;
314
315 if (SUCCEEDED(rc))
316 {
317 // create an empty machine config
318 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
319
320 rc = initDataAndChildObjects();
321 }
322
323 if (SUCCEEDED(rc))
324 {
325 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
326 mData->mAccessible = TRUE;
327
328 unconst(mData->mUuid) = aId;
329
330 mUserData->s.strName = strName;
331
332 if (llGroups.size())
333 mUserData->s.llGroups = llGroups;
334
335 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
336 // the "name sync" flag determines whether the machine directory gets renamed along
337 // with the machine file; say so if the settings file name is the same as the
338 // settings file parent directory (machine directory)
339 mUserData->s.fNameSync = i_isInOwnDir();
340
341 // initialize the default snapshots folder
342 rc = COMSETTER(SnapshotFolder)(NULL);
343 AssertComRC(rc);
344
345 if (aOsType)
346 {
347 /* Store OS type */
348 mUserData->s.strOsType = aOsType->i_id();
349
350 /* Let the OS type select 64-bit ness. */
351 mHWData->mLongMode = aOsType->i_is64Bit()
352 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
353
354 /* Let the OS type enable the X2APIC */
355 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
356 }
357 else if (!strOsType.isEmpty())
358 {
359 /* Store OS type */
360 mUserData->s.strOsType = strOsType;
361
362 /* No guest OS type object. Pick some plausible defaults which the
363 * host can handle. There's no way to know or validate anything. */
364 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365 mHWData->mX2APIC = false;
366 }
367
368 /* Apply BIOS defaults. */
369 mBIOSSettings->i_applyDefaults(aOsType);
370
371 /* Apply record defaults. */
372 mRecordingSettings->i_applyDefaults();
373
374 /* Apply network adapters defaults */
375 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
376 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
377
378 /* Apply serial port defaults */
379 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
380 mSerialPorts[slot]->i_applyDefaults(aOsType);
381
382 /* Apply parallel port defaults */
383 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
384 mParallelPorts[slot]->i_applyDefaults();
385
386 /* At this point the changing of the current state modification
387 * flag is allowed. */
388 i_allowStateModification();
389
390 /* commit all changes made during the initialization */
391 i_commit();
392 }
393
394 /* Confirm a successful initialization when it's the case */
395 if (SUCCEEDED(rc))
396 {
397 if (mData->mAccessible)
398 autoInitSpan.setSucceeded();
399 else
400 autoInitSpan.setLimited();
401 }
402
403 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
404 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
405 mData->mRegistered,
406 mData->mAccessible,
407 rc));
408
409 LogFlowThisFuncLeave();
410
411 return rc;
412}
413
414/**
415 * Initializes a new instance with data from machine XML (formerly Init_Registered).
416 * Gets called in two modes:
417 *
418 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
419 * UUID is specified and we mark the machine as "registered";
420 *
421 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
422 * and the machine remains unregistered until RegisterMachine() is called.
423 *
424 * @param aParent Associated parent object
425 * @param strConfigFile Local file system path to the VM settings file (can
426 * be relative to the VirtualBox config directory).
427 * @param aId UUID of the machine or NULL (see above).
428 *
429 * @return Success indicator. if not S_OK, the machine object is invalid
430 */
431HRESULT Machine::initFromSettings(VirtualBox *aParent,
432 const Utf8Str &strConfigFile,
433 const Guid *aId)
434{
435 LogFlowThisFuncEnter();
436 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
437
438 /* Enclose the state transition NotReady->InInit->Ready */
439 AutoInitSpan autoInitSpan(this);
440 AssertReturn(autoInitSpan.isOk(), E_FAIL);
441
442 HRESULT rc = initImpl(aParent, strConfigFile);
443 if (FAILED(rc)) return rc;
444
445 if (aId)
446 {
447 // loading a registered VM:
448 unconst(mData->mUuid) = *aId;
449 mData->mRegistered = TRUE;
450 // now load the settings from XML:
451 rc = i_registeredInit();
452 // this calls initDataAndChildObjects() and loadSettings()
453 }
454 else
455 {
456 // opening an unregistered VM (VirtualBox::OpenMachine()):
457 rc = initDataAndChildObjects();
458
459 if (SUCCEEDED(rc))
460 {
461 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
462 mData->mAccessible = TRUE;
463
464 try
465 {
466 // load and parse machine XML; this will throw on XML or logic errors
467 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
468
469 // reject VM UUID duplicates, they can happen if someone
470 // tries to register an already known VM config again
471 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
472 true /* fPermitInaccessible */,
473 false /* aDoSetError */,
474 NULL) != VBOX_E_OBJECT_NOT_FOUND)
475 {
476 throw setError(E_FAIL,
477 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
478 mData->m_strConfigFile.c_str());
479 }
480
481 // use UUID from machine config
482 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
483
484 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
485 NULL /* puuidRegistry */);
486 if (FAILED(rc)) throw rc;
487
488 /* At this point the changing of the current state modification
489 * flag is allowed. */
490 i_allowStateModification();
491
492 i_commit();
493 }
494 catch (HRESULT err)
495 {
496 /* we assume that error info is set by the thrower */
497 rc = err;
498 }
499 catch (...)
500 {
501 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
502 }
503 }
504 }
505
506 /* Confirm a successful initialization when it's the case */
507 if (SUCCEEDED(rc))
508 {
509 if (mData->mAccessible)
510 autoInitSpan.setSucceeded();
511 else
512 {
513 autoInitSpan.setLimited();
514
515 // uninit media from this machine's media registry, or else
516 // reloading the settings will fail
517 mParent->i_unregisterMachineMedia(i_getId());
518 }
519 }
520
521 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
522 "rc=%08X\n",
523 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
524 mData->mRegistered, mData->mAccessible, rc));
525
526 LogFlowThisFuncLeave();
527
528 return rc;
529}
530
531/**
532 * Initializes a new instance from a machine config that is already in memory
533 * (import OVF case). Since we are importing, the UUID in the machine
534 * config is ignored and we always generate a fresh one.
535 *
536 * @param aParent Associated parent object.
537 * @param strName Name for the new machine; this overrides what is specified in config.
538 * @param strSettingsFilename File name of .vbox file.
539 * @param config Machine configuration loaded and parsed from XML.
540 *
541 * @return Success indicator. if not S_OK, the machine object is invalid
542 */
543HRESULT Machine::init(VirtualBox *aParent,
544 const Utf8Str &strName,
545 const Utf8Str &strSettingsFilename,
546 const settings::MachineConfigFile &config)
547{
548 LogFlowThisFuncEnter();
549
550 /* Enclose the state transition NotReady->InInit->Ready */
551 AutoInitSpan autoInitSpan(this);
552 AssertReturn(autoInitSpan.isOk(), E_FAIL);
553
554 HRESULT rc = initImpl(aParent, strSettingsFilename);
555 if (FAILED(rc)) return rc;
556
557 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
558 if (FAILED(rc)) return rc;
559
560 rc = initDataAndChildObjects();
561
562 if (SUCCEEDED(rc))
563 {
564 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
565 mData->mAccessible = TRUE;
566
567 // create empty machine config for instance data
568 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
569
570 // generate fresh UUID, ignore machine config
571 unconst(mData->mUuid).create();
572
573 rc = i_loadMachineDataFromSettings(config,
574 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
575
576 // override VM name as well, it may be different
577 mUserData->s.strName = strName;
578
579 if (SUCCEEDED(rc))
580 {
581 /* At this point the changing of the current state modification
582 * flag is allowed. */
583 i_allowStateModification();
584
585 /* commit all changes made during the initialization */
586 i_commit();
587 }
588 }
589
590 /* Confirm a successful initialization when it's the case */
591 if (SUCCEEDED(rc))
592 {
593 if (mData->mAccessible)
594 autoInitSpan.setSucceeded();
595 else
596 {
597 /* Ignore all errors from unregistering, they would destroy
598- * the more interesting error information we already have,
599- * pinpointing the issue with the VM config. */
600 ErrorInfoKeeper eik;
601
602 autoInitSpan.setLimited();
603
604 // uninit media from this machine's media registry, or else
605 // reloading the settings will fail
606 mParent->i_unregisterMachineMedia(i_getId());
607 }
608 }
609
610 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
611 "rc=%08X\n",
612 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
613 mData->mRegistered, mData->mAccessible, rc));
614
615 LogFlowThisFuncLeave();
616
617 return rc;
618}
619
620/**
621 * Shared code between the various init() implementations.
622 * @param aParent The VirtualBox object.
623 * @param strConfigFile Settings file.
624 * @return
625 */
626HRESULT Machine::initImpl(VirtualBox *aParent,
627 const Utf8Str &strConfigFile)
628{
629 LogFlowThisFuncEnter();
630
631 AssertReturn(aParent, E_INVALIDARG);
632 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
633
634 HRESULT rc = S_OK;
635
636 /* share the parent weakly */
637 unconst(mParent) = aParent;
638
639 /* allocate the essential machine data structure (the rest will be
640 * allocated later by initDataAndChildObjects() */
641 mData.allocate();
642
643 /* memorize the config file name (as provided) */
644 mData->m_strConfigFile = strConfigFile;
645
646 /* get the full file name */
647 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
648 if (RT_FAILURE(vrc1))
649 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
650 tr("Invalid machine settings file name '%s' (%Rrc)"),
651 strConfigFile.c_str(),
652 vrc1);
653
654 LogFlowThisFuncLeave();
655
656 return rc;
657}
658
659/**
660 * Tries to create a machine settings file in the path stored in the machine
661 * instance data. Used when a new machine is created to fail gracefully if
662 * the settings file could not be written (e.g. because machine dir is read-only).
663 * @return
664 */
665HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
666{
667 HRESULT rc = S_OK;
668
669 // when we create a new machine, we must be able to create the settings file
670 RTFILE f = NIL_RTFILE;
671 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
672 if ( RT_SUCCESS(vrc)
673 || vrc == VERR_SHARING_VIOLATION
674 )
675 {
676 if (RT_SUCCESS(vrc))
677 RTFileClose(f);
678 if (!fForceOverwrite)
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Machine settings file '%s' already exists"),
681 mData->m_strConfigFileFull.c_str());
682 else
683 {
684 /* try to delete the config file, as otherwise the creation
685 * of a new settings file will fail. */
686 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
687 if (RT_FAILURE(vrc2))
688 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
689 tr("Could not delete the existing settings file '%s' (%Rrc)"),
690 mData->m_strConfigFileFull.c_str(), vrc2);
691 }
692 }
693 else if ( vrc != VERR_FILE_NOT_FOUND
694 && vrc != VERR_PATH_NOT_FOUND
695 )
696 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
697 tr("Invalid machine settings file name '%s' (%Rrc)"),
698 mData->m_strConfigFileFull.c_str(),
699 vrc);
700 return rc;
701}
702
703/**
704 * Initializes the registered machine by loading the settings file.
705 * This method is separated from #init() in order to make it possible to
706 * retry the operation after VirtualBox startup instead of refusing to
707 * startup the whole VirtualBox server in case if the settings file of some
708 * registered VM is invalid or inaccessible.
709 *
710 * @note Must be always called from this object's write lock
711 * (unless called from #init() that doesn't need any locking).
712 * @note Locks the mUSBController method for writing.
713 * @note Subclasses must not call this method.
714 */
715HRESULT Machine::i_registeredInit()
716{
717 AssertReturn(!i_isSessionMachine(), E_FAIL);
718 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
719 AssertReturn(mData->mUuid.isValid(), E_FAIL);
720 AssertReturn(!mData->mAccessible, E_FAIL);
721
722 HRESULT rc = initDataAndChildObjects();
723
724 if (SUCCEEDED(rc))
725 {
726 /* Temporarily reset the registered flag in order to let setters
727 * potentially called from loadSettings() succeed (isMutable() used in
728 * all setters will return FALSE for a Machine instance if mRegistered
729 * is TRUE). */
730 mData->mRegistered = FALSE;
731
732 try
733 {
734 // load and parse machine XML; this will throw on XML or logic errors
735 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
736
737 if (mData->mUuid != mData->pMachineConfigFile->uuid)
738 throw setError(E_FAIL,
739 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
740 mData->pMachineConfigFile->uuid.raw(),
741 mData->m_strConfigFileFull.c_str(),
742 mData->mUuid.toString().c_str(),
743 mParent->i_settingsFilePath().c_str());
744
745 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
746 NULL /* const Guid *puuidRegistry */);
747 if (FAILED(rc)) throw rc;
748 }
749 catch (HRESULT err)
750 {
751 /* we assume that error info is set by the thrower */
752 rc = err;
753 }
754 catch (...)
755 {
756 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
757 }
758
759 /* Restore the registered flag (even on failure) */
760 mData->mRegistered = TRUE;
761 }
762
763 if (SUCCEEDED(rc))
764 {
765 /* Set mAccessible to TRUE only if we successfully locked and loaded
766 * the settings file */
767 mData->mAccessible = TRUE;
768
769 /* commit all changes made during loading the settings file */
770 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
771 /// @todo r=klaus for some reason the settings loading logic backs up
772 // the settings, and therefore a commit is needed. Should probably be changed.
773 }
774 else
775 {
776 /* If the machine is registered, then, instead of returning a
777 * failure, we mark it as inaccessible and set the result to
778 * success to give it a try later */
779
780 /* fetch the current error info */
781 mData->mAccessError = com::ErrorInfo();
782 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
783
784 /* rollback all changes */
785 i_rollback(false /* aNotify */);
786
787 // uninit media from this machine's media registry, or else
788 // reloading the settings will fail
789 mParent->i_unregisterMachineMedia(i_getId());
790
791 /* uninitialize the common part to make sure all data is reset to
792 * default (null) values */
793 uninitDataAndChildObjects();
794
795 rc = S_OK;
796 }
797
798 return rc;
799}
800
801/**
802 * Uninitializes the instance.
803 * Called either from FinalRelease() or by the parent when it gets destroyed.
804 *
805 * @note The caller of this method must make sure that this object
806 * a) doesn't have active callers on the current thread and b) is not locked
807 * by the current thread; otherwise uninit() will hang either a) due to
808 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
809 * a dead-lock caused by this thread waiting for all callers on the other
810 * threads are done but preventing them from doing so by holding a lock.
811 */
812void Machine::uninit()
813{
814 LogFlowThisFuncEnter();
815
816 Assert(!isWriteLockOnCurrentThread());
817
818 Assert(!uRegistryNeedsSaving);
819 if (uRegistryNeedsSaving)
820 {
821 AutoCaller autoCaller(this);
822 if (SUCCEEDED(autoCaller.rc()))
823 {
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825 i_saveSettings(NULL, Machine::SaveS_Force);
826 }
827 }
828
829 /* Enclose the state transition Ready->InUninit->NotReady */
830 AutoUninitSpan autoUninitSpan(this);
831 if (autoUninitSpan.uninitDone())
832 return;
833
834 Assert(!i_isSnapshotMachine());
835 Assert(!i_isSessionMachine());
836 Assert(!!mData);
837
838 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
839 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
840
841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
842
843 if (!mData->mSession.mMachine.isNull())
844 {
845 /* Theoretically, this can only happen if the VirtualBox server has been
846 * terminated while there were clients running that owned open direct
847 * sessions. Since in this case we are definitely called by
848 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
849 * won't happen on the client watcher thread (because it has a
850 * VirtualBox caller for the duration of the
851 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
852 * cannot happen until the VirtualBox caller is released). This is
853 * important, because SessionMachine::uninit() cannot correctly operate
854 * after we return from this method (it expects the Machine instance is
855 * still valid). We'll call it ourselves below.
856 */
857 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
858 (SessionMachine*)mData->mSession.mMachine));
859
860 if (Global::IsOnlineOrTransient(mData->mMachineState))
861 {
862 Log1WarningThisFunc(("Setting state to Aborted!\n"));
863 /* set machine state using SessionMachine reimplementation */
864 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
865 }
866
867 /*
868 * Uninitialize SessionMachine using public uninit() to indicate
869 * an unexpected uninitialization.
870 */
871 mData->mSession.mMachine->uninit();
872 /* SessionMachine::uninit() must set mSession.mMachine to null */
873 Assert(mData->mSession.mMachine.isNull());
874 }
875
876 // uninit media from this machine's media registry, if they're still there
877 Guid uuidMachine(i_getId());
878
879 /* the lock is no more necessary (SessionMachine is uninitialized) */
880 alock.release();
881
882 /* XXX This will fail with
883 * "cannot be closed because it is still attached to 1 virtual machines"
884 * because at this point we did not call uninitDataAndChildObjects() yet
885 * and therefore also removeBackReference() for all these mediums was not called! */
886
887 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
888 mParent->i_unregisterMachineMedia(uuidMachine);
889
890 // has machine been modified?
891 if (mData->flModifications)
892 {
893 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
894 i_rollback(false /* aNotify */);
895 }
896
897 if (mData->mAccessible)
898 uninitDataAndChildObjects();
899
900 /* free the essential data structure last */
901 mData.free();
902
903 LogFlowThisFuncLeave();
904}
905
906// Wrapped IMachine properties
907/////////////////////////////////////////////////////////////////////////////
908HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
909{
910 /* mParent is constant during life time, no need to lock */
911 ComObjPtr<VirtualBox> pVirtualBox(mParent);
912 aParent = pVirtualBox;
913
914 return S_OK;
915}
916
917
918HRESULT Machine::getAccessible(BOOL *aAccessible)
919{
920 /* In some cases (medium registry related), it is necessary to be able to
921 * go through the list of all machines. Happens when an inaccessible VM
922 * has a sensible medium registry. */
923 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
925
926 HRESULT rc = S_OK;
927
928 if (!mData->mAccessible)
929 {
930 /* try to initialize the VM once more if not accessible */
931
932 AutoReinitSpan autoReinitSpan(this);
933 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
934
935#ifdef DEBUG
936 LogFlowThisFunc(("Dumping media backreferences\n"));
937 mParent->i_dumpAllBackRefs();
938#endif
939
940 if (mData->pMachineConfigFile)
941 {
942 // reset the XML file to force loadSettings() (called from i_registeredInit())
943 // to parse it again; the file might have changed
944 delete mData->pMachineConfigFile;
945 mData->pMachineConfigFile = NULL;
946 }
947
948 rc = i_registeredInit();
949
950 if (SUCCEEDED(rc) && mData->mAccessible)
951 {
952 autoReinitSpan.setSucceeded();
953
954 /* make sure interesting parties will notice the accessibility
955 * state change */
956 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
957 mParent->i_onMachineDataChanged(mData->mUuid);
958 }
959 }
960
961 if (SUCCEEDED(rc))
962 *aAccessible = mData->mAccessible;
963
964 LogFlowThisFuncLeave();
965
966 return rc;
967}
968
969HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
970{
971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
972
973 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
974 {
975 /* return shortly */
976 aAccessError = NULL;
977 return S_OK;
978 }
979
980 HRESULT rc = S_OK;
981
982 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
983 rc = errorInfo.createObject();
984 if (SUCCEEDED(rc))
985 {
986 errorInfo->init(mData->mAccessError.getResultCode(),
987 mData->mAccessError.getInterfaceID().ref(),
988 Utf8Str(mData->mAccessError.getComponent()).c_str(),
989 Utf8Str(mData->mAccessError.getText()));
990 aAccessError = errorInfo;
991 }
992
993 return rc;
994}
995
996HRESULT Machine::getName(com::Utf8Str &aName)
997{
998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 aName = mUserData->s.strName;
1001
1002 return S_OK;
1003}
1004
1005HRESULT Machine::setName(const com::Utf8Str &aName)
1006{
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 i_setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1027{
1028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1029
1030 aDescription = mUserData->s.strDescription;
1031
1032 return S_OK;
1033}
1034
1035HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1036{
1037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1038
1039 // this can be done in principle in any state as it doesn't affect the VM
1040 // significantly, but play safe by not messing around while complex
1041 // activities are going on
1042 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1043 if (FAILED(rc)) return rc;
1044
1045 i_setModified(IsModified_MachineData);
1046 mUserData.backup();
1047 mUserData->s.strDescription = aDescription;
1048
1049 return S_OK;
1050}
1051
1052HRESULT Machine::getId(com::Guid &aId)
1053{
1054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1055
1056 aId = mData->mUuid;
1057
1058 return S_OK;
1059}
1060
1061HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1062{
1063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1064 aGroups.resize(mUserData->s.llGroups.size());
1065 size_t i = 0;
1066 for (StringsList::const_iterator
1067 it = mUserData->s.llGroups.begin();
1068 it != mUserData->s.llGroups.end();
1069 ++it, ++i)
1070 aGroups[i] = (*it);
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1076{
1077 StringsList llGroups;
1078 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1079 if (FAILED(rc))
1080 return rc;
1081
1082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 rc = i_checkStateDependency(MutableOrSavedStateDep);
1085 if (FAILED(rc)) return rc;
1086
1087 i_setModified(IsModified_MachineData);
1088 mUserData.backup();
1089 mUserData->s.llGroups = llGroups;
1090
1091 return S_OK;
1092}
1093
1094HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1095{
1096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 aOSTypeId = mUserData->s.strOsType;
1099
1100 return S_OK;
1101}
1102
1103HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1104{
1105 /* look up the object by Id to check it is valid */
1106 ComObjPtr<GuestOSType> pGuestOSType;
1107 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1108
1109 /* when setting, always use the "etalon" value for consistency -- lookup
1110 * by ID is case-insensitive and the input value may have different case */
1111 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1112
1113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 HRESULT rc = i_checkStateDependency(MutableStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 i_setModified(IsModified_MachineData);
1119 mUserData.backup();
1120 mUserData->s.strOsType = osTypeId;
1121
1122 return S_OK;
1123}
1124
1125HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1126{
1127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1128
1129 *aFirmwareType = mHWData->mFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1135{
1136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 HRESULT rc = i_checkStateDependency(MutableStateDep);
1139 if (FAILED(rc)) return rc;
1140
1141 i_setModified(IsModified_MachineData);
1142 mHWData.backup();
1143 mHWData->mFirmwareType = aFirmwareType;
1144 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1145 alock.release();
1146
1147 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aPointingHIDType = mHWData->mPointingHIDType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 i_setModified(IsModified_MachineData);
1192 mHWData.backup();
1193 mHWData->mPointingHIDType = aPointingHIDType;
1194
1195 return S_OK;
1196}
1197
1198HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1199{
1200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 *aChipsetType = mHWData->mChipsetType;
1203
1204 return S_OK;
1205}
1206
1207HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1208{
1209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1210
1211 HRESULT rc = i_checkStateDependency(MutableStateDep);
1212 if (FAILED(rc)) return rc;
1213
1214 if (aChipsetType != mHWData->mChipsetType)
1215 {
1216 i_setModified(IsModified_MachineData);
1217 mHWData.backup();
1218 mHWData->mChipsetType = aChipsetType;
1219
1220 // Resize network adapter array, to be finalized on commit/rollback.
1221 // We must not throw away entries yet, otherwise settings are lost
1222 // without a way to roll back.
1223 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1224 size_t oldCount = mNetworkAdapters.size();
1225 if (newCount > oldCount)
1226 {
1227 mNetworkAdapters.resize(newCount);
1228 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1229 {
1230 unconst(mNetworkAdapters[slot]).createObject();
1231 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1232 }
1233 }
1234 }
1235
1236 return S_OK;
1237}
1238
1239HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1240{
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 *aIommuType = mHWData->mIommuType;
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::setIommuType(IommuType_T aIommuType)
1249{
1250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 HRESULT rc = i_checkStateDependency(MutableStateDep);
1253 if (FAILED(rc)) return rc;
1254
1255 if (aIommuType != mHWData->mIommuType)
1256 {
1257 if (aIommuType == IommuType_Intel)
1258 {
1259#ifndef VBOX_WITH_IOMMU_INTEL
1260 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1261 return E_UNEXPECTED;
1262#endif
1263 }
1264
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mIommuType = aIommuType;
1268 }
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1274{
1275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 aParavirtDebug = mHWData->mParavirtDebug;
1278 return S_OK;
1279}
1280
1281HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1282{
1283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 HRESULT rc = i_checkStateDependency(MutableStateDep);
1286 if (FAILED(rc)) return rc;
1287
1288 /** @todo Parse/validate options? */
1289 if (aParavirtDebug != mHWData->mParavirtDebug)
1290 {
1291 i_setModified(IsModified_MachineData);
1292 mHWData.backup();
1293 mHWData->mParavirtDebug = aParavirtDebug;
1294 }
1295
1296 return S_OK;
1297}
1298
1299HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1300{
1301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1302
1303 *aParavirtProvider = mHWData->mParavirtProvider;
1304
1305 return S_OK;
1306}
1307
1308HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1309{
1310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 HRESULT rc = i_checkStateDependency(MutableStateDep);
1313 if (FAILED(rc)) return rc;
1314
1315 if (aParavirtProvider != mHWData->mParavirtProvider)
1316 {
1317 i_setModified(IsModified_MachineData);
1318 mHWData.backup();
1319 mHWData->mParavirtProvider = aParavirtProvider;
1320 }
1321
1322 return S_OK;
1323}
1324
1325HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1326{
1327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1328
1329 *aParavirtProvider = mHWData->mParavirtProvider;
1330 switch (mHWData->mParavirtProvider)
1331 {
1332 case ParavirtProvider_None:
1333 case ParavirtProvider_HyperV:
1334 case ParavirtProvider_KVM:
1335 case ParavirtProvider_Minimal:
1336 break;
1337
1338 /* Resolve dynamic provider types to the effective types. */
1339 default:
1340 {
1341 ComObjPtr<GuestOSType> pGuestOSType;
1342 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1343 pGuestOSType);
1344 if (FAILED(hrc2) || pGuestOSType.isNull())
1345 {
1346 *aParavirtProvider = ParavirtProvider_None;
1347 break;
1348 }
1349
1350 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1351 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1352
1353 switch (mHWData->mParavirtProvider)
1354 {
1355 case ParavirtProvider_Legacy:
1356 {
1357 if (fOsXGuest)
1358 *aParavirtProvider = ParavirtProvider_Minimal;
1359 else
1360 *aParavirtProvider = ParavirtProvider_None;
1361 break;
1362 }
1363
1364 case ParavirtProvider_Default:
1365 {
1366 if (fOsXGuest)
1367 *aParavirtProvider = ParavirtProvider_Minimal;
1368 else if ( mUserData->s.strOsType == "Windows10"
1369 || mUserData->s.strOsType == "Windows10_64"
1370 || mUserData->s.strOsType == "Windows81"
1371 || mUserData->s.strOsType == "Windows81_64"
1372 || mUserData->s.strOsType == "Windows8"
1373 || mUserData->s.strOsType == "Windows8_64"
1374 || mUserData->s.strOsType == "Windows7"
1375 || mUserData->s.strOsType == "Windows7_64"
1376 || mUserData->s.strOsType == "WindowsVista"
1377 || mUserData->s.strOsType == "WindowsVista_64"
1378 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1379 || mUserData->s.strOsType.startsWith("Windows201"))
1380 && mUserData->s.strOsType.endsWith("_64"))
1381 || mUserData->s.strOsType == "Windows2012"
1382 || mUserData->s.strOsType == "Windows2012_64"
1383 || mUserData->s.strOsType == "Windows2008"
1384 || mUserData->s.strOsType == "Windows2008_64")
1385 {
1386 *aParavirtProvider = ParavirtProvider_HyperV;
1387 }
1388 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1389 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1390 || mUserData->s.strOsType == "Linux"
1391 || mUserData->s.strOsType == "Linux_64"
1392 || mUserData->s.strOsType == "ArchLinux"
1393 || mUserData->s.strOsType == "ArchLinux_64"
1394 || mUserData->s.strOsType == "Debian"
1395 || mUserData->s.strOsType == "Debian_64"
1396 || mUserData->s.strOsType == "Fedora"
1397 || mUserData->s.strOsType == "Fedora_64"
1398 || mUserData->s.strOsType == "Gentoo"
1399 || mUserData->s.strOsType == "Gentoo_64"
1400 || mUserData->s.strOsType == "Mandriva"
1401 || mUserData->s.strOsType == "Mandriva_64"
1402 || mUserData->s.strOsType == "OpenSUSE"
1403 || mUserData->s.strOsType == "OpenSUSE_64"
1404 || mUserData->s.strOsType == "Oracle"
1405 || mUserData->s.strOsType == "Oracle_64"
1406 || mUserData->s.strOsType == "RedHat"
1407 || mUserData->s.strOsType == "RedHat_64"
1408 || mUserData->s.strOsType == "Turbolinux"
1409 || mUserData->s.strOsType == "Turbolinux_64"
1410 || mUserData->s.strOsType == "Ubuntu"
1411 || mUserData->s.strOsType == "Ubuntu_64"
1412 || mUserData->s.strOsType == "Xandros"
1413 || mUserData->s.strOsType == "Xandros_64")
1414 {
1415 *aParavirtProvider = ParavirtProvider_KVM;
1416 }
1417 else
1418 *aParavirtProvider = ParavirtProvider_None;
1419 break;
1420 }
1421
1422 default: AssertFailedBreak(); /* Shut up MSC. */
1423 }
1424 break;
1425 }
1426 }
1427
1428 Assert( *aParavirtProvider == ParavirtProvider_None
1429 || *aParavirtProvider == ParavirtProvider_Minimal
1430 || *aParavirtProvider == ParavirtProvider_HyperV
1431 || *aParavirtProvider == ParavirtProvider_KVM);
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 aHardwareVersion = mHWData->mHWVersion;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1445{
1446 /* check known version */
1447 Utf8Str hwVersion = aHardwareVersion;
1448 if ( hwVersion.compare("1") != 0
1449 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1450 return setError(E_INVALIDARG,
1451 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1452
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 HRESULT rc = i_checkStateDependency(MutableStateDep);
1456 if (FAILED(rc)) return rc;
1457
1458 i_setModified(IsModified_MachineData);
1459 mHWData.backup();
1460 mHWData->mHWVersion = aHardwareVersion;
1461
1462 return S_OK;
1463}
1464
1465HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1466{
1467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1468
1469 if (!mHWData->mHardwareUUID.isZero())
1470 aHardwareUUID = mHWData->mHardwareUUID;
1471 else
1472 aHardwareUUID = mData->mUuid;
1473
1474 return S_OK;
1475}
1476
1477HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1478{
1479 if (!aHardwareUUID.isValid())
1480 return E_INVALIDARG;
1481
1482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1483
1484 HRESULT rc = i_checkStateDependency(MutableStateDep);
1485 if (FAILED(rc)) return rc;
1486
1487 i_setModified(IsModified_MachineData);
1488 mHWData.backup();
1489 if (aHardwareUUID == mData->mUuid)
1490 mHWData->mHardwareUUID.clear();
1491 else
1492 mHWData->mHardwareUUID = aHardwareUUID;
1493
1494 return S_OK;
1495}
1496
1497HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1498{
1499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1500
1501 *aMemorySize = mHWData->mMemorySize;
1502
1503 return S_OK;
1504}
1505
1506HRESULT Machine::setMemorySize(ULONG aMemorySize)
1507{
1508 /* check RAM limits */
1509 if ( aMemorySize < MM_RAM_MIN_IN_MB
1510 || aMemorySize > MM_RAM_MAX_IN_MB
1511 )
1512 return setError(E_INVALIDARG,
1513 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1514 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1515
1516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1517
1518 HRESULT rc = i_checkStateDependency(MutableStateDep);
1519 if (FAILED(rc)) return rc;
1520
1521 i_setModified(IsModified_MachineData);
1522 mHWData.backup();
1523 mHWData->mMemorySize = aMemorySize;
1524
1525 return S_OK;
1526}
1527
1528HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1529{
1530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 *aCPUCount = mHWData->mCPUCount;
1533
1534 return S_OK;
1535}
1536
1537HRESULT Machine::setCPUCount(ULONG aCPUCount)
1538{
1539 /* check CPU limits */
1540 if ( aCPUCount < SchemaDefs::MinCPUCount
1541 || aCPUCount > SchemaDefs::MaxCPUCount
1542 )
1543 return setError(E_INVALIDARG,
1544 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1545 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1550 if (mHWData->mCPUHotPlugEnabled)
1551 {
1552 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1553 {
1554 if (mHWData->mCPUAttached[idx])
1555 return setError(E_INVALIDARG,
1556 tr("There is still a CPU attached to socket %lu."
1557 "Detach the CPU before removing the socket"),
1558 aCPUCount, idx+1);
1559 }
1560 }
1561
1562 HRESULT rc = i_checkStateDependency(MutableStateDep);
1563 if (FAILED(rc)) return rc;
1564
1565 i_setModified(IsModified_MachineData);
1566 mHWData.backup();
1567 mHWData->mCPUCount = aCPUCount;
1568
1569 return S_OK;
1570}
1571
1572HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1573{
1574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1582{
1583 HRESULT rc = S_OK;
1584
1585 /* check throttle limits */
1586 if ( aCPUExecutionCap < 1
1587 || aCPUExecutionCap > 100
1588 )
1589 return setError(E_INVALIDARG,
1590 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1591 aCPUExecutionCap, 1, 100);
1592
1593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 alock.release();
1596 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1597 alock.acquire();
1598 if (FAILED(rc)) return rc;
1599
1600 i_setModified(IsModified_MachineData);
1601 mHWData.backup();
1602 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1603
1604 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1605 if (Global::IsOnline(mData->mMachineState))
1606 i_saveSettings(NULL);
1607
1608 return S_OK;
1609}
1610
1611HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1612{
1613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1621{
1622 HRESULT rc = S_OK;
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 rc = i_checkStateDependency(MutableStateDep);
1627 if (FAILED(rc)) return rc;
1628
1629 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1630 {
1631 if (aCPUHotPlugEnabled)
1632 {
1633 i_setModified(IsModified_MachineData);
1634 mHWData.backup();
1635
1636 /* Add the amount of CPUs currently attached */
1637 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1638 mHWData->mCPUAttached[i] = true;
1639 }
1640 else
1641 {
1642 /*
1643 * We can disable hotplug only if the amount of maximum CPUs is equal
1644 * to the amount of attached CPUs
1645 */
1646 unsigned cCpusAttached = 0;
1647 unsigned iHighestId = 0;
1648
1649 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1650 {
1651 if (mHWData->mCPUAttached[i])
1652 {
1653 cCpusAttached++;
1654 iHighestId = i;
1655 }
1656 }
1657
1658 if ( (cCpusAttached != mHWData->mCPUCount)
1659 || (iHighestId >= mHWData->mCPUCount))
1660 return setError(E_INVALIDARG,
1661 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1662
1663 i_setModified(IsModified_MachineData);
1664 mHWData.backup();
1665 }
1666 }
1667
1668 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1669
1670 return rc;
1671}
1672
1673HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1674{
1675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1678
1679 return S_OK;
1680}
1681
1682HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1683{
1684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1687 if (SUCCEEDED(hrc))
1688 {
1689 i_setModified(IsModified_MachineData);
1690 mHWData.backup();
1691 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1692 }
1693 return hrc;
1694}
1695
1696HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699 aCPUProfile = mHWData->mCpuProfile;
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1704{
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1707 if (SUCCEEDED(hrc))
1708 {
1709 i_setModified(IsModified_MachineData);
1710 mHWData.backup();
1711 /* Empty equals 'host'. */
1712 if (aCPUProfile.isNotEmpty())
1713 mHWData->mCpuProfile = aCPUProfile;
1714 else
1715 mHWData->mCpuProfile = "host";
1716 }
1717 return hrc;
1718}
1719
1720HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1721{
1722#ifdef VBOX_WITH_USB_CARDREADER
1723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1724
1725 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1726
1727 return S_OK;
1728#else
1729 NOREF(aEmulatedUSBCardReaderEnabled);
1730 return E_NOTIMPL;
1731#endif
1732}
1733
1734HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1735{
1736#ifdef VBOX_WITH_USB_CARDREADER
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1740 if (FAILED(rc)) return rc;
1741
1742 i_setModified(IsModified_MachineData);
1743 mHWData.backup();
1744 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1745
1746 return S_OK;
1747#else
1748 NOREF(aEmulatedUSBCardReaderEnabled);
1749 return E_NOTIMPL;
1750#endif
1751}
1752
1753HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1754{
1755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1756
1757 *aHPETEnabled = mHWData->mHPETEnabled;
1758
1759 return S_OK;
1760}
1761
1762HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1763{
1764 HRESULT rc = S_OK;
1765
1766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1767
1768 rc = i_checkStateDependency(MutableStateDep);
1769 if (FAILED(rc)) return rc;
1770
1771 i_setModified(IsModified_MachineData);
1772 mHWData.backup();
1773
1774 mHWData->mHPETEnabled = aHPETEnabled;
1775
1776 return rc;
1777}
1778
1779/** @todo this method should not be public */
1780HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1781{
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1785
1786 return S_OK;
1787}
1788
1789/**
1790 * Set the memory balloon size.
1791 *
1792 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1793 * we have to make sure that we never call IGuest from here.
1794 */
1795HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1796{
1797 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1798#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1799 /* check limits */
1800 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1801 return setError(E_INVALIDARG,
1802 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1803 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1804
1805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 i_setModified(IsModified_MachineData);
1808 mHWData.backup();
1809 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1810
1811 return S_OK;
1812#else
1813 NOREF(aMemoryBalloonSize);
1814 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1815#endif
1816}
1817
1818HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1819{
1820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1823 return S_OK;
1824}
1825
1826HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1827{
1828#ifdef VBOX_WITH_PAGE_SHARING
1829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1830
1831 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1832 i_setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1835 return S_OK;
1836#else
1837 NOREF(aPageFusionEnabled);
1838 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1839#endif
1840}
1841
1842HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1843{
1844 /* mBIOSSettings is constant during life time, no need to lock */
1845 aBIOSSettings = mBIOSSettings;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1851{
1852 /* mTrustedPlatformModule is constant during life time, no need to lock */
1853 aTrustedPlatformModule = mTrustedPlatformModule;
1854
1855 return S_OK;
1856}
1857
1858HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1859{
1860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 aRecordingSettings = mRecordingSettings;
1863
1864 return S_OK;
1865}
1866
1867HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1868{
1869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 aGraphicsAdapter = mGraphicsAdapter;
1872
1873 return S_OK;
1874}
1875
1876HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1877{
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 switch (aProperty)
1881 {
1882 case CPUPropertyType_PAE:
1883 *aValue = mHWData->mPAEEnabled;
1884 break;
1885
1886 case CPUPropertyType_LongMode:
1887 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1888 *aValue = TRUE;
1889 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1890 *aValue = FALSE;
1891#if HC_ARCH_BITS == 64
1892 else
1893 *aValue = TRUE;
1894#else
1895 else
1896 {
1897 *aValue = FALSE;
1898
1899 ComObjPtr<GuestOSType> pGuestOSType;
1900 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1901 pGuestOSType);
1902 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1903 {
1904 if (pGuestOSType->i_is64Bit())
1905 {
1906 ComObjPtr<Host> pHost = mParent->i_host();
1907 alock.release();
1908
1909 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1910 if (FAILED(hrc2))
1911 *aValue = FALSE;
1912 }
1913 }
1914 }
1915#endif
1916 break;
1917
1918 case CPUPropertyType_TripleFaultReset:
1919 *aValue = mHWData->mTripleFaultReset;
1920 break;
1921
1922 case CPUPropertyType_APIC:
1923 *aValue = mHWData->mAPIC;
1924 break;
1925
1926 case CPUPropertyType_X2APIC:
1927 *aValue = mHWData->mX2APIC;
1928 break;
1929
1930 case CPUPropertyType_IBPBOnVMExit:
1931 *aValue = mHWData->mIBPBOnVMExit;
1932 break;
1933
1934 case CPUPropertyType_IBPBOnVMEntry:
1935 *aValue = mHWData->mIBPBOnVMEntry;
1936 break;
1937
1938 case CPUPropertyType_SpecCtrl:
1939 *aValue = mHWData->mSpecCtrl;
1940 break;
1941
1942 case CPUPropertyType_SpecCtrlByHost:
1943 *aValue = mHWData->mSpecCtrlByHost;
1944 break;
1945
1946 case CPUPropertyType_HWVirt:
1947 *aValue = mHWData->mNestedHWVirt;
1948 break;
1949
1950 case CPUPropertyType_L1DFlushOnEMTScheduling:
1951 *aValue = mHWData->mL1DFlushOnSched;
1952 break;
1953
1954 case CPUPropertyType_L1DFlushOnVMEntry:
1955 *aValue = mHWData->mL1DFlushOnVMEntry;
1956 break;
1957
1958 case CPUPropertyType_MDSClearOnEMTScheduling:
1959 *aValue = mHWData->mMDSClearOnSched;
1960 break;
1961
1962 case CPUPropertyType_MDSClearOnVMEntry:
1963 *aValue = mHWData->mMDSClearOnVMEntry;
1964 break;
1965
1966 default:
1967 return E_INVALIDARG;
1968 }
1969 return S_OK;
1970}
1971
1972HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1973{
1974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 HRESULT rc = i_checkStateDependency(MutableStateDep);
1977 if (FAILED(rc)) return rc;
1978
1979 switch (aProperty)
1980 {
1981 case CPUPropertyType_PAE:
1982 i_setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mPAEEnabled = !!aValue;
1985 break;
1986
1987 case CPUPropertyType_LongMode:
1988 i_setModified(IsModified_MachineData);
1989 mHWData.backup();
1990 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1991 break;
1992
1993 case CPUPropertyType_TripleFaultReset:
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mTripleFaultReset = !!aValue;
1997 break;
1998
1999 case CPUPropertyType_APIC:
2000 if (mHWData->mX2APIC)
2001 aValue = TRUE;
2002 i_setModified(IsModified_MachineData);
2003 mHWData.backup();
2004 mHWData->mAPIC = !!aValue;
2005 break;
2006
2007 case CPUPropertyType_X2APIC:
2008 i_setModified(IsModified_MachineData);
2009 mHWData.backup();
2010 mHWData->mX2APIC = !!aValue;
2011 if (aValue)
2012 mHWData->mAPIC = !!aValue;
2013 break;
2014
2015 case CPUPropertyType_IBPBOnVMExit:
2016 i_setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mIBPBOnVMExit = !!aValue;
2019 break;
2020
2021 case CPUPropertyType_IBPBOnVMEntry:
2022 i_setModified(IsModified_MachineData);
2023 mHWData.backup();
2024 mHWData->mIBPBOnVMEntry = !!aValue;
2025 break;
2026
2027 case CPUPropertyType_SpecCtrl:
2028 i_setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mSpecCtrl = !!aValue;
2031 break;
2032
2033 case CPUPropertyType_SpecCtrlByHost:
2034 i_setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mSpecCtrlByHost = !!aValue;
2037 break;
2038
2039 case CPUPropertyType_HWVirt:
2040 i_setModified(IsModified_MachineData);
2041 mHWData.backup();
2042 mHWData->mNestedHWVirt = !!aValue;
2043 break;
2044
2045 case CPUPropertyType_L1DFlushOnEMTScheduling:
2046 i_setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mL1DFlushOnSched = !!aValue;
2049 break;
2050
2051 case CPUPropertyType_L1DFlushOnVMEntry:
2052 i_setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mL1DFlushOnVMEntry = !!aValue;
2055 break;
2056
2057 case CPUPropertyType_MDSClearOnEMTScheduling:
2058 i_setModified(IsModified_MachineData);
2059 mHWData.backup();
2060 mHWData->mMDSClearOnSched = !!aValue;
2061 break;
2062
2063 case CPUPropertyType_MDSClearOnVMEntry:
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mMDSClearOnVMEntry = !!aValue;
2067 break;
2068
2069 default:
2070 return E_INVALIDARG;
2071 }
2072 return S_OK;
2073}
2074
2075HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2076 ULONG *aValEcx, ULONG *aValEdx)
2077{
2078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2079 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2080 {
2081 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2082 it != mHWData->mCpuIdLeafList.end();
2083 ++it)
2084 {
2085 if (aOrdinal == 0)
2086 {
2087 const settings::CpuIdLeaf &rLeaf= *it;
2088 *aIdx = rLeaf.idx;
2089 *aSubIdx = rLeaf.idxSub;
2090 *aValEax = rLeaf.uEax;
2091 *aValEbx = rLeaf.uEbx;
2092 *aValEcx = rLeaf.uEcx;
2093 *aValEdx = rLeaf.uEdx;
2094 return S_OK;
2095 }
2096 aOrdinal--;
2097 }
2098 }
2099 return E_INVALIDARG;
2100}
2101
2102HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2103{
2104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 /*
2107 * Search the list.
2108 */
2109 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2110 {
2111 const settings::CpuIdLeaf &rLeaf= *it;
2112 if ( rLeaf.idx == aIdx
2113 && ( aSubIdx == UINT32_MAX
2114 || rLeaf.idxSub == aSubIdx) )
2115 {
2116 *aValEax = rLeaf.uEax;
2117 *aValEbx = rLeaf.uEbx;
2118 *aValEcx = rLeaf.uEcx;
2119 *aValEdx = rLeaf.uEdx;
2120 return S_OK;
2121 }
2122 }
2123
2124 return E_INVALIDARG;
2125}
2126
2127
2128HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2129{
2130 /*
2131 * Validate input before taking locks and checking state.
2132 */
2133 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2134 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2135 if ( aIdx >= UINT32_C(0x20)
2136 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2137 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2138 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2139
2140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2141 HRESULT rc = i_checkStateDependency(MutableStateDep);
2142 if (FAILED(rc)) return rc;
2143
2144 /*
2145 * Impose a maximum number of leaves.
2146 */
2147 if (mHWData->mCpuIdLeafList.size() > 256)
2148 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2149
2150 /*
2151 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2152 */
2153 i_setModified(IsModified_MachineData);
2154 mHWData.backup();
2155
2156 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2157 {
2158 settings::CpuIdLeaf &rLeaf= *it;
2159 if ( rLeaf.idx == aIdx
2160 && ( aSubIdx == UINT32_MAX
2161 || rLeaf.idxSub == aSubIdx) )
2162 it = mHWData->mCpuIdLeafList.erase(it);
2163 else
2164 ++it;
2165 }
2166
2167 settings::CpuIdLeaf NewLeaf;
2168 NewLeaf.idx = aIdx;
2169 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2170 NewLeaf.uEax = aValEax;
2171 NewLeaf.uEbx = aValEbx;
2172 NewLeaf.uEcx = aValEcx;
2173 NewLeaf.uEdx = aValEdx;
2174 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2175 return S_OK;
2176}
2177
2178HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2179{
2180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2181
2182 HRESULT rc = i_checkStateDependency(MutableStateDep);
2183 if (FAILED(rc)) return rc;
2184
2185 /*
2186 * Do the removal.
2187 */
2188 bool fModified = mHWData.isBackedUp();
2189 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2190 {
2191 settings::CpuIdLeaf &rLeaf= *it;
2192 if ( rLeaf.idx == aIdx
2193 && ( aSubIdx == UINT32_MAX
2194 || rLeaf.idxSub == aSubIdx) )
2195 {
2196 if (!fModified)
2197 {
2198 fModified = true;
2199 i_setModified(IsModified_MachineData);
2200 mHWData.backup();
2201 // Start from the beginning, since mHWData.backup() creates
2202 // a new list, causing iterator mixup. This makes sure that
2203 // the settings are not unnecessarily marked as modified,
2204 // at the price of extra list walking.
2205 it = mHWData->mCpuIdLeafList.begin();
2206 }
2207 else
2208 it = mHWData->mCpuIdLeafList.erase(it);
2209 }
2210 else
2211 ++it;
2212 }
2213
2214 return S_OK;
2215}
2216
2217HRESULT Machine::removeAllCPUIDLeaves()
2218{
2219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2220
2221 HRESULT rc = i_checkStateDependency(MutableStateDep);
2222 if (FAILED(rc)) return rc;
2223
2224 if (mHWData->mCpuIdLeafList.size() > 0)
2225 {
2226 i_setModified(IsModified_MachineData);
2227 mHWData.backup();
2228
2229 mHWData->mCpuIdLeafList.clear();
2230 }
2231
2232 return S_OK;
2233}
2234HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2235{
2236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2237
2238 switch(aProperty)
2239 {
2240 case HWVirtExPropertyType_Enabled:
2241 *aValue = mHWData->mHWVirtExEnabled;
2242 break;
2243
2244 case HWVirtExPropertyType_VPID:
2245 *aValue = mHWData->mHWVirtExVPIDEnabled;
2246 break;
2247
2248 case HWVirtExPropertyType_NestedPaging:
2249 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2250 break;
2251
2252 case HWVirtExPropertyType_UnrestrictedExecution:
2253 *aValue = mHWData->mHWVirtExUXEnabled;
2254 break;
2255
2256 case HWVirtExPropertyType_LargePages:
2257 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2258#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2259 *aValue = FALSE;
2260#endif
2261 break;
2262
2263 case HWVirtExPropertyType_Force:
2264 *aValue = mHWData->mHWVirtExForceEnabled;
2265 break;
2266
2267 case HWVirtExPropertyType_UseNativeApi:
2268 *aValue = mHWData->mHWVirtExUseNativeApi;
2269 break;
2270
2271 case HWVirtExPropertyType_VirtVmsaveVmload:
2272 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2273 break;
2274
2275 default:
2276 return E_INVALIDARG;
2277 }
2278 return S_OK;
2279}
2280
2281HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2282{
2283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2284
2285 HRESULT rc = i_checkStateDependency(MutableStateDep);
2286 if (FAILED(rc)) return rc;
2287
2288 switch (aProperty)
2289 {
2290 case HWVirtExPropertyType_Enabled:
2291 i_setModified(IsModified_MachineData);
2292 mHWData.backup();
2293 mHWData->mHWVirtExEnabled = !!aValue;
2294 break;
2295
2296 case HWVirtExPropertyType_VPID:
2297 i_setModified(IsModified_MachineData);
2298 mHWData.backup();
2299 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2300 break;
2301
2302 case HWVirtExPropertyType_NestedPaging:
2303 i_setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2306 break;
2307
2308 case HWVirtExPropertyType_UnrestrictedExecution:
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mHWVirtExUXEnabled = !!aValue;
2312 break;
2313
2314 case HWVirtExPropertyType_LargePages:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2318 break;
2319
2320 case HWVirtExPropertyType_Force:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mHWVirtExForceEnabled = !!aValue;
2324 break;
2325
2326 case HWVirtExPropertyType_UseNativeApi:
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mHWVirtExUseNativeApi = !!aValue;
2330 break;
2331
2332 case HWVirtExPropertyType_VirtVmsaveVmload:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2336 break;
2337
2338 default:
2339 return E_INVALIDARG;
2340 }
2341
2342 return S_OK;
2343}
2344
2345HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2346{
2347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2348
2349 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2350
2351 return S_OK;
2352}
2353
2354HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2355{
2356 /** @todo (r=dmik):
2357 * 1. Allow to change the name of the snapshot folder containing snapshots
2358 * 2. Rename the folder on disk instead of just changing the property
2359 * value (to be smart and not to leave garbage). Note that it cannot be
2360 * done here because the change may be rolled back. Thus, the right
2361 * place is #saveSettings().
2362 */
2363
2364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2365
2366 HRESULT rc = i_checkStateDependency(MutableStateDep);
2367 if (FAILED(rc)) return rc;
2368
2369 if (!mData->mCurrentSnapshot.isNull())
2370 return setError(E_FAIL,
2371 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2372
2373 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2374
2375 if (strSnapshotFolder.isEmpty())
2376 strSnapshotFolder = "Snapshots";
2377 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2378 if (RT_FAILURE(vrc))
2379 return setErrorBoth(E_FAIL, vrc,
2380 tr("Invalid snapshot folder '%s' (%Rrc)"),
2381 strSnapshotFolder.c_str(), vrc);
2382
2383 i_setModified(IsModified_MachineData);
2384 mUserData.backup();
2385
2386 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2387
2388 return S_OK;
2389}
2390
2391HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2392{
2393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2394
2395 aMediumAttachments.resize(mMediumAttachments->size());
2396 size_t i = 0;
2397 for (MediumAttachmentList::const_iterator
2398 it = mMediumAttachments->begin();
2399 it != mMediumAttachments->end();
2400 ++it, ++i)
2401 aMediumAttachments[i] = *it;
2402
2403 return S_OK;
2404}
2405
2406HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2407{
2408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2409
2410 Assert(!!mVRDEServer);
2411
2412 aVRDEServer = mVRDEServer;
2413
2414 return S_OK;
2415}
2416
2417HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2418{
2419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2420
2421 aAudioAdapter = mAudioAdapter;
2422
2423 return S_OK;
2424}
2425
2426HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2427{
2428#ifdef VBOX_WITH_VUSB
2429 clearError();
2430 MultiResult rc(S_OK);
2431
2432# ifdef VBOX_WITH_USB
2433 rc = mParent->i_host()->i_checkUSBProxyService();
2434 if (FAILED(rc)) return rc;
2435# endif
2436
2437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2438
2439 aUSBControllers.resize(mUSBControllers->size());
2440 size_t i = 0;
2441 for (USBControllerList::const_iterator
2442 it = mUSBControllers->begin();
2443 it != mUSBControllers->end();
2444 ++it, ++i)
2445 aUSBControllers[i] = *it;
2446
2447 return S_OK;
2448#else
2449 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2450 * extended error info to indicate that USB is simply not available
2451 * (w/o treating it as a failure), for example, as in OSE */
2452 NOREF(aUSBControllers);
2453 ReturnComNotImplemented();
2454#endif /* VBOX_WITH_VUSB */
2455}
2456
2457HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2458{
2459#ifdef VBOX_WITH_VUSB
2460 clearError();
2461 MultiResult rc(S_OK);
2462
2463# ifdef VBOX_WITH_USB
2464 rc = mParent->i_host()->i_checkUSBProxyService();
2465 if (FAILED(rc)) return rc;
2466# endif
2467
2468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2469
2470 aUSBDeviceFilters = mUSBDeviceFilters;
2471 return rc;
2472#else
2473 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2474 * extended error info to indicate that USB is simply not available
2475 * (w/o treating it as a failure), for example, as in OSE */
2476 NOREF(aUSBDeviceFilters);
2477 ReturnComNotImplemented();
2478#endif /* VBOX_WITH_VUSB */
2479}
2480
2481HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2482{
2483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2484
2485 aSettingsFilePath = mData->m_strConfigFileFull;
2486
2487 return S_OK;
2488}
2489
2490HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2491{
2492 RT_NOREF(aSettingsFilePath);
2493 ReturnComNotImplemented();
2494}
2495
2496HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2497{
2498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2499
2500 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2501 if (FAILED(rc)) return rc;
2502
2503 if (!mData->pMachineConfigFile->fileExists())
2504 // this is a new machine, and no config file exists yet:
2505 *aSettingsModified = TRUE;
2506 else
2507 *aSettingsModified = (mData->flModifications != 0);
2508
2509 return S_OK;
2510}
2511
2512HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2513{
2514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 *aSessionState = mData->mSession.mState;
2517
2518 return S_OK;
2519}
2520
2521HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2522{
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 aSessionName = mData->mSession.mName;
2526
2527 return S_OK;
2528}
2529
2530HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 *aSessionPID = mData->mSession.mPID;
2535
2536 return S_OK;
2537}
2538
2539HRESULT Machine::getState(MachineState_T *aState)
2540{
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 *aState = mData->mMachineState;
2544 Assert(mData->mMachineState != MachineState_Null);
2545
2546 return S_OK;
2547}
2548
2549HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2550{
2551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2552
2553 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2554
2555 return S_OK;
2556}
2557
2558HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2559{
2560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 aStateFilePath = mSSData->strStateFilePath;
2563
2564 return S_OK;
2565}
2566
2567HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2568{
2569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2570
2571 i_getLogFolder(aLogFolder);
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 aCurrentSnapshot = mData->mCurrentSnapshot;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2586{
2587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2590 ? 0
2591 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2592
2593 return S_OK;
2594}
2595
2596HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2597{
2598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 /* Note: for machines with no snapshots, we always return FALSE
2601 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2602 * reasons :) */
2603
2604 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2605 ? FALSE
2606 : mData->mCurrentStateModified;
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 aSharedFolders.resize(mHWData->mSharedFolders.size());
2616 size_t i = 0;
2617 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2618 it = mHWData->mSharedFolders.begin();
2619 it != mHWData->mSharedFolders.end();
2620 ++it, ++i)
2621 aSharedFolders[i] = *it;
2622
2623 return S_OK;
2624}
2625
2626HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2627{
2628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2629
2630 *aClipboardMode = mHWData->mClipboardMode;
2631
2632 return S_OK;
2633}
2634
2635HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2636{
2637 HRESULT rc = S_OK;
2638
2639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 alock.release();
2642 rc = i_onClipboardModeChange(aClipboardMode);
2643 alock.acquire();
2644 if (FAILED(rc)) return rc;
2645
2646 i_setModified(IsModified_MachineData);
2647 mHWData.backup();
2648 mHWData->mClipboardMode = aClipboardMode;
2649
2650 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2651 if (Global::IsOnline(mData->mMachineState))
2652 i_saveSettings(NULL);
2653
2654 return S_OK;
2655}
2656
2657HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2658{
2659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2662
2663 return S_OK;
2664}
2665
2666HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2667{
2668 HRESULT rc = S_OK;
2669
2670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2671
2672 alock.release();
2673 rc = i_onClipboardFileTransferModeChange(aEnabled);
2674 alock.acquire();
2675 if (FAILED(rc)) return rc;
2676
2677 i_setModified(IsModified_MachineData);
2678 mHWData.backup();
2679 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2680
2681 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2682 if (Global::IsOnline(mData->mMachineState))
2683 i_saveSettings(NULL);
2684
2685 return S_OK;
2686}
2687
2688HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2689{
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 *aDnDMode = mHWData->mDnDMode;
2693
2694 return S_OK;
2695}
2696
2697HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2698{
2699 HRESULT rc = S_OK;
2700
2701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 alock.release();
2704 rc = i_onDnDModeChange(aDnDMode);
2705
2706 alock.acquire();
2707 if (FAILED(rc)) return rc;
2708
2709 i_setModified(IsModified_MachineData);
2710 mHWData.backup();
2711 mHWData->mDnDMode = aDnDMode;
2712
2713 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2714 if (Global::IsOnline(mData->mMachineState))
2715 i_saveSettings(NULL);
2716
2717 return S_OK;
2718}
2719
2720HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2721{
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 aStorageControllers.resize(mStorageControllers->size());
2725 size_t i = 0;
2726 for (StorageControllerList::const_iterator
2727 it = mStorageControllers->begin();
2728 it != mStorageControllers->end();
2729 ++it, ++i)
2730 aStorageControllers[i] = *it;
2731
2732 return S_OK;
2733}
2734
2735HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2736{
2737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 *aEnabled = mUserData->s.fTeleporterEnabled;
2740
2741 return S_OK;
2742}
2743
2744HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2745{
2746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 /* Only allow it to be set to true when PoweredOff or Aborted.
2749 (Clearing it is always permitted.) */
2750 if ( aTeleporterEnabled
2751 && mData->mRegistered
2752 && ( !i_isSessionMachine()
2753 || ( mData->mMachineState != MachineState_PoweredOff
2754 && mData->mMachineState != MachineState_Teleported
2755 && mData->mMachineState != MachineState_Aborted
2756 )
2757 )
2758 )
2759 return setError(VBOX_E_INVALID_VM_STATE,
2760 tr("The machine is not powered off (state is %s)"),
2761 Global::stringifyMachineState(mData->mMachineState));
2762
2763 i_setModified(IsModified_MachineData);
2764 mUserData.backup();
2765 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2780{
2781 if (aTeleporterPort >= _64K)
2782 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2783
2784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2787 if (FAILED(rc)) return rc;
2788
2789 i_setModified(IsModified_MachineData);
2790 mUserData.backup();
2791 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2801
2802 return S_OK;
2803}
2804
2805HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2806{
2807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2810 if (FAILED(rc)) return rc;
2811
2812 i_setModified(IsModified_MachineData);
2813 mUserData.backup();
2814 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2820{
2821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2822 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2828{
2829 /*
2830 * Hash the password first.
2831 */
2832 com::Utf8Str aT = aTeleporterPassword;
2833
2834 if (!aT.isEmpty())
2835 {
2836 if (VBoxIsPasswordHashed(&aT))
2837 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2838 VBoxHashPassword(&aT);
2839 }
2840
2841 /*
2842 * Do the update.
2843 */
2844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2845 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2846 if (SUCCEEDED(hrc))
2847 {
2848 i_setModified(IsModified_MachineData);
2849 mUserData.backup();
2850 mUserData->s.strTeleporterPassword = aT;
2851 }
2852
2853 return hrc;
2854}
2855
2856HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2857{
2858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
2860 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2866{
2867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 /* Only allow it to be set to true when PoweredOff or Aborted.
2870 (Clearing it is always permitted.) */
2871 if ( aRTCUseUTC
2872 && mData->mRegistered
2873 && ( !i_isSessionMachine()
2874 || ( mData->mMachineState != MachineState_PoweredOff
2875 && mData->mMachineState != MachineState_Teleported
2876 && mData->mMachineState != MachineState_Aborted
2877 )
2878 )
2879 )
2880 return setError(VBOX_E_INVALID_VM_STATE,
2881 tr("The machine is not powered off (state is %s)"),
2882 Global::stringifyMachineState(mData->mMachineState));
2883
2884 i_setModified(IsModified_MachineData);
2885 mUserData.backup();
2886 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2887
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2896
2897 return S_OK;
2898}
2899
2900HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2901{
2902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2903
2904 HRESULT rc = i_checkStateDependency(MutableStateDep);
2905 if (FAILED(rc)) return rc;
2906
2907 i_setModified(IsModified_MachineData);
2908 mHWData.backup();
2909 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2915{
2916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 *aIOCacheSize = mHWData->mIOCacheSize;
2919
2920 return S_OK;
2921}
2922
2923HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2924{
2925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 HRESULT rc = i_checkStateDependency(MutableStateDep);
2928 if (FAILED(rc)) return rc;
2929
2930 i_setModified(IsModified_MachineData);
2931 mHWData.backup();
2932 mHWData->mIOCacheSize = aIOCacheSize;
2933
2934 return S_OK;
2935}
2936
2937
2938/**
2939 * @note Locks objects!
2940 */
2941HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2942 LockType_T aLockType)
2943{
2944 /* check the session state */
2945 SessionState_T state;
2946 HRESULT rc = aSession->COMGETTER(State)(&state);
2947 if (FAILED(rc)) return rc;
2948
2949 if (state != SessionState_Unlocked)
2950 return setError(VBOX_E_INVALID_OBJECT_STATE,
2951 tr("The given session is busy"));
2952
2953 // get the client's IInternalSessionControl interface
2954 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2955 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2956 E_INVALIDARG);
2957
2958 // session name (only used in some code paths)
2959 Utf8Str strSessionName;
2960
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 if (!mData->mRegistered)
2964 return setError(E_UNEXPECTED,
2965 tr("The machine '%s' is not registered"),
2966 mUserData->s.strName.c_str());
2967
2968 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2969
2970 SessionState_T oldState = mData->mSession.mState;
2971 /* Hack: in case the session is closing and there is a progress object
2972 * which allows waiting for the session to be closed, take the opportunity
2973 * and do a limited wait (max. 1 second). This helps a lot when the system
2974 * is busy and thus session closing can take a little while. */
2975 if ( mData->mSession.mState == SessionState_Unlocking
2976 && mData->mSession.mProgress)
2977 {
2978 alock.release();
2979 mData->mSession.mProgress->WaitForCompletion(1000);
2980 alock.acquire();
2981 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2982 }
2983
2984 // try again now
2985 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2986 // (i.e. session machine exists)
2987 && (aLockType == LockType_Shared) // caller wants a shared link to the
2988 // existing session that holds the write lock:
2989 )
2990 {
2991 // OK, share the session... we are now dealing with three processes:
2992 // 1) VBoxSVC (where this code runs);
2993 // 2) process C: the caller's client process (who wants a shared session);
2994 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2995
2996 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2997 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2998 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2999 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3000 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3001
3002 /*
3003 * Release the lock before calling the client process. It's safe here
3004 * since the only thing to do after we get the lock again is to add
3005 * the remote control to the list (which doesn't directly influence
3006 * anything).
3007 */
3008 alock.release();
3009
3010 // get the console of the session holding the write lock (this is a remote call)
3011 ComPtr<IConsole> pConsoleW;
3012 if (mData->mSession.mLockType == LockType_VM)
3013 {
3014 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3015 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3016 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3017 if (FAILED(rc))
3018 // the failure may occur w/o any error info (from RPC), so provide one
3019 return setError(VBOX_E_VM_ERROR,
3020 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3021 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3022 }
3023
3024 // share the session machine and W's console with the caller's session
3025 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3026 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3027 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3028
3029 if (FAILED(rc))
3030 // the failure may occur w/o any error info (from RPC), so provide one
3031 return setError(VBOX_E_VM_ERROR,
3032 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3033 alock.acquire();
3034
3035 // need to revalidate the state after acquiring the lock again
3036 if (mData->mSession.mState != SessionState_Locked)
3037 {
3038 pSessionControl->Uninitialize();
3039 return setError(VBOX_E_INVALID_SESSION_STATE,
3040 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3041 mUserData->s.strName.c_str());
3042 }
3043
3044 // add the caller's session to the list
3045 mData->mSession.mRemoteControls.push_back(pSessionControl);
3046 }
3047 else if ( mData->mSession.mState == SessionState_Locked
3048 || mData->mSession.mState == SessionState_Unlocking
3049 )
3050 {
3051 // sharing not permitted, or machine still unlocking:
3052 return setError(VBOX_E_INVALID_OBJECT_STATE,
3053 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3054 mUserData->s.strName.c_str());
3055 }
3056 else
3057 {
3058 // machine is not locked: then write-lock the machine (create the session machine)
3059
3060 // must not be busy
3061 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3062
3063 // get the caller's session PID
3064 RTPROCESS pid = NIL_RTPROCESS;
3065 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3066 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3067 Assert(pid != NIL_RTPROCESS);
3068
3069 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3070
3071 if (fLaunchingVMProcess)
3072 {
3073 if (mData->mSession.mPID == NIL_RTPROCESS)
3074 {
3075 // two or more clients racing for a lock, the one which set the
3076 // session state to Spawning will win, the others will get an
3077 // error as we can't decide here if waiting a little would help
3078 // (only for shared locks this would avoid an error)
3079 return setError(VBOX_E_INVALID_OBJECT_STATE,
3080 tr("The machine '%s' already has a lock request pending"),
3081 mUserData->s.strName.c_str());
3082 }
3083
3084 // this machine is awaiting for a spawning session to be opened:
3085 // then the calling process must be the one that got started by
3086 // LaunchVMProcess()
3087
3088 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3089 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3090
3091#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3092 /* Hardened windows builds spawns three processes when a VM is
3093 launched, the 3rd one is the one that will end up here. */
3094 RTPROCESS pidParent;
3095 int vrc = RTProcQueryParent(pid, &pidParent);
3096 if (RT_SUCCESS(vrc))
3097 vrc = RTProcQueryParent(pidParent, &pidParent);
3098 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3099 || vrc == VERR_ACCESS_DENIED)
3100 {
3101 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3102 mData->mSession.mPID = pid;
3103 }
3104#endif
3105
3106 if (mData->mSession.mPID != pid)
3107 return setError(E_ACCESSDENIED,
3108 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3109 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3110 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3111 }
3112
3113 // create the mutable SessionMachine from the current machine
3114 ComObjPtr<SessionMachine> sessionMachine;
3115 sessionMachine.createObject();
3116 rc = sessionMachine->init(this);
3117 AssertComRC(rc);
3118
3119 /* NOTE: doing return from this function after this point but
3120 * before the end is forbidden since it may call SessionMachine::uninit()
3121 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3122 * lock while still holding the Machine lock in alock so that a deadlock
3123 * is possible due to the wrong lock order. */
3124
3125 if (SUCCEEDED(rc))
3126 {
3127 /*
3128 * Set the session state to Spawning to protect against subsequent
3129 * attempts to open a session and to unregister the machine after
3130 * we release the lock.
3131 */
3132 SessionState_T origState = mData->mSession.mState;
3133 mData->mSession.mState = SessionState_Spawning;
3134
3135#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3136 /* Get the client token ID to be passed to the client process */
3137 Utf8Str strTokenId;
3138 sessionMachine->i_getTokenId(strTokenId);
3139 Assert(!strTokenId.isEmpty());
3140#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3141 /* Get the client token to be passed to the client process */
3142 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3143 /* The token is now "owned" by pToken, fix refcount */
3144 if (!pToken.isNull())
3145 pToken->Release();
3146#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3147
3148 /*
3149 * Release the lock before calling the client process -- it will call
3150 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3151 * because the state is Spawning, so that LaunchVMProcess() and
3152 * LockMachine() calls will fail. This method, called before we
3153 * acquire the lock again, will fail because of the wrong PID.
3154 *
3155 * Note that mData->mSession.mRemoteControls accessed outside
3156 * the lock may not be modified when state is Spawning, so it's safe.
3157 */
3158 alock.release();
3159
3160 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3161#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3162 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3163#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3164 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3165 /* Now the token is owned by the client process. */
3166 pToken.setNull();
3167#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3168 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3169
3170 /* The failure may occur w/o any error info (from RPC), so provide one */
3171 if (FAILED(rc))
3172 setError(VBOX_E_VM_ERROR,
3173 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3174
3175 // get session name, either to remember or to compare against
3176 // the already known session name.
3177 {
3178 Bstr bstrSessionName;
3179 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3180 if (SUCCEEDED(rc2))
3181 strSessionName = bstrSessionName;
3182 }
3183
3184 if ( SUCCEEDED(rc)
3185 && fLaunchingVMProcess
3186 )
3187 {
3188 /* complete the remote session initialization */
3189
3190 /* get the console from the direct session */
3191 ComPtr<IConsole> console;
3192 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3193 ComAssertComRC(rc);
3194
3195 if (SUCCEEDED(rc) && !console)
3196 {
3197 ComAssert(!!console);
3198 rc = E_FAIL;
3199 }
3200
3201 /* assign machine & console to the remote session */
3202 if (SUCCEEDED(rc))
3203 {
3204 /*
3205 * after LaunchVMProcess(), the first and the only
3206 * entry in remoteControls is that remote session
3207 */
3208 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3209 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3210 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3211
3212 /* The failure may occur w/o any error info (from RPC), so provide one */
3213 if (FAILED(rc))
3214 setError(VBOX_E_VM_ERROR,
3215 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3216 }
3217
3218 if (FAILED(rc))
3219 pSessionControl->Uninitialize();
3220 }
3221
3222 /* acquire the lock again */
3223 alock.acquire();
3224
3225 /* Restore the session state */
3226 mData->mSession.mState = origState;
3227 }
3228
3229 // finalize spawning anyway (this is why we don't return on errors above)
3230 if (fLaunchingVMProcess)
3231 {
3232 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3233 /* Note that the progress object is finalized later */
3234 /** @todo Consider checking mData->mSession.mProgress for cancellation
3235 * around here. */
3236
3237 /* We don't reset mSession.mPID here because it is necessary for
3238 * SessionMachine::uninit() to reap the child process later. */
3239
3240 if (FAILED(rc))
3241 {
3242 /* Close the remote session, remove the remote control from the list
3243 * and reset session state to Closed (@note keep the code in sync
3244 * with the relevant part in checkForSpawnFailure()). */
3245
3246 Assert(mData->mSession.mRemoteControls.size() == 1);
3247 if (mData->mSession.mRemoteControls.size() == 1)
3248 {
3249 ErrorInfoKeeper eik;
3250 mData->mSession.mRemoteControls.front()->Uninitialize();
3251 }
3252
3253 mData->mSession.mRemoteControls.clear();
3254 mData->mSession.mState = SessionState_Unlocked;
3255 }
3256 }
3257 else
3258 {
3259 /* memorize PID of the directly opened session */
3260 if (SUCCEEDED(rc))
3261 mData->mSession.mPID = pid;
3262 }
3263
3264 if (SUCCEEDED(rc))
3265 {
3266 mData->mSession.mLockType = aLockType;
3267 /* memorize the direct session control and cache IUnknown for it */
3268 mData->mSession.mDirectControl = pSessionControl;
3269 mData->mSession.mState = SessionState_Locked;
3270 if (!fLaunchingVMProcess)
3271 mData->mSession.mName = strSessionName;
3272 /* associate the SessionMachine with this Machine */
3273 mData->mSession.mMachine = sessionMachine;
3274
3275 /* request an IUnknown pointer early from the remote party for later
3276 * identity checks (it will be internally cached within mDirectControl
3277 * at least on XPCOM) */
3278 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3279 NOREF(unk);
3280 }
3281
3282 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3283 * would break the lock order */
3284 alock.release();
3285
3286 /* uninitialize the created session machine on failure */
3287 if (FAILED(rc))
3288 sessionMachine->uninit();
3289 }
3290
3291 if (SUCCEEDED(rc))
3292 {
3293 /*
3294 * tell the client watcher thread to update the set of
3295 * machines that have open sessions
3296 */
3297 mParent->i_updateClientWatcher();
3298
3299 if (oldState != SessionState_Locked)
3300 /* fire an event */
3301 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3302 }
3303
3304 return rc;
3305}
3306
3307/**
3308 * @note Locks objects!
3309 */
3310HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3311 const com::Utf8Str &aName,
3312 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3313 ComPtr<IProgress> &aProgress)
3314{
3315 Utf8Str strFrontend(aName);
3316 /* "emergencystop" doesn't need the session, so skip the checks/interface
3317 * retrieval. This code doesn't quite fit in here, but introducing a
3318 * special API method would be even more effort, and would require explicit
3319 * support by every API client. It's better to hide the feature a bit. */
3320 if (strFrontend != "emergencystop")
3321 CheckComArgNotNull(aSession);
3322
3323 HRESULT rc = S_OK;
3324 if (strFrontend.isEmpty())
3325 {
3326 Bstr bstrFrontend;
3327 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3328 if (FAILED(rc))
3329 return rc;
3330 strFrontend = bstrFrontend;
3331 if (strFrontend.isEmpty())
3332 {
3333 ComPtr<ISystemProperties> systemProperties;
3334 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3335 if (FAILED(rc))
3336 return rc;
3337 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3338 if (FAILED(rc))
3339 return rc;
3340 strFrontend = bstrFrontend;
3341 }
3342 /* paranoia - emergencystop is not a valid default */
3343 if (strFrontend == "emergencystop")
3344 strFrontend = Utf8Str::Empty;
3345 }
3346 /* default frontend: Qt GUI */
3347 if (strFrontend.isEmpty())
3348 strFrontend = "GUI/Qt";
3349
3350 if (strFrontend != "emergencystop")
3351 {
3352 /* check the session state */
3353 SessionState_T state;
3354 rc = aSession->COMGETTER(State)(&state);
3355 if (FAILED(rc))
3356 return rc;
3357
3358 if (state != SessionState_Unlocked)
3359 return setError(VBOX_E_INVALID_OBJECT_STATE,
3360 tr("The given session is busy"));
3361
3362 /* get the IInternalSessionControl interface */
3363 ComPtr<IInternalSessionControl> control(aSession);
3364 ComAssertMsgRet(!control.isNull(),
3365 ("No IInternalSessionControl interface"),
3366 E_INVALIDARG);
3367
3368 /* get the teleporter enable state for the progress object init. */
3369 BOOL fTeleporterEnabled;
3370 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3371 if (FAILED(rc))
3372 return rc;
3373
3374 /* create a progress object */
3375 ComObjPtr<ProgressProxy> progress;
3376 progress.createObject();
3377 rc = progress->init(mParent,
3378 static_cast<IMachine*>(this),
3379 Bstr(tr("Starting VM")).raw(),
3380 TRUE /* aCancelable */,
3381 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3382 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3383 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3384 2 /* uFirstOperationWeight */,
3385 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3386
3387 if (SUCCEEDED(rc))
3388 {
3389 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3390 if (SUCCEEDED(rc))
3391 {
3392 aProgress = progress;
3393
3394 /* signal the client watcher thread */
3395 mParent->i_updateClientWatcher();
3396
3397 /* fire an event */
3398 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3399 }
3400 }
3401 }
3402 else
3403 {
3404 /* no progress object - either instant success or failure */
3405 aProgress = NULL;
3406
3407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3408
3409 if (mData->mSession.mState != SessionState_Locked)
3410 return setError(VBOX_E_INVALID_OBJECT_STATE,
3411 tr("The machine '%s' is not locked by a session"),
3412 mUserData->s.strName.c_str());
3413
3414 /* must have a VM process associated - do not kill normal API clients
3415 * with an open session */
3416 if (!Global::IsOnline(mData->mMachineState))
3417 return setError(VBOX_E_INVALID_OBJECT_STATE,
3418 tr("The machine '%s' does not have a VM process"),
3419 mUserData->s.strName.c_str());
3420
3421 /* forcibly terminate the VM process */
3422 if (mData->mSession.mPID != NIL_RTPROCESS)
3423 RTProcTerminate(mData->mSession.mPID);
3424
3425 /* signal the client watcher thread, as most likely the client has
3426 * been terminated */
3427 mParent->i_updateClientWatcher();
3428 }
3429
3430 return rc;
3431}
3432
3433HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3434{
3435 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3436 return setError(E_INVALIDARG,
3437 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3438 aPosition, SchemaDefs::MaxBootPosition);
3439
3440 if (aDevice == DeviceType_USB)
3441 return setError(E_NOTIMPL,
3442 tr("Booting from USB device is currently not supported"));
3443
3444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3445
3446 HRESULT rc = i_checkStateDependency(MutableStateDep);
3447 if (FAILED(rc)) return rc;
3448
3449 i_setModified(IsModified_MachineData);
3450 mHWData.backup();
3451 mHWData->mBootOrder[aPosition - 1] = aDevice;
3452
3453 return S_OK;
3454}
3455
3456HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3457{
3458 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3459 return setError(E_INVALIDARG,
3460 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3461 aPosition, SchemaDefs::MaxBootPosition);
3462
3463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3464
3465 *aDevice = mHWData->mBootOrder[aPosition - 1];
3466
3467 return S_OK;
3468}
3469
3470HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3471 LONG aControllerPort,
3472 LONG aDevice,
3473 DeviceType_T aType,
3474 const ComPtr<IMedium> &aMedium)
3475{
3476 IMedium *aM = aMedium;
3477 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3478 aName.c_str(), aControllerPort, aDevice, aType, aM));
3479
3480 // request the host lock first, since might be calling Host methods for getting host drives;
3481 // next, protect the media tree all the while we're in here, as well as our member variables
3482 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3483 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3484
3485 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3486 if (FAILED(rc)) return rc;
3487
3488 /// @todo NEWMEDIA implicit machine registration
3489 if (!mData->mRegistered)
3490 return setError(VBOX_E_INVALID_OBJECT_STATE,
3491 tr("Cannot attach storage devices to an unregistered machine"));
3492
3493 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3494
3495 /* Check for an existing controller. */
3496 ComObjPtr<StorageController> ctl;
3497 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3498 if (FAILED(rc)) return rc;
3499
3500 StorageControllerType_T ctrlType;
3501 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3502 if (FAILED(rc))
3503 return setError(E_FAIL,
3504 tr("Could not get type of controller '%s'"),
3505 aName.c_str());
3506
3507 bool fSilent = false;
3508 Utf8Str strReconfig;
3509
3510 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3511 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3512 if ( mData->mMachineState == MachineState_Paused
3513 && strReconfig == "1")
3514 fSilent = true;
3515
3516 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3517 bool fHotplug = false;
3518 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3519 fHotplug = true;
3520
3521 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3522 return setError(VBOX_E_INVALID_VM_STATE,
3523 tr("Controller '%s' does not support hotplugging"),
3524 aName.c_str());
3525
3526 // check that the port and device are not out of range
3527 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3528 if (FAILED(rc)) return rc;
3529
3530 /* check if the device slot is already busy */
3531 MediumAttachment *pAttachTemp;
3532 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3533 aName,
3534 aControllerPort,
3535 aDevice)))
3536 {
3537 Medium *pMedium = pAttachTemp->i_getMedium();
3538 if (pMedium)
3539 {
3540 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3541 return setError(VBOX_E_OBJECT_IN_USE,
3542 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3543 pMedium->i_getLocationFull().c_str(),
3544 aControllerPort,
3545 aDevice,
3546 aName.c_str());
3547 }
3548 else
3549 return setError(VBOX_E_OBJECT_IN_USE,
3550 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3551 aControllerPort, aDevice, aName.c_str());
3552 }
3553
3554 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3555 if (aMedium && medium.isNull())
3556 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3557
3558 AutoCaller mediumCaller(medium);
3559 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3560
3561 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3562
3563 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3564 && !medium.isNull()
3565 && ( medium->i_getType() != MediumType_Readonly
3566 || medium->i_getDeviceType() != DeviceType_DVD)
3567 )
3568 return setError(VBOX_E_OBJECT_IN_USE,
3569 tr("Medium '%s' is already attached to this virtual machine"),
3570 medium->i_getLocationFull().c_str());
3571
3572 if (!medium.isNull())
3573 {
3574 MediumType_T mtype = medium->i_getType();
3575 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3576 // For DVDs it's not written to the config file, so needs no global config
3577 // version bump. For floppies it's a new attribute "type", which is ignored
3578 // by older VirtualBox version, so needs no global config version bump either.
3579 // For hard disks this type is not accepted.
3580 if (mtype == MediumType_MultiAttach)
3581 {
3582 // This type is new with VirtualBox 4.0 and therefore requires settings
3583 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3584 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3585 // two reasons: The medium type is a property of the media registry tree, which
3586 // can reside in the global config file (for pre-4.0 media); we would therefore
3587 // possibly need to bump the global config version. We don't want to do that though
3588 // because that might make downgrading to pre-4.0 impossible.
3589 // As a result, we can only use these two new types if the medium is NOT in the
3590 // global registry:
3591 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3592 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3593 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3594 )
3595 return setError(VBOX_E_INVALID_OBJECT_STATE,
3596 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3597 "to machines that were created with VirtualBox 4.0 or later"),
3598 medium->i_getLocationFull().c_str());
3599 }
3600 }
3601
3602 bool fIndirect = false;
3603 if (!medium.isNull())
3604 fIndirect = medium->i_isReadOnly();
3605 bool associate = true;
3606
3607 do
3608 {
3609 if ( aType == DeviceType_HardDisk
3610 && mMediumAttachments.isBackedUp())
3611 {
3612 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3613
3614 /* check if the medium was attached to the VM before we started
3615 * changing attachments in which case the attachment just needs to
3616 * be restored */
3617 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3618 {
3619 AssertReturn(!fIndirect, E_FAIL);
3620
3621 /* see if it's the same bus/channel/device */
3622 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3623 {
3624 /* the simplest case: restore the whole attachment
3625 * and return, nothing else to do */
3626 mMediumAttachments->push_back(pAttachTemp);
3627
3628 /* Reattach the medium to the VM. */
3629 if (fHotplug || fSilent)
3630 {
3631 mediumLock.release();
3632 treeLock.release();
3633 alock.release();
3634
3635 MediumLockList *pMediumLockList(new MediumLockList());
3636
3637 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3638 medium /* pToLockWrite */,
3639 false /* fMediumLockWriteAll */,
3640 NULL,
3641 *pMediumLockList);
3642 alock.acquire();
3643 if (FAILED(rc))
3644 delete pMediumLockList;
3645 else
3646 {
3647 mData->mSession.mLockedMedia.Unlock();
3648 alock.release();
3649 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3650 mData->mSession.mLockedMedia.Lock();
3651 alock.acquire();
3652 }
3653 alock.release();
3654
3655 if (SUCCEEDED(rc))
3656 {
3657 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3658 /* Remove lock list in case of error. */
3659 if (FAILED(rc))
3660 {
3661 mData->mSession.mLockedMedia.Unlock();
3662 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3663 mData->mSession.mLockedMedia.Lock();
3664 }
3665 }
3666 }
3667
3668 return S_OK;
3669 }
3670
3671 /* bus/channel/device differ; we need a new attachment object,
3672 * but don't try to associate it again */
3673 associate = false;
3674 break;
3675 }
3676 }
3677
3678 /* go further only if the attachment is to be indirect */
3679 if (!fIndirect)
3680 break;
3681
3682 /* perform the so called smart attachment logic for indirect
3683 * attachments. Note that smart attachment is only applicable to base
3684 * hard disks. */
3685
3686 if (medium->i_getParent().isNull())
3687 {
3688 /* first, investigate the backup copy of the current hard disk
3689 * attachments to make it possible to re-attach existing diffs to
3690 * another device slot w/o losing their contents */
3691 if (mMediumAttachments.isBackedUp())
3692 {
3693 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3694
3695 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3696 uint32_t foundLevel = 0;
3697
3698 for (MediumAttachmentList::const_iterator
3699 it = oldAtts.begin();
3700 it != oldAtts.end();
3701 ++it)
3702 {
3703 uint32_t level = 0;
3704 MediumAttachment *pAttach = *it;
3705 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3706 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3707 if (pMedium.isNull())
3708 continue;
3709
3710 if (pMedium->i_getBase(&level) == medium)
3711 {
3712 /* skip the hard disk if its currently attached (we
3713 * cannot attach the same hard disk twice) */
3714 if (i_findAttachment(*mMediumAttachments.data(),
3715 pMedium))
3716 continue;
3717
3718 /* matched device, channel and bus (i.e. attached to the
3719 * same place) will win and immediately stop the search;
3720 * otherwise the attachment that has the youngest
3721 * descendant of medium will be used
3722 */
3723 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3724 {
3725 /* the simplest case: restore the whole attachment
3726 * and return, nothing else to do */
3727 mMediumAttachments->push_back(*it);
3728
3729 /* Reattach the medium to the VM. */
3730 if (fHotplug || fSilent)
3731 {
3732 mediumLock.release();
3733 treeLock.release();
3734 alock.release();
3735
3736 MediumLockList *pMediumLockList(new MediumLockList());
3737
3738 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3739 medium /* pToLockWrite */,
3740 false /* fMediumLockWriteAll */,
3741 NULL,
3742 *pMediumLockList);
3743 alock.acquire();
3744 if (FAILED(rc))
3745 delete pMediumLockList;
3746 else
3747 {
3748 mData->mSession.mLockedMedia.Unlock();
3749 alock.release();
3750 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3751 mData->mSession.mLockedMedia.Lock();
3752 alock.acquire();
3753 }
3754 alock.release();
3755
3756 if (SUCCEEDED(rc))
3757 {
3758 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3759 /* Remove lock list in case of error. */
3760 if (FAILED(rc))
3761 {
3762 mData->mSession.mLockedMedia.Unlock();
3763 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3764 mData->mSession.mLockedMedia.Lock();
3765 }
3766 }
3767 }
3768
3769 return S_OK;
3770 }
3771 else if ( foundIt == oldAtts.end()
3772 || level > foundLevel /* prefer younger */
3773 )
3774 {
3775 foundIt = it;
3776 foundLevel = level;
3777 }
3778 }
3779 }
3780
3781 if (foundIt != oldAtts.end())
3782 {
3783 /* use the previously attached hard disk */
3784 medium = (*foundIt)->i_getMedium();
3785 mediumCaller.attach(medium);
3786 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3787 mediumLock.attach(medium);
3788 /* not implicit, doesn't require association with this VM */
3789 fIndirect = false;
3790 associate = false;
3791 /* go right to the MediumAttachment creation */
3792 break;
3793 }
3794 }
3795
3796 /* must give up the medium lock and medium tree lock as below we
3797 * go over snapshots, which needs a lock with higher lock order. */
3798 mediumLock.release();
3799 treeLock.release();
3800
3801 /* then, search through snapshots for the best diff in the given
3802 * hard disk's chain to base the new diff on */
3803
3804 ComObjPtr<Medium> base;
3805 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3806 while (snap)
3807 {
3808 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3809
3810 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3811
3812 MediumAttachment *pAttachFound = NULL;
3813 uint32_t foundLevel = 0;
3814
3815 for (MediumAttachmentList::const_iterator
3816 it = snapAtts.begin();
3817 it != snapAtts.end();
3818 ++it)
3819 {
3820 MediumAttachment *pAttach = *it;
3821 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3822 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3823 if (pMedium.isNull())
3824 continue;
3825
3826 uint32_t level = 0;
3827 if (pMedium->i_getBase(&level) == medium)
3828 {
3829 /* matched device, channel and bus (i.e. attached to the
3830 * same place) will win and immediately stop the search;
3831 * otherwise the attachment that has the youngest
3832 * descendant of medium will be used
3833 */
3834 if ( pAttach->i_getDevice() == aDevice
3835 && pAttach->i_getPort() == aControllerPort
3836 && pAttach->i_getControllerName() == aName
3837 )
3838 {
3839 pAttachFound = pAttach;
3840 break;
3841 }
3842 else if ( !pAttachFound
3843 || level > foundLevel /* prefer younger */
3844 )
3845 {
3846 pAttachFound = pAttach;
3847 foundLevel = level;
3848 }
3849 }
3850 }
3851
3852 if (pAttachFound)
3853 {
3854 base = pAttachFound->i_getMedium();
3855 break;
3856 }
3857
3858 snap = snap->i_getParent();
3859 }
3860
3861 /* re-lock medium tree and the medium, as we need it below */
3862 treeLock.acquire();
3863 mediumLock.acquire();
3864
3865 /* found a suitable diff, use it as a base */
3866 if (!base.isNull())
3867 {
3868 medium = base;
3869 mediumCaller.attach(medium);
3870 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3871 mediumLock.attach(medium);
3872 }
3873 }
3874
3875 Utf8Str strFullSnapshotFolder;
3876 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3877
3878 ComObjPtr<Medium> diff;
3879 diff.createObject();
3880 // store this diff in the same registry as the parent
3881 Guid uuidRegistryParent;
3882 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3883 {
3884 // parent image has no registry: this can happen if we're attaching a new immutable
3885 // image that has not yet been attached (medium then points to the base and we're
3886 // creating the diff image for the immutable, and the parent is not yet registered);
3887 // put the parent in the machine registry then
3888 mediumLock.release();
3889 treeLock.release();
3890 alock.release();
3891 i_addMediumToRegistry(medium);
3892 alock.acquire();
3893 treeLock.acquire();
3894 mediumLock.acquire();
3895 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3896 }
3897 rc = diff->init(mParent,
3898 medium->i_getPreferredDiffFormat(),
3899 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3900 uuidRegistryParent,
3901 DeviceType_HardDisk);
3902 if (FAILED(rc)) return rc;
3903
3904 /* Apply the normal locking logic to the entire chain. */
3905 MediumLockList *pMediumLockList(new MediumLockList());
3906 mediumLock.release();
3907 treeLock.release();
3908 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3909 diff /* pToLockWrite */,
3910 false /* fMediumLockWriteAll */,
3911 medium,
3912 *pMediumLockList);
3913 treeLock.acquire();
3914 mediumLock.acquire();
3915 if (SUCCEEDED(rc))
3916 {
3917 mediumLock.release();
3918 treeLock.release();
3919 rc = pMediumLockList->Lock();
3920 treeLock.acquire();
3921 mediumLock.acquire();
3922 if (FAILED(rc))
3923 setError(rc,
3924 tr("Could not lock medium when creating diff '%s'"),
3925 diff->i_getLocationFull().c_str());
3926 else
3927 {
3928 /* will release the lock before the potentially lengthy
3929 * operation, so protect with the special state */
3930 MachineState_T oldState = mData->mMachineState;
3931 i_setMachineState(MachineState_SettingUp);
3932
3933 mediumLock.release();
3934 treeLock.release();
3935 alock.release();
3936
3937 rc = medium->i_createDiffStorage(diff,
3938 medium->i_getPreferredDiffVariant(),
3939 pMediumLockList,
3940 NULL /* aProgress */,
3941 true /* aWait */,
3942 false /* aNotify */);
3943
3944 alock.acquire();
3945 treeLock.acquire();
3946 mediumLock.acquire();
3947
3948 i_setMachineState(oldState);
3949 }
3950 }
3951
3952 /* Unlock the media and free the associated memory. */
3953 delete pMediumLockList;
3954
3955 if (FAILED(rc)) return rc;
3956
3957 /* use the created diff for the actual attachment */
3958 medium = diff;
3959 mediumCaller.attach(medium);
3960 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3961 mediumLock.attach(medium);
3962 }
3963 while (0);
3964
3965 ComObjPtr<MediumAttachment> attachment;
3966 attachment.createObject();
3967 rc = attachment->init(this,
3968 medium,
3969 aName,
3970 aControllerPort,
3971 aDevice,
3972 aType,
3973 fIndirect,
3974 false /* fPassthrough */,
3975 false /* fTempEject */,
3976 false /* fNonRotational */,
3977 false /* fDiscard */,
3978 fHotplug /* fHotPluggable */,
3979 Utf8Str::Empty);
3980 if (FAILED(rc)) return rc;
3981
3982 if (associate && !medium.isNull())
3983 {
3984 // as the last step, associate the medium to the VM
3985 rc = medium->i_addBackReference(mData->mUuid);
3986 // here we can fail because of Deleting, or being in process of creating a Diff
3987 if (FAILED(rc)) return rc;
3988
3989 mediumLock.release();
3990 treeLock.release();
3991 alock.release();
3992 i_addMediumToRegistry(medium);
3993 alock.acquire();
3994 treeLock.acquire();
3995 mediumLock.acquire();
3996 }
3997
3998 /* success: finally remember the attachment */
3999 i_setModified(IsModified_Storage);
4000 mMediumAttachments.backup();
4001 mMediumAttachments->push_back(attachment);
4002
4003 mediumLock.release();
4004 treeLock.release();
4005 alock.release();
4006
4007 if (fHotplug || fSilent)
4008 {
4009 if (!medium.isNull())
4010 {
4011 MediumLockList *pMediumLockList(new MediumLockList());
4012
4013 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4014 medium /* pToLockWrite */,
4015 false /* fMediumLockWriteAll */,
4016 NULL,
4017 *pMediumLockList);
4018 alock.acquire();
4019 if (FAILED(rc))
4020 delete pMediumLockList;
4021 else
4022 {
4023 mData->mSession.mLockedMedia.Unlock();
4024 alock.release();
4025 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4026 mData->mSession.mLockedMedia.Lock();
4027 alock.acquire();
4028 }
4029 alock.release();
4030 }
4031
4032 if (SUCCEEDED(rc))
4033 {
4034 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4035 /* Remove lock list in case of error. */
4036 if (FAILED(rc))
4037 {
4038 mData->mSession.mLockedMedia.Unlock();
4039 mData->mSession.mLockedMedia.Remove(attachment);
4040 mData->mSession.mLockedMedia.Lock();
4041 }
4042 }
4043 }
4044
4045 /* Save modified registries, but skip this machine as it's the caller's
4046 * job to save its settings like all other settings changes. */
4047 mParent->i_unmarkRegistryModified(i_getId());
4048 mParent->i_saveModifiedRegistries();
4049
4050 if (SUCCEEDED(rc))
4051 {
4052 if (fIndirect && medium != aM)
4053 mParent->i_onMediumConfigChanged(medium);
4054 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4055 }
4056
4057 return rc;
4058}
4059
4060HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4061 LONG aDevice)
4062{
4063 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4064 aName.c_str(), aControllerPort, aDevice));
4065
4066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4067
4068 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4069 if (FAILED(rc)) return rc;
4070
4071 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4072
4073 /* Check for an existing controller. */
4074 ComObjPtr<StorageController> ctl;
4075 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4076 if (FAILED(rc)) return rc;
4077
4078 StorageControllerType_T ctrlType;
4079 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4080 if (FAILED(rc))
4081 return setError(E_FAIL,
4082 tr("Could not get type of controller '%s'"),
4083 aName.c_str());
4084
4085 bool fSilent = false;
4086 Utf8Str strReconfig;
4087
4088 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4089 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4090 if ( mData->mMachineState == MachineState_Paused
4091 && strReconfig == "1")
4092 fSilent = true;
4093
4094 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4095 bool fHotplug = false;
4096 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4097 fHotplug = true;
4098
4099 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4100 return setError(VBOX_E_INVALID_VM_STATE,
4101 tr("Controller '%s' does not support hotplugging"),
4102 aName.c_str());
4103
4104 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4105 aName,
4106 aControllerPort,
4107 aDevice);
4108 if (!pAttach)
4109 return setError(VBOX_E_OBJECT_NOT_FOUND,
4110 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4111 aDevice, aControllerPort, aName.c_str());
4112
4113 if (fHotplug && !pAttach->i_getHotPluggable())
4114 return setError(VBOX_E_NOT_SUPPORTED,
4115 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4116 aDevice, aControllerPort, aName.c_str());
4117
4118 /*
4119 * The VM has to detach the device before we delete any implicit diffs.
4120 * If this fails we can roll back without loosing data.
4121 */
4122 if (fHotplug || fSilent)
4123 {
4124 alock.release();
4125 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4126 alock.acquire();
4127 }
4128 if (FAILED(rc)) return rc;
4129
4130 /* If we are here everything went well and we can delete the implicit now. */
4131 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4132
4133 alock.release();
4134
4135 /* Save modified registries, but skip this machine as it's the caller's
4136 * job to save its settings like all other settings changes. */
4137 mParent->i_unmarkRegistryModified(i_getId());
4138 mParent->i_saveModifiedRegistries();
4139
4140 if (SUCCEEDED(rc))
4141 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4142
4143 return rc;
4144}
4145
4146HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4147 LONG aDevice, BOOL aPassthrough)
4148{
4149 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4150 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4151
4152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4153
4154 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4155 if (FAILED(rc)) return rc;
4156
4157 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4158
4159 /* Check for an existing controller. */
4160 ComObjPtr<StorageController> ctl;
4161 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4162 if (FAILED(rc)) return rc;
4163
4164 StorageControllerType_T ctrlType;
4165 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4166 if (FAILED(rc))
4167 return setError(E_FAIL,
4168 tr("Could not get type of controller '%s'"),
4169 aName.c_str());
4170
4171 bool fSilent = false;
4172 Utf8Str strReconfig;
4173
4174 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4175 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4176 if ( mData->mMachineState == MachineState_Paused
4177 && strReconfig == "1")
4178 fSilent = true;
4179
4180 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4181 bool fHotplug = false;
4182 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4183 fHotplug = true;
4184
4185 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4186 return setError(VBOX_E_INVALID_VM_STATE,
4187 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4188 aName.c_str());
4189
4190 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4191 aName,
4192 aControllerPort,
4193 aDevice);
4194 if (!pAttach)
4195 return setError(VBOX_E_OBJECT_NOT_FOUND,
4196 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4197 aDevice, aControllerPort, aName.c_str());
4198
4199
4200 i_setModified(IsModified_Storage);
4201 mMediumAttachments.backup();
4202
4203 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4204
4205 if (pAttach->i_getType() != DeviceType_DVD)
4206 return setError(E_INVALIDARG,
4207 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4208 aDevice, aControllerPort, aName.c_str());
4209
4210 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4211
4212 pAttach->i_updatePassthrough(!!aPassthrough);
4213
4214 attLock.release();
4215 alock.release();
4216 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4217 if (SUCCEEDED(rc) && fValueChanged)
4218 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4219
4220 return rc;
4221}
4222
4223HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4224 LONG aDevice, BOOL aTemporaryEject)
4225{
4226
4227 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4228 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4229
4230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4231
4232 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4233 if (FAILED(rc)) return rc;
4234
4235 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4236 aName,
4237 aControllerPort,
4238 aDevice);
4239 if (!pAttach)
4240 return setError(VBOX_E_OBJECT_NOT_FOUND,
4241 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4242 aDevice, aControllerPort, aName.c_str());
4243
4244
4245 i_setModified(IsModified_Storage);
4246 mMediumAttachments.backup();
4247
4248 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4249
4250 if (pAttach->i_getType() != DeviceType_DVD)
4251 return setError(E_INVALIDARG,
4252 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4253 aDevice, aControllerPort, aName.c_str());
4254 pAttach->i_updateTempEject(!!aTemporaryEject);
4255
4256 return S_OK;
4257}
4258
4259HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4260 LONG aDevice, BOOL aNonRotational)
4261{
4262
4263 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4264 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4265
4266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4267
4268 HRESULT rc = i_checkStateDependency(MutableStateDep);
4269 if (FAILED(rc)) return rc;
4270
4271 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4272
4273 if (Global::IsOnlineOrTransient(mData->mMachineState))
4274 return setError(VBOX_E_INVALID_VM_STATE,
4275 tr("Invalid machine state: %s"),
4276 Global::stringifyMachineState(mData->mMachineState));
4277
4278 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4279 aName,
4280 aControllerPort,
4281 aDevice);
4282 if (!pAttach)
4283 return setError(VBOX_E_OBJECT_NOT_FOUND,
4284 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4285 aDevice, aControllerPort, aName.c_str());
4286
4287
4288 i_setModified(IsModified_Storage);
4289 mMediumAttachments.backup();
4290
4291 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4292
4293 if (pAttach->i_getType() != DeviceType_HardDisk)
4294 return setError(E_INVALIDARG,
4295 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"),
4296 aDevice, aControllerPort, aName.c_str());
4297 pAttach->i_updateNonRotational(!!aNonRotational);
4298
4299 return S_OK;
4300}
4301
4302HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4303 LONG aDevice, BOOL aDiscard)
4304{
4305
4306 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4307 aName.c_str(), aControllerPort, aDevice, aDiscard));
4308
4309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4310
4311 HRESULT rc = i_checkStateDependency(MutableStateDep);
4312 if (FAILED(rc)) return rc;
4313
4314 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4315
4316 if (Global::IsOnlineOrTransient(mData->mMachineState))
4317 return setError(VBOX_E_INVALID_VM_STATE,
4318 tr("Invalid machine state: %s"),
4319 Global::stringifyMachineState(mData->mMachineState));
4320
4321 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4322 aName,
4323 aControllerPort,
4324 aDevice);
4325 if (!pAttach)
4326 return setError(VBOX_E_OBJECT_NOT_FOUND,
4327 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4328 aDevice, aControllerPort, aName.c_str());
4329
4330
4331 i_setModified(IsModified_Storage);
4332 mMediumAttachments.backup();
4333
4334 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4335
4336 if (pAttach->i_getType() != DeviceType_HardDisk)
4337 return setError(E_INVALIDARG,
4338 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"),
4339 aDevice, aControllerPort, aName.c_str());
4340 pAttach->i_updateDiscard(!!aDiscard);
4341
4342 return S_OK;
4343}
4344
4345HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4346 LONG aDevice, BOOL aHotPluggable)
4347{
4348 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4349 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4350
4351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4352
4353 HRESULT rc = i_checkStateDependency(MutableStateDep);
4354 if (FAILED(rc)) return rc;
4355
4356 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4357
4358 if (Global::IsOnlineOrTransient(mData->mMachineState))
4359 return setError(VBOX_E_INVALID_VM_STATE,
4360 tr("Invalid machine state: %s"),
4361 Global::stringifyMachineState(mData->mMachineState));
4362
4363 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4364 aName,
4365 aControllerPort,
4366 aDevice);
4367 if (!pAttach)
4368 return setError(VBOX_E_OBJECT_NOT_FOUND,
4369 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4370 aDevice, aControllerPort, aName.c_str());
4371
4372 /* Check for an existing controller. */
4373 ComObjPtr<StorageController> ctl;
4374 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4375 if (FAILED(rc)) return rc;
4376
4377 StorageControllerType_T ctrlType;
4378 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4379 if (FAILED(rc))
4380 return setError(E_FAIL,
4381 tr("Could not get type of controller '%s'"),
4382 aName.c_str());
4383
4384 if (!i_isControllerHotplugCapable(ctrlType))
4385 return setError(VBOX_E_NOT_SUPPORTED,
4386 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4387 aName.c_str());
4388
4389 i_setModified(IsModified_Storage);
4390 mMediumAttachments.backup();
4391
4392 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4393
4394 if (pAttach->i_getType() == DeviceType_Floppy)
4395 return setError(E_INVALIDARG,
4396 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"),
4397 aDevice, aControllerPort, aName.c_str());
4398 pAttach->i_updateHotPluggable(!!aHotPluggable);
4399
4400 return S_OK;
4401}
4402
4403HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4404 LONG aDevice)
4405{
4406 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4407 aName.c_str(), aControllerPort, aDevice));
4408
4409 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4410}
4411
4412HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4413 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4414{
4415 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4416 aName.c_str(), aControllerPort, aDevice));
4417
4418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4419
4420 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4421 if (FAILED(rc)) return rc;
4422
4423 if (Global::IsOnlineOrTransient(mData->mMachineState))
4424 return setError(VBOX_E_INVALID_VM_STATE,
4425 tr("Invalid machine state: %s"),
4426 Global::stringifyMachineState(mData->mMachineState));
4427
4428 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4429 aName,
4430 aControllerPort,
4431 aDevice);
4432 if (!pAttach)
4433 return setError(VBOX_E_OBJECT_NOT_FOUND,
4434 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4435 aDevice, aControllerPort, aName.c_str());
4436
4437
4438 i_setModified(IsModified_Storage);
4439 mMediumAttachments.backup();
4440
4441 IBandwidthGroup *iB = aBandwidthGroup;
4442 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4443 if (aBandwidthGroup && group.isNull())
4444 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4445
4446 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4447
4448 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4449 if (strBandwidthGroupOld.isNotEmpty())
4450 {
4451 /* Get the bandwidth group object and release it - this must not fail. */
4452 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4453 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4454 Assert(SUCCEEDED(rc));
4455
4456 pBandwidthGroupOld->i_release();
4457 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4458 }
4459
4460 if (!group.isNull())
4461 {
4462 group->i_reference();
4463 pAttach->i_updateBandwidthGroup(group->i_getName());
4464 }
4465
4466 return S_OK;
4467}
4468
4469HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4470 LONG aControllerPort,
4471 LONG aDevice,
4472 DeviceType_T aType)
4473{
4474 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4475 aName.c_str(), aControllerPort, aDevice, aType));
4476
4477 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4478}
4479
4480
4481HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 BOOL aForce)
4485{
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4487 aName.c_str(), aControllerPort, aForce));
4488
4489 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4490}
4491
4492HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4493 LONG aControllerPort,
4494 LONG aDevice,
4495 const ComPtr<IMedium> &aMedium,
4496 BOOL aForce)
4497{
4498 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4499 aName.c_str(), aControllerPort, aDevice, aForce));
4500
4501 // request the host lock first, since might be calling Host methods for getting host drives;
4502 // next, protect the media tree all the while we're in here, as well as our member variables
4503 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4504 this->lockHandle(),
4505 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4506
4507 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4508 aName,
4509 aControllerPort,
4510 aDevice);
4511 if (pAttach.isNull())
4512 return setError(VBOX_E_OBJECT_NOT_FOUND,
4513 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4514 aDevice, aControllerPort, aName.c_str());
4515
4516 /* Remember previously mounted medium. The medium before taking the
4517 * backup is not necessarily the same thing. */
4518 ComObjPtr<Medium> oldmedium;
4519 oldmedium = pAttach->i_getMedium();
4520
4521 IMedium *iM = aMedium;
4522 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4523 if (aMedium && pMedium.isNull())
4524 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4525
4526 AutoCaller mediumCaller(pMedium);
4527 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4528
4529 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4530 if (pMedium)
4531 {
4532 DeviceType_T mediumType = pAttach->i_getType();
4533 switch (mediumType)
4534 {
4535 case DeviceType_DVD:
4536 case DeviceType_Floppy:
4537 break;
4538
4539 default:
4540 return setError(VBOX_E_INVALID_OBJECT_STATE,
4541 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4542 aControllerPort,
4543 aDevice,
4544 aName.c_str());
4545 }
4546 }
4547
4548 i_setModified(IsModified_Storage);
4549 mMediumAttachments.backup();
4550
4551 {
4552 // The backup operation makes the pAttach reference point to the
4553 // old settings. Re-get the correct reference.
4554 pAttach = i_findAttachment(*mMediumAttachments.data(),
4555 aName,
4556 aControllerPort,
4557 aDevice);
4558 if (!oldmedium.isNull())
4559 oldmedium->i_removeBackReference(mData->mUuid);
4560 if (!pMedium.isNull())
4561 {
4562 pMedium->i_addBackReference(mData->mUuid);
4563
4564 mediumLock.release();
4565 multiLock.release();
4566 i_addMediumToRegistry(pMedium);
4567 multiLock.acquire();
4568 mediumLock.acquire();
4569 }
4570
4571 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4572 pAttach->i_updateMedium(pMedium);
4573 }
4574
4575 i_setModified(IsModified_Storage);
4576
4577 mediumLock.release();
4578 multiLock.release();
4579 HRESULT rc = i_onMediumChange(pAttach, aForce);
4580 multiLock.acquire();
4581 mediumLock.acquire();
4582
4583 /* On error roll back this change only. */
4584 if (FAILED(rc))
4585 {
4586 if (!pMedium.isNull())
4587 pMedium->i_removeBackReference(mData->mUuid);
4588 pAttach = i_findAttachment(*mMediumAttachments.data(),
4589 aName,
4590 aControllerPort,
4591 aDevice);
4592 /* If the attachment is gone in the meantime, bail out. */
4593 if (pAttach.isNull())
4594 return rc;
4595 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4596 if (!oldmedium.isNull())
4597 oldmedium->i_addBackReference(mData->mUuid);
4598 pAttach->i_updateMedium(oldmedium);
4599 }
4600
4601 mediumLock.release();
4602 multiLock.release();
4603
4604 /* Save modified registries, but skip this machine as it's the caller's
4605 * job to save its settings like all other settings changes. */
4606 mParent->i_unmarkRegistryModified(i_getId());
4607 mParent->i_saveModifiedRegistries();
4608
4609 return rc;
4610}
4611HRESULT Machine::getMedium(const com::Utf8Str &aName,
4612 LONG aControllerPort,
4613 LONG aDevice,
4614 ComPtr<IMedium> &aMedium)
4615{
4616 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4617 aName.c_str(), aControllerPort, aDevice));
4618
4619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4620
4621 aMedium = NULL;
4622
4623 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4624 aName,
4625 aControllerPort,
4626 aDevice);
4627 if (pAttach.isNull())
4628 return setError(VBOX_E_OBJECT_NOT_FOUND,
4629 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4630 aDevice, aControllerPort, aName.c_str());
4631
4632 aMedium = pAttach->i_getMedium();
4633
4634 return S_OK;
4635}
4636
4637HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4638{
4639 if (aSlot < RT_ELEMENTS(mSerialPorts))
4640 {
4641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4642 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4643 return S_OK;
4644 }
4645 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4646}
4647
4648HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4649{
4650 if (aSlot < RT_ELEMENTS(mParallelPorts))
4651 {
4652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4653 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4654 return S_OK;
4655 }
4656 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4657}
4658
4659
4660HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4661{
4662 /* Do not assert if slot is out of range, just return the advertised
4663 status. testdriver/vbox.py triggers this in logVmInfo. */
4664 if (aSlot >= mNetworkAdapters.size())
4665 return setError(E_INVALIDARG,
4666 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4667 aSlot, mNetworkAdapters.size());
4668
4669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4670
4671 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4672
4673 return S_OK;
4674}
4675
4676HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4677{
4678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4679
4680 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4681 size_t i = 0;
4682 for (settings::StringsMap::const_iterator
4683 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4684 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4685 ++it, ++i)
4686 aKeys[i] = it->first;
4687
4688 return S_OK;
4689}
4690
4691 /**
4692 * @note Locks this object for reading.
4693 */
4694HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4695 com::Utf8Str &aValue)
4696{
4697 /* start with nothing found */
4698 aValue = "";
4699
4700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4701
4702 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4703 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4704 // found:
4705 aValue = it->second; // source is a Utf8Str
4706
4707 /* return the result to caller (may be empty) */
4708 return S_OK;
4709}
4710
4711 /**
4712 * @note Locks mParent for writing + this object for writing.
4713 */
4714HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4715{
4716 /* Because control characters in aKey have caused problems in the settings
4717 * they are rejected unless the key should be deleted. */
4718 if (!aValue.isEmpty())
4719 {
4720 for (size_t i = 0; i < aKey.length(); ++i)
4721 {
4722 char ch = aKey[i];
4723 if (RTLocCIsCntrl(ch))
4724 return E_INVALIDARG;
4725 }
4726 }
4727
4728 Utf8Str strOldValue; // empty
4729
4730 // locking note: we only hold the read lock briefly to look up the old value,
4731 // then release it and call the onExtraCanChange callbacks. There is a small
4732 // chance of a race insofar as the callback might be called twice if two callers
4733 // change the same key at the same time, but that's a much better solution
4734 // than the deadlock we had here before. The actual changing of the extradata
4735 // is then performed under the write lock and race-free.
4736
4737 // look up the old value first; if nothing has changed then we need not do anything
4738 {
4739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4740
4741 // For snapshots don't even think about allowing changes, extradata
4742 // is global for a machine, so there is nothing snapshot specific.
4743 if (i_isSnapshotMachine())
4744 return setError(VBOX_E_INVALID_VM_STATE,
4745 tr("Cannot set extradata for a snapshot"));
4746
4747 // check if the right IMachine instance is used
4748 if (mData->mRegistered && !i_isSessionMachine())
4749 return setError(VBOX_E_INVALID_VM_STATE,
4750 tr("Cannot set extradata for an immutable machine"));
4751
4752 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4753 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4754 strOldValue = it->second;
4755 }
4756
4757 bool fChanged;
4758 if ((fChanged = (strOldValue != aValue)))
4759 {
4760 // ask for permission from all listeners outside the locks;
4761 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4762 // lock to copy the list of callbacks to invoke
4763 Bstr bstrError;
4764 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4765 {
4766 const char *sep = bstrError.isEmpty() ? "" : ": ";
4767 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4768 return setError(E_ACCESSDENIED,
4769 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4770 aKey.c_str(),
4771 aValue.c_str(),
4772 sep,
4773 bstrError.raw());
4774 }
4775
4776 // data is changing and change not vetoed: then write it out under the lock
4777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4778
4779 if (aValue.isEmpty())
4780 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4781 else
4782 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4783 // creates a new key if needed
4784
4785 bool fNeedsGlobalSaveSettings = false;
4786 // This saving of settings is tricky: there is no "old state" for the
4787 // extradata items at all (unlike all other settings), so the old/new
4788 // settings comparison would give a wrong result!
4789 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4790
4791 if (fNeedsGlobalSaveSettings)
4792 {
4793 // save the global settings; for that we should hold only the VirtualBox lock
4794 alock.release();
4795 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4796 mParent->i_saveSettings();
4797 }
4798 }
4799
4800 // fire notification outside the lock
4801 if (fChanged)
4802 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4803
4804 return S_OK;
4805}
4806
4807HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4808{
4809 aProgress = NULL;
4810 NOREF(aSettingsFilePath);
4811 ReturnComNotImplemented();
4812}
4813
4814HRESULT Machine::saveSettings()
4815{
4816 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4817
4818 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4819 if (FAILED(rc)) return rc;
4820
4821 /* the settings file path may never be null */
4822 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4823
4824 /* save all VM data excluding snapshots */
4825 bool fNeedsGlobalSaveSettings = false;
4826 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4827 mlock.release();
4828
4829 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4830 {
4831 // save the global settings; for that we should hold only the VirtualBox lock
4832 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4833 rc = mParent->i_saveSettings();
4834 }
4835
4836 return rc;
4837}
4838
4839
4840HRESULT Machine::discardSettings()
4841{
4842 /*
4843 * We need to take the machine list lock here as well as the machine one
4844 * or we'll get into trouble should any media stuff require rolling back.
4845 *
4846 * Details:
4847 *
4848 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4849 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4850 * 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]
4851 * 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
4852 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4853 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4854 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4855 * 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
4856 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4857 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4858 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4859 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4861 * 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]
4862 * 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] (*)
4863 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4864 * 0:005> k
4865 * # Child-SP RetAddr Call Site
4866 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4867 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4868 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4869 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4870 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4871 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4872 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4873 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4874 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4875 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4876 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4877 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4878 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4879 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4880 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4881 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4882 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4883 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4884 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4885 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4886 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4887 *
4888 */
4889 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4891
4892 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4893 if (FAILED(rc)) return rc;
4894
4895 /*
4896 * during this rollback, the session will be notified if data has
4897 * been actually changed
4898 */
4899 i_rollback(true /* aNotify */);
4900
4901 return S_OK;
4902}
4903
4904/** @note Locks objects! */
4905HRESULT Machine::unregister(AutoCaller &autoCaller,
4906 CleanupMode_T aCleanupMode,
4907 std::vector<ComPtr<IMedium> > &aMedia)
4908{
4909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4910
4911 Guid id(i_getId());
4912
4913 if (mData->mSession.mState != SessionState_Unlocked)
4914 return setError(VBOX_E_INVALID_OBJECT_STATE,
4915 tr("Cannot unregister the machine '%s' while it is locked"),
4916 mUserData->s.strName.c_str());
4917
4918 // wait for state dependents to drop to zero
4919 i_ensureNoStateDependencies();
4920
4921 if (!mData->mAccessible)
4922 {
4923 // inaccessible machines can only be unregistered; uninitialize ourselves
4924 // here because currently there may be no unregistered that are inaccessible
4925 // (this state combination is not supported). Note releasing the caller and
4926 // leaving the lock before calling uninit()
4927 alock.release();
4928 autoCaller.release();
4929
4930 uninit();
4931
4932 mParent->i_unregisterMachine(this, id);
4933 // calls VirtualBox::i_saveSettings()
4934
4935 return S_OK;
4936 }
4937
4938 HRESULT rc = S_OK;
4939 mData->llFilesToDelete.clear();
4940
4941 if (!mSSData->strStateFilePath.isEmpty())
4942 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4943
4944 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4945 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4946 mData->llFilesToDelete.push_back(strNVRAMFile);
4947
4948 // This list collects the medium objects from all medium attachments
4949 // which we will detach from the machine and its snapshots, in a specific
4950 // order which allows for closing all media without getting "media in use"
4951 // errors, simply by going through the list from the front to the back:
4952 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4953 // and must be closed before the parent media from the snapshots, or closing the parents
4954 // will fail because they still have children);
4955 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4956 // the root ("first") snapshot of the machine.
4957 MediaList llMedia;
4958
4959 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4960 && mMediumAttachments->size()
4961 )
4962 {
4963 // we have media attachments: detach them all and add the Medium objects to our list
4964 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4965 }
4966
4967 if (mData->mFirstSnapshot)
4968 {
4969 // add the media from the medium attachments of the snapshots to llMedia
4970 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4971 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4972 // into the children first
4973
4974 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4975 MachineState_T oldState = mData->mMachineState;
4976 mData->mMachineState = MachineState_DeletingSnapshot;
4977
4978 // make a copy of the first snapshot reference so the refcount does not
4979 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4980 // (would hang due to the AutoCaller voodoo)
4981 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4982
4983 // GO!
4984 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4985
4986 mData->mMachineState = oldState;
4987 }
4988
4989 if (FAILED(rc))
4990 {
4991 i_rollbackMedia();
4992 return rc;
4993 }
4994
4995 // commit all the media changes made above
4996 i_commitMedia();
4997
4998 mData->mRegistered = false;
4999
5000 // machine lock no longer needed
5001 alock.release();
5002
5003 /* Make sure that the settings of the current VM are not saved, because
5004 * they are rather crippled at this point to meet the cleanup expectations
5005 * and there's no point destroying the VM config on disk just because. */
5006 mParent->i_unmarkRegistryModified(id);
5007
5008 // return media to caller
5009 aMedia.resize(llMedia.size());
5010 size_t i = 0;
5011 for (MediaList::const_iterator
5012 it = llMedia.begin();
5013 it != llMedia.end();
5014 ++it, ++i)
5015 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5016
5017 mParent->i_unregisterMachine(this, id);
5018 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5019
5020 return S_OK;
5021}
5022
5023/**
5024 * Task record for deleting a machine config.
5025 */
5026class Machine::DeleteConfigTask
5027 : public Machine::Task
5028{
5029public:
5030 DeleteConfigTask(Machine *m,
5031 Progress *p,
5032 const Utf8Str &t,
5033 const RTCList<ComPtr<IMedium> > &llMediums,
5034 const StringsList &llFilesToDelete)
5035 : Task(m, p, t),
5036 m_llMediums(llMediums),
5037 m_llFilesToDelete(llFilesToDelete)
5038 {}
5039
5040private:
5041 void handler()
5042 {
5043 try
5044 {
5045 m_pMachine->i_deleteConfigHandler(*this);
5046 }
5047 catch (...)
5048 {
5049 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5050 }
5051 }
5052
5053 RTCList<ComPtr<IMedium> > m_llMediums;
5054 StringsList m_llFilesToDelete;
5055
5056 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5057};
5058
5059/**
5060 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5061 * SessionMachine::taskHandler().
5062 *
5063 * @note Locks this object for writing.
5064 *
5065 * @param task
5066 * @return
5067 */
5068void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5069{
5070 LogFlowThisFuncEnter();
5071
5072 AutoCaller autoCaller(this);
5073 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5074 if (FAILED(autoCaller.rc()))
5075 {
5076 /* we might have been uninitialized because the session was accidentally
5077 * closed by the client, so don't assert */
5078 HRESULT rc = setError(E_FAIL,
5079 tr("The session has been accidentally closed"));
5080 task.m_pProgress->i_notifyComplete(rc);
5081 LogFlowThisFuncLeave();
5082 return;
5083 }
5084
5085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5086
5087 HRESULT rc = S_OK;
5088
5089 try
5090 {
5091 ULONG uLogHistoryCount = 3;
5092 ComPtr<ISystemProperties> systemProperties;
5093 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5094 if (FAILED(rc)) throw rc;
5095
5096 if (!systemProperties.isNull())
5097 {
5098 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5099 if (FAILED(rc)) throw rc;
5100 }
5101
5102 MachineState_T oldState = mData->mMachineState;
5103 i_setMachineState(MachineState_SettingUp);
5104 alock.release();
5105 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5106 {
5107 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5108 {
5109 AutoCaller mac(pMedium);
5110 if (FAILED(mac.rc())) throw mac.rc();
5111 Utf8Str strLocation = pMedium->i_getLocationFull();
5112 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5113 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5114 if (FAILED(rc)) throw rc;
5115 }
5116 if (pMedium->i_isMediumFormatFile())
5117 {
5118 ComPtr<IProgress> pProgress2;
5119 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5120 if (FAILED(rc)) throw rc;
5121 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5122 if (FAILED(rc)) throw rc;
5123 }
5124
5125 /* Close the medium, deliberately without checking the return
5126 * code, and without leaving any trace in the error info, as
5127 * a failure here is a very minor issue, which shouldn't happen
5128 * as above we even managed to delete the medium. */
5129 {
5130 ErrorInfoKeeper eik;
5131 pMedium->Close();
5132 }
5133 }
5134 i_setMachineState(oldState);
5135 alock.acquire();
5136
5137 // delete the files pushed on the task list by Machine::Delete()
5138 // (this includes saved states of the machine and snapshots and
5139 // medium storage files from the IMedium list passed in, and the
5140 // machine XML file)
5141 for (StringsList::const_iterator
5142 it = task.m_llFilesToDelete.begin();
5143 it != task.m_llFilesToDelete.end();
5144 ++it)
5145 {
5146 const Utf8Str &strFile = *it;
5147 LogFunc(("Deleting file %s\n", strFile.c_str()));
5148 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5149 if (FAILED(rc)) throw rc;
5150
5151 int vrc = RTFileDelete(strFile.c_str());
5152 if (RT_FAILURE(vrc))
5153 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5154 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5155 }
5156
5157 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5158 if (FAILED(rc)) throw rc;
5159
5160 /* delete the settings only when the file actually exists */
5161 if (mData->pMachineConfigFile->fileExists())
5162 {
5163 /* Delete any backup or uncommitted XML files. Ignore failures.
5164 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5165 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5166 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5167 RTFileDelete(otherXml.c_str());
5168 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5169 RTFileDelete(otherXml.c_str());
5170
5171 /* delete the Logs folder, nothing important should be left
5172 * there (we don't check for errors because the user might have
5173 * some private files there that we don't want to delete) */
5174 Utf8Str logFolder;
5175 getLogFolder(logFolder);
5176 Assert(logFolder.length());
5177 if (RTDirExists(logFolder.c_str()))
5178 {
5179 /* Delete all VBox.log[.N] files from the Logs folder
5180 * (this must be in sync with the rotation logic in
5181 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5182 * files that may have been created by the GUI. */
5183 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5184 RTFileDelete(log.c_str());
5185 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5186 RTFileDelete(log.c_str());
5187 for (ULONG i = uLogHistoryCount; i > 0; i--)
5188 {
5189 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5190 RTFileDelete(log.c_str());
5191 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5192 RTFileDelete(log.c_str());
5193 }
5194 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5195 RTFileDelete(log.c_str());
5196#if defined(RT_OS_WINDOWS)
5197 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5198 RTFileDelete(log.c_str());
5199 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5200 RTFileDelete(log.c_str());
5201#endif
5202
5203 RTDirRemove(logFolder.c_str());
5204 }
5205
5206 /* delete the Snapshots folder, nothing important should be left
5207 * there (we don't check for errors because the user might have
5208 * some private files there that we don't want to delete) */
5209 Utf8Str strFullSnapshotFolder;
5210 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5211 Assert(!strFullSnapshotFolder.isEmpty());
5212 if (RTDirExists(strFullSnapshotFolder.c_str()))
5213 RTDirRemove(strFullSnapshotFolder.c_str());
5214
5215 // delete the directory that contains the settings file, but only
5216 // if it matches the VM name
5217 Utf8Str settingsDir;
5218 if (i_isInOwnDir(&settingsDir))
5219 RTDirRemove(settingsDir.c_str());
5220 }
5221
5222 alock.release();
5223
5224 mParent->i_saveModifiedRegistries();
5225 }
5226 catch (HRESULT aRC) { rc = aRC; }
5227
5228 task.m_pProgress->i_notifyComplete(rc);
5229
5230 LogFlowThisFuncLeave();
5231}
5232
5233HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5234{
5235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5236
5237 HRESULT rc = i_checkStateDependency(MutableStateDep);
5238 if (FAILED(rc)) return rc;
5239
5240 if (mData->mRegistered)
5241 return setError(VBOX_E_INVALID_VM_STATE,
5242 tr("Cannot delete settings of a registered machine"));
5243
5244 // collect files to delete
5245 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5246 // machine config file
5247 if (mData->pMachineConfigFile->fileExists())
5248 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5249 // backup of machine config file
5250 Utf8Str strTmp(mData->m_strConfigFileFull);
5251 strTmp.append("-prev");
5252 if (RTFileExists(strTmp.c_str()))
5253 llFilesToDelete.push_back(strTmp);
5254
5255 RTCList<ComPtr<IMedium> > llMediums;
5256 for (size_t i = 0; i < aMedia.size(); ++i)
5257 {
5258 IMedium *pIMedium(aMedia[i]);
5259 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5260 if (pMedium.isNull())
5261 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5262 SafeArray<BSTR> ids;
5263 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5264 if (FAILED(rc)) return rc;
5265 /* At this point the medium should not have any back references
5266 * anymore. If it has it is attached to another VM and *must* not
5267 * deleted. */
5268 if (ids.size() < 1)
5269 llMediums.append(pMedium);
5270 }
5271
5272 ComObjPtr<Progress> pProgress;
5273 pProgress.createObject();
5274 rc = pProgress->init(i_getVirtualBox(),
5275 static_cast<IMachine*>(this) /* aInitiator */,
5276 tr("Deleting files"),
5277 true /* fCancellable */,
5278 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5279 tr("Collecting file inventory"));
5280 if (FAILED(rc))
5281 return rc;
5282
5283 /* create and start the task on a separate thread (note that it will not
5284 * start working until we release alock) */
5285 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5286 rc = pTask->createThread();
5287 pTask = NULL;
5288 if (FAILED(rc))
5289 return rc;
5290
5291 pProgress.queryInterfaceTo(aProgress.asOutParam());
5292
5293 LogFlowFuncLeave();
5294
5295 return S_OK;
5296}
5297
5298HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5299{
5300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5301
5302 ComObjPtr<Snapshot> pSnapshot;
5303 HRESULT rc;
5304
5305 if (aNameOrId.isEmpty())
5306 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5307 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5308 else
5309 {
5310 Guid uuid(aNameOrId);
5311 if (uuid.isValid())
5312 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5313 else
5314 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5315 }
5316 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5317
5318 return rc;
5319}
5320
5321HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5322 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5323{
5324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5325
5326 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5327 if (FAILED(rc)) return rc;
5328
5329 ComObjPtr<SharedFolder> sharedFolder;
5330 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5331 if (SUCCEEDED(rc))
5332 return setError(VBOX_E_OBJECT_IN_USE,
5333 tr("Shared folder named '%s' already exists"),
5334 aName.c_str());
5335
5336 sharedFolder.createObject();
5337 rc = sharedFolder->init(i_getMachine(),
5338 aName,
5339 aHostPath,
5340 !!aWritable,
5341 !!aAutomount,
5342 aAutoMountPoint,
5343 true /* fFailOnError */);
5344 if (FAILED(rc)) return rc;
5345
5346 i_setModified(IsModified_SharedFolders);
5347 mHWData.backup();
5348 mHWData->mSharedFolders.push_back(sharedFolder);
5349
5350 /* inform the direct session if any */
5351 alock.release();
5352 i_onSharedFolderChange();
5353
5354 return S_OK;
5355}
5356
5357HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5358{
5359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5360
5361 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5362 if (FAILED(rc)) return rc;
5363
5364 ComObjPtr<SharedFolder> sharedFolder;
5365 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5366 if (FAILED(rc)) return rc;
5367
5368 i_setModified(IsModified_SharedFolders);
5369 mHWData.backup();
5370 mHWData->mSharedFolders.remove(sharedFolder);
5371
5372 /* inform the direct session if any */
5373 alock.release();
5374 i_onSharedFolderChange();
5375
5376 return S_OK;
5377}
5378
5379HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5380{
5381 /* start with No */
5382 *aCanShow = FALSE;
5383
5384 ComPtr<IInternalSessionControl> directControl;
5385 {
5386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 if (mData->mSession.mState != SessionState_Locked)
5389 return setError(VBOX_E_INVALID_VM_STATE,
5390 tr("Machine is not locked for session (session state: %s)"),
5391 Global::stringifySessionState(mData->mSession.mState));
5392
5393 if (mData->mSession.mLockType == LockType_VM)
5394 directControl = mData->mSession.mDirectControl;
5395 }
5396
5397 /* ignore calls made after #OnSessionEnd() is called */
5398 if (!directControl)
5399 return S_OK;
5400
5401 LONG64 dummy;
5402 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5403}
5404
5405HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5406{
5407 ComPtr<IInternalSessionControl> directControl;
5408 {
5409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5410
5411 if (mData->mSession.mState != SessionState_Locked)
5412 return setError(E_FAIL,
5413 tr("Machine is not locked for session (session state: %s)"),
5414 Global::stringifySessionState(mData->mSession.mState));
5415
5416 if (mData->mSession.mLockType == LockType_VM)
5417 directControl = mData->mSession.mDirectControl;
5418 }
5419
5420 /* ignore calls made after #OnSessionEnd() is called */
5421 if (!directControl)
5422 return S_OK;
5423
5424 BOOL dummy;
5425 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5426}
5427
5428#ifdef VBOX_WITH_GUEST_PROPS
5429/**
5430 * Look up a guest property in VBoxSVC's internal structures.
5431 */
5432HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5433 com::Utf8Str &aValue,
5434 LONG64 *aTimestamp,
5435 com::Utf8Str &aFlags) const
5436{
5437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5438
5439 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5440 if (it != mHWData->mGuestProperties.end())
5441 {
5442 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5443 aValue = it->second.strValue;
5444 *aTimestamp = it->second.mTimestamp;
5445 GuestPropWriteFlags(it->second.mFlags, szFlags);
5446 aFlags = Utf8Str(szFlags);
5447 }
5448
5449 return S_OK;
5450}
5451
5452/**
5453 * Query the VM that a guest property belongs to for the property.
5454 * @returns E_ACCESSDENIED if the VM process is not available or not
5455 * currently handling queries and the lookup should then be done in
5456 * VBoxSVC.
5457 */
5458HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5459 com::Utf8Str &aValue,
5460 LONG64 *aTimestamp,
5461 com::Utf8Str &aFlags) const
5462{
5463 HRESULT rc = S_OK;
5464 Bstr bstrValue;
5465 Bstr bstrFlags;
5466
5467 ComPtr<IInternalSessionControl> directControl;
5468 {
5469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5470 if (mData->mSession.mLockType == LockType_VM)
5471 directControl = mData->mSession.mDirectControl;
5472 }
5473
5474 /* ignore calls made after #OnSessionEnd() is called */
5475 if (!directControl)
5476 rc = E_ACCESSDENIED;
5477 else
5478 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5479 0 /* accessMode */,
5480 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5481
5482 aValue = bstrValue;
5483 aFlags = bstrFlags;
5484
5485 return rc;
5486}
5487#endif // VBOX_WITH_GUEST_PROPS
5488
5489HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5490 com::Utf8Str &aValue,
5491 LONG64 *aTimestamp,
5492 com::Utf8Str &aFlags)
5493{
5494#ifndef VBOX_WITH_GUEST_PROPS
5495 ReturnComNotImplemented();
5496#else // VBOX_WITH_GUEST_PROPS
5497
5498 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5499
5500 if (rc == E_ACCESSDENIED)
5501 /* The VM is not running or the service is not (yet) accessible */
5502 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5503 return rc;
5504#endif // VBOX_WITH_GUEST_PROPS
5505}
5506
5507HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5508{
5509 LONG64 dummyTimestamp;
5510 com::Utf8Str dummyFlags;
5511 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5512 return rc;
5513
5514}
5515HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5516{
5517 com::Utf8Str dummyFlags;
5518 com::Utf8Str dummyValue;
5519 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5520 return rc;
5521}
5522
5523#ifdef VBOX_WITH_GUEST_PROPS
5524/**
5525 * Set a guest property in VBoxSVC's internal structures.
5526 */
5527HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5528 const com::Utf8Str &aFlags, bool fDelete)
5529{
5530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5531 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5532 if (FAILED(rc)) return rc;
5533
5534 try
5535 {
5536 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5537 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5538 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5539
5540 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5541 if (it == mHWData->mGuestProperties.end())
5542 {
5543 if (!fDelete)
5544 {
5545 i_setModified(IsModified_MachineData);
5546 mHWData.backupEx();
5547
5548 RTTIMESPEC time;
5549 HWData::GuestProperty prop;
5550 prop.strValue = Bstr(aValue).raw();
5551 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5552 prop.mFlags = fFlags;
5553 mHWData->mGuestProperties[aName] = prop;
5554 }
5555 }
5556 else
5557 {
5558 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5559 {
5560 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5561 }
5562 else
5563 {
5564 i_setModified(IsModified_MachineData);
5565 mHWData.backupEx();
5566
5567 /* The backupEx() operation invalidates our iterator,
5568 * so get a new one. */
5569 it = mHWData->mGuestProperties.find(aName);
5570 Assert(it != mHWData->mGuestProperties.end());
5571
5572 if (!fDelete)
5573 {
5574 RTTIMESPEC time;
5575 it->second.strValue = aValue;
5576 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5577 it->second.mFlags = fFlags;
5578 }
5579 else
5580 mHWData->mGuestProperties.erase(it);
5581 }
5582 }
5583
5584 if (SUCCEEDED(rc))
5585 {
5586 alock.release();
5587
5588 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5589 }
5590 }
5591 catch (std::bad_alloc &)
5592 {
5593 rc = E_OUTOFMEMORY;
5594 }
5595
5596 return rc;
5597}
5598
5599/**
5600 * Set a property on the VM that that property belongs to.
5601 * @returns E_ACCESSDENIED if the VM process is not available or not
5602 * currently handling queries and the setting should then be done in
5603 * VBoxSVC.
5604 */
5605HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5606 const com::Utf8Str &aFlags, bool fDelete)
5607{
5608 HRESULT rc;
5609
5610 try
5611 {
5612 ComPtr<IInternalSessionControl> directControl;
5613 {
5614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5615 if (mData->mSession.mLockType == LockType_VM)
5616 directControl = mData->mSession.mDirectControl;
5617 }
5618
5619 Bstr dummy1; /* will not be changed (setter) */
5620 Bstr dummy2; /* will not be changed (setter) */
5621 LONG64 dummy64;
5622 if (!directControl)
5623 rc = E_ACCESSDENIED;
5624 else
5625 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5626 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5627 fDelete ? 2 : 1 /* accessMode */,
5628 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5629 }
5630 catch (std::bad_alloc &)
5631 {
5632 rc = E_OUTOFMEMORY;
5633 }
5634
5635 return rc;
5636}
5637#endif // VBOX_WITH_GUEST_PROPS
5638
5639HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5640 const com::Utf8Str &aFlags)
5641{
5642#ifndef VBOX_WITH_GUEST_PROPS
5643 ReturnComNotImplemented();
5644#else // VBOX_WITH_GUEST_PROPS
5645 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5646 if (rc == E_ACCESSDENIED)
5647 /* The VM is not running or the service is not (yet) accessible */
5648 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5649 return rc;
5650#endif // VBOX_WITH_GUEST_PROPS
5651}
5652
5653HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5654{
5655 return setGuestProperty(aProperty, aValue, "");
5656}
5657
5658HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5659{
5660#ifndef VBOX_WITH_GUEST_PROPS
5661 ReturnComNotImplemented();
5662#else // VBOX_WITH_GUEST_PROPS
5663 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5664 if (rc == E_ACCESSDENIED)
5665 /* The VM is not running or the service is not (yet) accessible */
5666 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5667 return rc;
5668#endif // VBOX_WITH_GUEST_PROPS
5669}
5670
5671#ifdef VBOX_WITH_GUEST_PROPS
5672/**
5673 * Enumerate the guest properties in VBoxSVC's internal structures.
5674 */
5675HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5676 std::vector<com::Utf8Str> &aNames,
5677 std::vector<com::Utf8Str> &aValues,
5678 std::vector<LONG64> &aTimestamps,
5679 std::vector<com::Utf8Str> &aFlags)
5680{
5681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5682 Utf8Str strPatterns(aPatterns);
5683
5684 /*
5685 * Look for matching patterns and build up a list.
5686 */
5687 HWData::GuestPropertyMap propMap;
5688 for (HWData::GuestPropertyMap::const_iterator
5689 it = mHWData->mGuestProperties.begin();
5690 it != mHWData->mGuestProperties.end();
5691 ++it)
5692 {
5693 if ( strPatterns.isEmpty()
5694 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5695 RTSTR_MAX,
5696 it->first.c_str(),
5697 RTSTR_MAX,
5698 NULL)
5699 )
5700 propMap.insert(*it);
5701 }
5702
5703 alock.release();
5704
5705 /*
5706 * And build up the arrays for returning the property information.
5707 */
5708 size_t cEntries = propMap.size();
5709
5710 aNames.resize(cEntries);
5711 aValues.resize(cEntries);
5712 aTimestamps.resize(cEntries);
5713 aFlags.resize(cEntries);
5714
5715 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5716 size_t i = 0;
5717 for (HWData::GuestPropertyMap::const_iterator
5718 it = propMap.begin();
5719 it != propMap.end();
5720 ++it, ++i)
5721 {
5722 aNames[i] = it->first;
5723 aValues[i] = it->second.strValue;
5724 aTimestamps[i] = it->second.mTimestamp;
5725 GuestPropWriteFlags(it->second.mFlags, szFlags);
5726 aFlags[i] = Utf8Str(szFlags);
5727 }
5728
5729 return S_OK;
5730}
5731
5732/**
5733 * Enumerate the properties managed by a VM.
5734 * @returns E_ACCESSDENIED if the VM process is not available or not
5735 * currently handling queries and the setting should then be done in
5736 * VBoxSVC.
5737 */
5738HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5739 std::vector<com::Utf8Str> &aNames,
5740 std::vector<com::Utf8Str> &aValues,
5741 std::vector<LONG64> &aTimestamps,
5742 std::vector<com::Utf8Str> &aFlags)
5743{
5744 HRESULT rc;
5745 ComPtr<IInternalSessionControl> directControl;
5746 {
5747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5748 if (mData->mSession.mLockType == LockType_VM)
5749 directControl = mData->mSession.mDirectControl;
5750 }
5751
5752 com::SafeArray<BSTR> bNames;
5753 com::SafeArray<BSTR> bValues;
5754 com::SafeArray<LONG64> bTimestamps;
5755 com::SafeArray<BSTR> bFlags;
5756
5757 if (!directControl)
5758 rc = E_ACCESSDENIED;
5759 else
5760 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5761 ComSafeArrayAsOutParam(bNames),
5762 ComSafeArrayAsOutParam(bValues),
5763 ComSafeArrayAsOutParam(bTimestamps),
5764 ComSafeArrayAsOutParam(bFlags));
5765 size_t i;
5766 aNames.resize(bNames.size());
5767 for (i = 0; i < bNames.size(); ++i)
5768 aNames[i] = Utf8Str(bNames[i]);
5769 aValues.resize(bValues.size());
5770 for (i = 0; i < bValues.size(); ++i)
5771 aValues[i] = Utf8Str(bValues[i]);
5772 aTimestamps.resize(bTimestamps.size());
5773 for (i = 0; i < bTimestamps.size(); ++i)
5774 aTimestamps[i] = bTimestamps[i];
5775 aFlags.resize(bFlags.size());
5776 for (i = 0; i < bFlags.size(); ++i)
5777 aFlags[i] = Utf8Str(bFlags[i]);
5778
5779 return rc;
5780}
5781#endif // VBOX_WITH_GUEST_PROPS
5782HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5783 std::vector<com::Utf8Str> &aNames,
5784 std::vector<com::Utf8Str> &aValues,
5785 std::vector<LONG64> &aTimestamps,
5786 std::vector<com::Utf8Str> &aFlags)
5787{
5788#ifndef VBOX_WITH_GUEST_PROPS
5789 ReturnComNotImplemented();
5790#else // VBOX_WITH_GUEST_PROPS
5791
5792 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5793
5794 if (rc == E_ACCESSDENIED)
5795 /* The VM is not running or the service is not (yet) accessible */
5796 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5797 return rc;
5798#endif // VBOX_WITH_GUEST_PROPS
5799}
5800
5801HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5802 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5803{
5804 MediumAttachmentList atts;
5805
5806 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5807 if (FAILED(rc)) return rc;
5808
5809 aMediumAttachments.resize(atts.size());
5810 size_t i = 0;
5811 for (MediumAttachmentList::const_iterator
5812 it = atts.begin();
5813 it != atts.end();
5814 ++it, ++i)
5815 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5816
5817 return S_OK;
5818}
5819
5820HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5821 LONG aControllerPort,
5822 LONG aDevice,
5823 ComPtr<IMediumAttachment> &aAttachment)
5824{
5825 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5826 aName.c_str(), aControllerPort, aDevice));
5827
5828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5829
5830 aAttachment = NULL;
5831
5832 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5833 aName,
5834 aControllerPort,
5835 aDevice);
5836 if (pAttach.isNull())
5837 return setError(VBOX_E_OBJECT_NOT_FOUND,
5838 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5839 aDevice, aControllerPort, aName.c_str());
5840
5841 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5842
5843 return S_OK;
5844}
5845
5846
5847HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5848 StorageBus_T aConnectionType,
5849 ComPtr<IStorageController> &aController)
5850{
5851 if ( (aConnectionType <= StorageBus_Null)
5852 || (aConnectionType > StorageBus_VirtioSCSI))
5853 return setError(E_INVALIDARG,
5854 tr("Invalid connection type: %d"),
5855 aConnectionType);
5856
5857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5858
5859 HRESULT rc = i_checkStateDependency(MutableStateDep);
5860 if (FAILED(rc)) return rc;
5861
5862 /* try to find one with the name first. */
5863 ComObjPtr<StorageController> ctrl;
5864
5865 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5866 if (SUCCEEDED(rc))
5867 return setError(VBOX_E_OBJECT_IN_USE,
5868 tr("Storage controller named '%s' already exists"),
5869 aName.c_str());
5870
5871 ctrl.createObject();
5872
5873 /* get a new instance number for the storage controller */
5874 ULONG ulInstance = 0;
5875 bool fBootable = true;
5876 for (StorageControllerList::const_iterator
5877 it = mStorageControllers->begin();
5878 it != mStorageControllers->end();
5879 ++it)
5880 {
5881 if ((*it)->i_getStorageBus() == aConnectionType)
5882 {
5883 ULONG ulCurInst = (*it)->i_getInstance();
5884
5885 if (ulCurInst >= ulInstance)
5886 ulInstance = ulCurInst + 1;
5887
5888 /* Only one controller of each type can be marked as bootable. */
5889 if ((*it)->i_getBootable())
5890 fBootable = false;
5891 }
5892 }
5893
5894 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5895 if (FAILED(rc)) return rc;
5896
5897 i_setModified(IsModified_Storage);
5898 mStorageControllers.backup();
5899 mStorageControllers->push_back(ctrl);
5900
5901 ctrl.queryInterfaceTo(aController.asOutParam());
5902
5903 /* inform the direct session if any */
5904 alock.release();
5905 i_onStorageControllerChange(i_getId(), aName);
5906
5907 return S_OK;
5908}
5909
5910HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5911 ComPtr<IStorageController> &aStorageController)
5912{
5913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5914
5915 ComObjPtr<StorageController> ctrl;
5916
5917 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5918 if (SUCCEEDED(rc))
5919 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5920
5921 return rc;
5922}
5923
5924HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5925 ULONG aInstance,
5926 ComPtr<IStorageController> &aStorageController)
5927{
5928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5929
5930 for (StorageControllerList::const_iterator
5931 it = mStorageControllers->begin();
5932 it != mStorageControllers->end();
5933 ++it)
5934 {
5935 if ( (*it)->i_getStorageBus() == aConnectionType
5936 && (*it)->i_getInstance() == aInstance)
5937 {
5938 (*it).queryInterfaceTo(aStorageController.asOutParam());
5939 return S_OK;
5940 }
5941 }
5942
5943 return setError(VBOX_E_OBJECT_NOT_FOUND,
5944 tr("Could not find a storage controller with instance number '%lu'"),
5945 aInstance);
5946}
5947
5948HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5949{
5950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5951
5952 HRESULT rc = i_checkStateDependency(MutableStateDep);
5953 if (FAILED(rc)) return rc;
5954
5955 ComObjPtr<StorageController> ctrl;
5956
5957 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5958 if (SUCCEEDED(rc))
5959 {
5960 /* Ensure that only one controller of each type is marked as bootable. */
5961 if (aBootable == TRUE)
5962 {
5963 for (StorageControllerList::const_iterator
5964 it = mStorageControllers->begin();
5965 it != mStorageControllers->end();
5966 ++it)
5967 {
5968 ComObjPtr<StorageController> aCtrl = (*it);
5969
5970 if ( (aCtrl->i_getName() != aName)
5971 && aCtrl->i_getBootable() == TRUE
5972 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5973 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5974 {
5975 aCtrl->i_setBootable(FALSE);
5976 break;
5977 }
5978 }
5979 }
5980
5981 if (SUCCEEDED(rc))
5982 {
5983 ctrl->i_setBootable(aBootable);
5984 i_setModified(IsModified_Storage);
5985 }
5986 }
5987
5988 if (SUCCEEDED(rc))
5989 {
5990 /* inform the direct session if any */
5991 alock.release();
5992 i_onStorageControllerChange(i_getId(), aName);
5993 }
5994
5995 return rc;
5996}
5997
5998HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5999{
6000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6001
6002 HRESULT rc = i_checkStateDependency(MutableStateDep);
6003 if (FAILED(rc)) return rc;
6004
6005 ComObjPtr<StorageController> ctrl;
6006 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6007 if (FAILED(rc)) return rc;
6008
6009 MediumAttachmentList llDetachedAttachments;
6010 {
6011 /* find all attached devices to the appropriate storage controller and detach them all */
6012 // make a temporary list because detachDevice invalidates iterators into
6013 // mMediumAttachments
6014 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6015
6016 for (MediumAttachmentList::const_iterator
6017 it = llAttachments2.begin();
6018 it != llAttachments2.end();
6019 ++it)
6020 {
6021 MediumAttachment *pAttachTemp = *it;
6022
6023 AutoCaller localAutoCaller(pAttachTemp);
6024 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6025
6026 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6027
6028 if (pAttachTemp->i_getControllerName() == aName)
6029 {
6030 llDetachedAttachments.push_back(pAttachTemp);
6031 rc = i_detachDevice(pAttachTemp, alock, NULL);
6032 if (FAILED(rc)) return rc;
6033 }
6034 }
6035 }
6036
6037 /* send event about detached devices before removing parent controller */
6038 for (MediumAttachmentList::const_iterator
6039 it = llDetachedAttachments.begin();
6040 it != llDetachedAttachments.end();
6041 ++it)
6042 {
6043 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6044 }
6045
6046 /* We can remove it now. */
6047 i_setModified(IsModified_Storage);
6048 mStorageControllers.backup();
6049
6050 ctrl->i_unshare();
6051
6052 mStorageControllers->remove(ctrl);
6053
6054 /* inform the direct session if any */
6055 alock.release();
6056 i_onStorageControllerChange(i_getId(), aName);
6057
6058 return S_OK;
6059}
6060
6061HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6062 ComPtr<IUSBController> &aController)
6063{
6064 if ( (aType <= USBControllerType_Null)
6065 || (aType >= USBControllerType_Last))
6066 return setError(E_INVALIDARG,
6067 tr("Invalid USB controller type: %d"),
6068 aType);
6069
6070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6071
6072 HRESULT rc = i_checkStateDependency(MutableStateDep);
6073 if (FAILED(rc)) return rc;
6074
6075 /* try to find one with the same type first. */
6076 ComObjPtr<USBController> ctrl;
6077
6078 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6079 if (SUCCEEDED(rc))
6080 return setError(VBOX_E_OBJECT_IN_USE,
6081 tr("USB controller named '%s' already exists"),
6082 aName.c_str());
6083
6084 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6085 ULONG maxInstances;
6086 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6087 if (FAILED(rc))
6088 return rc;
6089
6090 ULONG cInstances = i_getUSBControllerCountByType(aType);
6091 if (cInstances >= maxInstances)
6092 return setError(E_INVALIDARG,
6093 tr("Too many USB controllers of this type"));
6094
6095 ctrl.createObject();
6096
6097 rc = ctrl->init(this, aName, aType);
6098 if (FAILED(rc)) return rc;
6099
6100 i_setModified(IsModified_USB);
6101 mUSBControllers.backup();
6102 mUSBControllers->push_back(ctrl);
6103
6104 ctrl.queryInterfaceTo(aController.asOutParam());
6105
6106 /* inform the direct session if any */
6107 alock.release();
6108 i_onUSBControllerChange();
6109
6110 return S_OK;
6111}
6112
6113HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6114{
6115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6116
6117 ComObjPtr<USBController> ctrl;
6118
6119 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6120 if (SUCCEEDED(rc))
6121 ctrl.queryInterfaceTo(aController.asOutParam());
6122
6123 return rc;
6124}
6125
6126HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6127 ULONG *aControllers)
6128{
6129 if ( (aType <= USBControllerType_Null)
6130 || (aType >= USBControllerType_Last))
6131 return setError(E_INVALIDARG,
6132 tr("Invalid USB controller type: %d"),
6133 aType);
6134
6135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6136
6137 ComObjPtr<USBController> ctrl;
6138
6139 *aControllers = i_getUSBControllerCountByType(aType);
6140
6141 return S_OK;
6142}
6143
6144HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6145{
6146
6147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6148
6149 HRESULT rc = i_checkStateDependency(MutableStateDep);
6150 if (FAILED(rc)) return rc;
6151
6152 ComObjPtr<USBController> ctrl;
6153 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6154 if (FAILED(rc)) return rc;
6155
6156 i_setModified(IsModified_USB);
6157 mUSBControllers.backup();
6158
6159 ctrl->i_unshare();
6160
6161 mUSBControllers->remove(ctrl);
6162
6163 /* inform the direct session if any */
6164 alock.release();
6165 i_onUSBControllerChange();
6166
6167 return S_OK;
6168}
6169
6170HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6171 ULONG *aOriginX,
6172 ULONG *aOriginY,
6173 ULONG *aWidth,
6174 ULONG *aHeight,
6175 BOOL *aEnabled)
6176{
6177 uint32_t u32OriginX= 0;
6178 uint32_t u32OriginY= 0;
6179 uint32_t u32Width = 0;
6180 uint32_t u32Height = 0;
6181 uint16_t u16Flags = 0;
6182
6183 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6184 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6185 if (RT_FAILURE(vrc))
6186 {
6187#ifdef RT_OS_WINDOWS
6188 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6189 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6190 * So just assign fEnable to TRUE again.
6191 * The right fix would be to change GUI API wrappers to make sure that parameters
6192 * are changed only if API succeeds.
6193 */
6194 *aEnabled = TRUE;
6195#endif
6196 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6197 tr("Saved guest size is not available (%Rrc)"),
6198 vrc);
6199 }
6200
6201 *aOriginX = u32OriginX;
6202 *aOriginY = u32OriginY;
6203 *aWidth = u32Width;
6204 *aHeight = u32Height;
6205 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6206
6207 return S_OK;
6208}
6209
6210HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6211 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6212{
6213 if (aScreenId != 0)
6214 return E_NOTIMPL;
6215
6216 if ( aBitmapFormat != BitmapFormat_BGR0
6217 && aBitmapFormat != BitmapFormat_BGRA
6218 && aBitmapFormat != BitmapFormat_RGBA
6219 && aBitmapFormat != BitmapFormat_PNG)
6220 return setError(E_NOTIMPL,
6221 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6222
6223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 uint8_t *pu8Data = NULL;
6226 uint32_t cbData = 0;
6227 uint32_t u32Width = 0;
6228 uint32_t u32Height = 0;
6229
6230 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6231
6232 if (RT_FAILURE(vrc))
6233 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6234 tr("Saved thumbnail data is not available (%Rrc)"),
6235 vrc);
6236
6237 HRESULT hr = S_OK;
6238
6239 *aWidth = u32Width;
6240 *aHeight = u32Height;
6241
6242 if (cbData > 0)
6243 {
6244 /* Convert pixels to the format expected by the API caller. */
6245 if (aBitmapFormat == BitmapFormat_BGR0)
6246 {
6247 /* [0] B, [1] G, [2] R, [3] 0. */
6248 aData.resize(cbData);
6249 memcpy(&aData.front(), pu8Data, cbData);
6250 }
6251 else if (aBitmapFormat == BitmapFormat_BGRA)
6252 {
6253 /* [0] B, [1] G, [2] R, [3] A. */
6254 aData.resize(cbData);
6255 for (uint32_t i = 0; i < cbData; i += 4)
6256 {
6257 aData[i] = pu8Data[i];
6258 aData[i + 1] = pu8Data[i + 1];
6259 aData[i + 2] = pu8Data[i + 2];
6260 aData[i + 3] = 0xff;
6261 }
6262 }
6263 else if (aBitmapFormat == BitmapFormat_RGBA)
6264 {
6265 /* [0] R, [1] G, [2] B, [3] A. */
6266 aData.resize(cbData);
6267 for (uint32_t i = 0; i < cbData; i += 4)
6268 {
6269 aData[i] = pu8Data[i + 2];
6270 aData[i + 1] = pu8Data[i + 1];
6271 aData[i + 2] = pu8Data[i];
6272 aData[i + 3] = 0xff;
6273 }
6274 }
6275 else if (aBitmapFormat == BitmapFormat_PNG)
6276 {
6277 uint8_t *pu8PNG = NULL;
6278 uint32_t cbPNG = 0;
6279 uint32_t cxPNG = 0;
6280 uint32_t cyPNG = 0;
6281
6282 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6283
6284 if (RT_SUCCESS(vrc))
6285 {
6286 aData.resize(cbPNG);
6287 if (cbPNG)
6288 memcpy(&aData.front(), pu8PNG, cbPNG);
6289 }
6290 else
6291 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6292 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6293 vrc);
6294
6295 RTMemFree(pu8PNG);
6296 }
6297 }
6298
6299 freeSavedDisplayScreenshot(pu8Data);
6300
6301 return hr;
6302}
6303
6304HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6305 ULONG *aWidth,
6306 ULONG *aHeight,
6307 std::vector<BitmapFormat_T> &aBitmapFormats)
6308{
6309 if (aScreenId != 0)
6310 return E_NOTIMPL;
6311
6312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 uint8_t *pu8Data = NULL;
6315 uint32_t cbData = 0;
6316 uint32_t u32Width = 0;
6317 uint32_t u32Height = 0;
6318
6319 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6320
6321 if (RT_FAILURE(vrc))
6322 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6323 tr("Saved screenshot data is not available (%Rrc)"),
6324 vrc);
6325
6326 *aWidth = u32Width;
6327 *aHeight = u32Height;
6328 aBitmapFormats.resize(1);
6329 aBitmapFormats[0] = BitmapFormat_PNG;
6330
6331 freeSavedDisplayScreenshot(pu8Data);
6332
6333 return S_OK;
6334}
6335
6336HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6337 BitmapFormat_T aBitmapFormat,
6338 ULONG *aWidth,
6339 ULONG *aHeight,
6340 std::vector<BYTE> &aData)
6341{
6342 if (aScreenId != 0)
6343 return E_NOTIMPL;
6344
6345 if (aBitmapFormat != BitmapFormat_PNG)
6346 return E_NOTIMPL;
6347
6348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6349
6350 uint8_t *pu8Data = NULL;
6351 uint32_t cbData = 0;
6352 uint32_t u32Width = 0;
6353 uint32_t u32Height = 0;
6354
6355 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6356
6357 if (RT_FAILURE(vrc))
6358 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6359 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6360 vrc);
6361
6362 *aWidth = u32Width;
6363 *aHeight = u32Height;
6364
6365 aData.resize(cbData);
6366 if (cbData)
6367 memcpy(&aData.front(), pu8Data, cbData);
6368
6369 freeSavedDisplayScreenshot(pu8Data);
6370
6371 return S_OK;
6372}
6373
6374HRESULT Machine::hotPlugCPU(ULONG aCpu)
6375{
6376 HRESULT rc = S_OK;
6377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 if (!mHWData->mCPUHotPlugEnabled)
6380 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6381
6382 if (aCpu >= mHWData->mCPUCount)
6383 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6384
6385 if (mHWData->mCPUAttached[aCpu])
6386 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6387
6388 alock.release();
6389 rc = i_onCPUChange(aCpu, false);
6390 alock.acquire();
6391 if (FAILED(rc)) return rc;
6392
6393 i_setModified(IsModified_MachineData);
6394 mHWData.backup();
6395 mHWData->mCPUAttached[aCpu] = true;
6396
6397 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6398 if (Global::IsOnline(mData->mMachineState))
6399 i_saveSettings(NULL);
6400
6401 return S_OK;
6402}
6403
6404HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6405{
6406 HRESULT rc = S_OK;
6407
6408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6409
6410 if (!mHWData->mCPUHotPlugEnabled)
6411 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6412
6413 if (aCpu >= SchemaDefs::MaxCPUCount)
6414 return setError(E_INVALIDARG,
6415 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6416 SchemaDefs::MaxCPUCount);
6417
6418 if (!mHWData->mCPUAttached[aCpu])
6419 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6420
6421 /* CPU 0 can't be detached */
6422 if (aCpu == 0)
6423 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6424
6425 alock.release();
6426 rc = i_onCPUChange(aCpu, true);
6427 alock.acquire();
6428 if (FAILED(rc)) return rc;
6429
6430 i_setModified(IsModified_MachineData);
6431 mHWData.backup();
6432 mHWData->mCPUAttached[aCpu] = false;
6433
6434 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6435 if (Global::IsOnline(mData->mMachineState))
6436 i_saveSettings(NULL);
6437
6438 return S_OK;
6439}
6440
6441HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6442{
6443 *aAttached = false;
6444
6445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6446
6447 /* If hotplug is enabled the CPU is always enabled. */
6448 if (!mHWData->mCPUHotPlugEnabled)
6449 {
6450 if (aCpu < mHWData->mCPUCount)
6451 *aAttached = true;
6452 }
6453 else
6454 {
6455 if (aCpu < SchemaDefs::MaxCPUCount)
6456 *aAttached = mHWData->mCPUAttached[aCpu];
6457 }
6458
6459 return S_OK;
6460}
6461
6462HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6463{
6464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6465
6466 Utf8Str log = i_getLogFilename(aIdx);
6467 if (!RTFileExists(log.c_str()))
6468 log.setNull();
6469 aFilename = log;
6470
6471 return S_OK;
6472}
6473
6474HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6475{
6476 if (aSize < 0)
6477 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6478
6479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6480
6481 HRESULT rc = S_OK;
6482 Utf8Str log = i_getLogFilename(aIdx);
6483
6484 /* do not unnecessarily hold the lock while doing something which does
6485 * not need the lock and potentially takes a long time. */
6486 alock.release();
6487
6488 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6489 * keeps the SOAP reply size under 1M for the webservice (we're using
6490 * base64 encoded strings for binary data for years now, avoiding the
6491 * expansion of each byte array element to approx. 25 bytes of XML. */
6492 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6493 aData.resize(cbData);
6494
6495 RTFILE LogFile;
6496 int vrc = RTFileOpen(&LogFile, log.c_str(),
6497 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6498 if (RT_SUCCESS(vrc))
6499 {
6500 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6501 if (RT_SUCCESS(vrc))
6502 aData.resize(cbData);
6503 else
6504 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6505 tr("Could not read log file '%s' (%Rrc)"),
6506 log.c_str(), vrc);
6507 RTFileClose(LogFile);
6508 }
6509 else
6510 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6511 tr("Could not open log file '%s' (%Rrc)"),
6512 log.c_str(), vrc);
6513
6514 if (FAILED(rc))
6515 aData.resize(0);
6516
6517 return rc;
6518}
6519
6520
6521/**
6522 * Currently this method doesn't attach device to the running VM,
6523 * just makes sure it's plugged on next VM start.
6524 */
6525HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6526{
6527 // lock scope
6528 {
6529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6530
6531 HRESULT rc = i_checkStateDependency(MutableStateDep);
6532 if (FAILED(rc)) return rc;
6533
6534 ChipsetType_T aChipset = ChipsetType_PIIX3;
6535 COMGETTER(ChipsetType)(&aChipset);
6536
6537 if (aChipset != ChipsetType_ICH9)
6538 {
6539 return setError(E_INVALIDARG,
6540 tr("Host PCI attachment only supported with ICH9 chipset"));
6541 }
6542
6543 // check if device with this host PCI address already attached
6544 for (HWData::PCIDeviceAssignmentList::const_iterator
6545 it = mHWData->mPCIDeviceAssignments.begin();
6546 it != mHWData->mPCIDeviceAssignments.end();
6547 ++it)
6548 {
6549 LONG iHostAddress = -1;
6550 ComPtr<PCIDeviceAttachment> pAttach;
6551 pAttach = *it;
6552 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6553 if (iHostAddress == aHostAddress)
6554 return setError(E_INVALIDARG,
6555 tr("Device with host PCI address already attached to this VM"));
6556 }
6557
6558 ComObjPtr<PCIDeviceAttachment> pda;
6559 char name[32];
6560
6561 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6562 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6563 pda.createObject();
6564 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6565 i_setModified(IsModified_MachineData);
6566 mHWData.backup();
6567 mHWData->mPCIDeviceAssignments.push_back(pda);
6568 }
6569
6570 return S_OK;
6571}
6572
6573/**
6574 * Currently this method doesn't detach device from the running VM,
6575 * just makes sure it's not plugged on next VM start.
6576 */
6577HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6578{
6579 ComObjPtr<PCIDeviceAttachment> pAttach;
6580 bool fRemoved = false;
6581 HRESULT rc;
6582
6583 // lock scope
6584 {
6585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 rc = i_checkStateDependency(MutableStateDep);
6588 if (FAILED(rc)) return rc;
6589
6590 for (HWData::PCIDeviceAssignmentList::const_iterator
6591 it = mHWData->mPCIDeviceAssignments.begin();
6592 it != mHWData->mPCIDeviceAssignments.end();
6593 ++it)
6594 {
6595 LONG iHostAddress = -1;
6596 pAttach = *it;
6597 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6598 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6599 {
6600 i_setModified(IsModified_MachineData);
6601 mHWData.backup();
6602 mHWData->mPCIDeviceAssignments.remove(pAttach);
6603 fRemoved = true;
6604 break;
6605 }
6606 }
6607 }
6608
6609
6610 /* Fire event outside of the lock */
6611 if (fRemoved)
6612 {
6613 Assert(!pAttach.isNull());
6614 ComPtr<IEventSource> es;
6615 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6616 Assert(SUCCEEDED(rc));
6617 Bstr mid;
6618 rc = this->COMGETTER(Id)(mid.asOutParam());
6619 Assert(SUCCEEDED(rc));
6620 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6621 }
6622
6623 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6624 tr("No host PCI device %08x attached"),
6625 aHostAddress
6626 );
6627}
6628
6629HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6630{
6631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6632
6633 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6634 size_t i = 0;
6635 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6636 it = mHWData->mPCIDeviceAssignments.begin();
6637 it != mHWData->mPCIDeviceAssignments.end();
6638 ++it, ++i)
6639 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6640
6641 return S_OK;
6642}
6643
6644HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6645{
6646 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6647
6648 return S_OK;
6649}
6650
6651HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6652{
6653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6654
6655 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6656
6657 return S_OK;
6658}
6659
6660HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6661{
6662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6663 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6664 if (SUCCEEDED(hrc))
6665 {
6666 hrc = mHWData.backupEx();
6667 if (SUCCEEDED(hrc))
6668 {
6669 i_setModified(IsModified_MachineData);
6670 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6671 }
6672 }
6673 return hrc;
6674}
6675
6676HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6677{
6678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6679 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6680 return S_OK;
6681}
6682
6683HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6684{
6685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6687 if (SUCCEEDED(hrc))
6688 {
6689 hrc = mHWData.backupEx();
6690 if (SUCCEEDED(hrc))
6691 {
6692 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6693 if (SUCCEEDED(hrc))
6694 i_setModified(IsModified_MachineData);
6695 }
6696 }
6697 return hrc;
6698}
6699
6700HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6701{
6702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6703
6704 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6705
6706 return S_OK;
6707}
6708
6709HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6710{
6711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6712 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6713 if (SUCCEEDED(hrc))
6714 {
6715 hrc = mHWData.backupEx();
6716 if (SUCCEEDED(hrc))
6717 {
6718 i_setModified(IsModified_MachineData);
6719 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6720 }
6721 }
6722 return hrc;
6723}
6724
6725HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6726{
6727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6728
6729 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6730
6731 return S_OK;
6732}
6733
6734HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6735{
6736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6737
6738 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6739 if ( SUCCEEDED(hrc)
6740 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6741 {
6742 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6743 int vrc;
6744
6745 if (aAutostartEnabled)
6746 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6747 else
6748 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6749
6750 if (RT_SUCCESS(vrc))
6751 {
6752 hrc = mHWData.backupEx();
6753 if (SUCCEEDED(hrc))
6754 {
6755 i_setModified(IsModified_MachineData);
6756 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6757 }
6758 }
6759 else if (vrc == VERR_NOT_SUPPORTED)
6760 hrc = setError(VBOX_E_NOT_SUPPORTED,
6761 tr("The VM autostart feature is not supported on this platform"));
6762 else if (vrc == VERR_PATH_NOT_FOUND)
6763 hrc = setError(E_FAIL,
6764 tr("The path to the autostart database is not set"));
6765 else
6766 hrc = setError(E_UNEXPECTED,
6767 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6768 aAutostartEnabled ? "Adding" : "Removing",
6769 mUserData->s.strName.c_str(), vrc);
6770 }
6771 return hrc;
6772}
6773
6774HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6775{
6776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6777
6778 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6779
6780 return S_OK;
6781}
6782
6783HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6784{
6785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6786 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6787 if (SUCCEEDED(hrc))
6788 {
6789 hrc = mHWData.backupEx();
6790 if (SUCCEEDED(hrc))
6791 {
6792 i_setModified(IsModified_MachineData);
6793 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6794 }
6795 }
6796 return hrc;
6797}
6798
6799HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6800{
6801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6802
6803 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6804
6805 return S_OK;
6806}
6807
6808HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6809{
6810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6811 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6812 if ( SUCCEEDED(hrc)
6813 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6814 {
6815 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6816 int vrc;
6817
6818 if (aAutostopType != AutostopType_Disabled)
6819 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6820 else
6821 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6822
6823 if (RT_SUCCESS(vrc))
6824 {
6825 hrc = mHWData.backupEx();
6826 if (SUCCEEDED(hrc))
6827 {
6828 i_setModified(IsModified_MachineData);
6829 mHWData->mAutostart.enmAutostopType = aAutostopType;
6830 }
6831 }
6832 else if (vrc == VERR_NOT_SUPPORTED)
6833 hrc = setError(VBOX_E_NOT_SUPPORTED,
6834 tr("The VM autostop feature is not supported on this platform"));
6835 else if (vrc == VERR_PATH_NOT_FOUND)
6836 hrc = setError(E_FAIL,
6837 tr("The path to the autostart database is not set"));
6838 else
6839 hrc = setError(E_UNEXPECTED,
6840 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6841 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6842 mUserData->s.strName.c_str(), vrc);
6843 }
6844 return hrc;
6845}
6846
6847HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6848{
6849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6850
6851 aDefaultFrontend = mHWData->mDefaultFrontend;
6852
6853 return S_OK;
6854}
6855
6856HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6857{
6858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6859 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6860 if (SUCCEEDED(hrc))
6861 {
6862 hrc = mHWData.backupEx();
6863 if (SUCCEEDED(hrc))
6864 {
6865 i_setModified(IsModified_MachineData);
6866 mHWData->mDefaultFrontend = aDefaultFrontend;
6867 }
6868 }
6869 return hrc;
6870}
6871
6872HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6873{
6874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6875 size_t cbIcon = mUserData->s.ovIcon.size();
6876 aIcon.resize(cbIcon);
6877 if (cbIcon)
6878 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6879 return S_OK;
6880}
6881
6882HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6883{
6884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6885 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6886 if (SUCCEEDED(hrc))
6887 {
6888 i_setModified(IsModified_MachineData);
6889 mUserData.backup();
6890 size_t cbIcon = aIcon.size();
6891 mUserData->s.ovIcon.resize(cbIcon);
6892 if (cbIcon)
6893 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6894 }
6895 return hrc;
6896}
6897
6898HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6899{
6900#ifdef VBOX_WITH_USB
6901 *aUSBProxyAvailable = true;
6902#else
6903 *aUSBProxyAvailable = false;
6904#endif
6905 return S_OK;
6906}
6907
6908HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6909{
6910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6911
6912 *aVMProcessPriority = mUserData->s.enmVMPriority;
6913
6914 return S_OK;
6915}
6916
6917HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6918{
6919 RT_NOREF(aVMProcessPriority);
6920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6921 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6922 if (SUCCEEDED(hrc))
6923 {
6924 hrc = mUserData.backupEx();
6925 if (SUCCEEDED(hrc))
6926 {
6927 i_setModified(IsModified_MachineData);
6928 mUserData->s.enmVMPriority = aVMProcessPriority;
6929 }
6930 }
6931 alock.release();
6932 if (SUCCEEDED(hrc))
6933 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6934 return hrc;
6935}
6936
6937HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6938 ComPtr<IProgress> &aProgress)
6939{
6940 ComObjPtr<Progress> pP;
6941 Progress *ppP = pP;
6942 IProgress *iP = static_cast<IProgress *>(ppP);
6943 IProgress **pProgress = &iP;
6944
6945 IMachine *pTarget = aTarget;
6946
6947 /* Convert the options. */
6948 RTCList<CloneOptions_T> optList;
6949 if (aOptions.size())
6950 for (size_t i = 0; i < aOptions.size(); ++i)
6951 optList.append(aOptions[i]);
6952
6953 if (optList.contains(CloneOptions_Link))
6954 {
6955 if (!i_isSnapshotMachine())
6956 return setError(E_INVALIDARG,
6957 tr("Linked clone can only be created from a snapshot"));
6958 if (aMode != CloneMode_MachineState)
6959 return setError(E_INVALIDARG,
6960 tr("Linked clone can only be created for a single machine state"));
6961 }
6962 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6963
6964 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6965
6966 HRESULT rc = pWorker->start(pProgress);
6967
6968 pP = static_cast<Progress *>(*pProgress);
6969 pP.queryInterfaceTo(aProgress.asOutParam());
6970
6971 return rc;
6972
6973}
6974
6975HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6976 const com::Utf8Str &aType,
6977 ComPtr<IProgress> &aProgress)
6978{
6979 LogFlowThisFuncEnter();
6980
6981 ComObjPtr<Progress> ptrProgress;
6982 HRESULT hrc = ptrProgress.createObject();
6983 if (SUCCEEDED(hrc))
6984 {
6985 com::Utf8Str strDefaultPath;
6986 if (aTargetPath.isEmpty())
6987 i_calculateFullPath(".", strDefaultPath);
6988
6989 /* Initialize our worker task */
6990 MachineMoveVM *pTask = NULL;
6991 try
6992 {
6993 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
6994 }
6995 catch (std::bad_alloc &)
6996 {
6997 return E_OUTOFMEMORY;
6998 }
6999
7000 hrc = pTask->init();//no exceptions are thrown
7001
7002 if (SUCCEEDED(hrc))
7003 {
7004 hrc = pTask->createThread();
7005 pTask = NULL; /* Consumed by createThread(). */
7006 if (SUCCEEDED(hrc))
7007 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7008 else
7009 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7010 }
7011 else
7012 delete pTask;
7013 }
7014
7015 LogFlowThisFuncLeave();
7016 return hrc;
7017
7018}
7019
7020HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7021{
7022 NOREF(aProgress);
7023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7024
7025 // This check should always fail.
7026 HRESULT rc = i_checkStateDependency(MutableStateDep);
7027 if (FAILED(rc)) return rc;
7028
7029 AssertFailedReturn(E_NOTIMPL);
7030}
7031
7032HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7033{
7034 NOREF(aSavedStateFile);
7035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7036
7037 // This check should always fail.
7038 HRESULT rc = i_checkStateDependency(MutableStateDep);
7039 if (FAILED(rc)) return rc;
7040
7041 AssertFailedReturn(E_NOTIMPL);
7042}
7043
7044HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7045{
7046 NOREF(aFRemoveFile);
7047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7048
7049 // This check should always fail.
7050 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7051 if (FAILED(rc)) return rc;
7052
7053 AssertFailedReturn(E_NOTIMPL);
7054}
7055
7056// public methods for internal purposes
7057/////////////////////////////////////////////////////////////////////////////
7058
7059/**
7060 * Adds the given IsModified_* flag to the dirty flags of the machine.
7061 * This must be called either during i_loadSettings or under the machine write lock.
7062 * @param fl Flag
7063 * @param fAllowStateModification If state modifications are allowed.
7064 */
7065void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7066{
7067 mData->flModifications |= fl;
7068 if (fAllowStateModification && i_isStateModificationAllowed())
7069 mData->mCurrentStateModified = true;
7070}
7071
7072/**
7073 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7074 * care of the write locking.
7075 *
7076 * @param fModification The flag to add.
7077 * @param fAllowStateModification If state modifications are allowed.
7078 */
7079void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7080{
7081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7082 i_setModified(fModification, fAllowStateModification);
7083}
7084
7085/**
7086 * Saves the registry entry of this machine to the given configuration node.
7087 *
7088 * @param data Machine registry data.
7089 *
7090 * @note locks this object for reading.
7091 */
7092HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7093{
7094 AutoLimitedCaller autoCaller(this);
7095 AssertComRCReturnRC(autoCaller.rc());
7096
7097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7098
7099 data.uuid = mData->mUuid;
7100 data.strSettingsFile = mData->m_strConfigFile;
7101
7102 return S_OK;
7103}
7104
7105/**
7106 * Calculates the absolute path of the given path taking the directory of the
7107 * machine settings file as the current directory.
7108 *
7109 * @param strPath Path to calculate the absolute path for.
7110 * @param aResult Where to put the result (used only on success, can be the
7111 * same Utf8Str instance as passed in @a aPath).
7112 * @return IPRT result.
7113 *
7114 * @note Locks this object for reading.
7115 */
7116int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7117{
7118 AutoCaller autoCaller(this);
7119 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7120
7121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7122
7123 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7124
7125 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7126
7127 strSettingsDir.stripFilename();
7128 char szFolder[RTPATH_MAX];
7129 size_t cbFolder = sizeof(szFolder);
7130 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7131 if (RT_SUCCESS(vrc))
7132 aResult = szFolder;
7133
7134 return vrc;
7135}
7136
7137/**
7138 * Copies strSource to strTarget, making it relative to the machine folder
7139 * if it is a subdirectory thereof, or simply copying it otherwise.
7140 *
7141 * @param strSource Path to evaluate and copy.
7142 * @param strTarget Buffer to receive target path.
7143 *
7144 * @note Locks this object for reading.
7145 */
7146void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7147 Utf8Str &strTarget)
7148{
7149 AutoCaller autoCaller(this);
7150 AssertComRCReturn(autoCaller.rc(), (void)0);
7151
7152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7153
7154 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7155 // use strTarget as a temporary buffer to hold the machine settings dir
7156 strTarget = mData->m_strConfigFileFull;
7157 strTarget.stripFilename();
7158 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7159 {
7160 // is relative: then append what's left
7161 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7162 // for empty paths (only possible for subdirs) use "." to avoid
7163 // triggering default settings for not present config attributes.
7164 if (strTarget.isEmpty())
7165 strTarget = ".";
7166 }
7167 else
7168 // is not relative: then overwrite
7169 strTarget = strSource;
7170}
7171
7172/**
7173 * Returns the full path to the machine's log folder in the
7174 * \a aLogFolder argument.
7175 */
7176void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7177{
7178 AutoCaller autoCaller(this);
7179 AssertComRCReturnVoid(autoCaller.rc());
7180
7181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7182
7183 char szTmp[RTPATH_MAX];
7184 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7185 if (RT_SUCCESS(vrc))
7186 {
7187 if (szTmp[0] && !mUserData.isNull())
7188 {
7189 char szTmp2[RTPATH_MAX];
7190 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7191 if (RT_SUCCESS(vrc))
7192 aLogFolder.printf("%s%c%s",
7193 szTmp2,
7194 RTPATH_DELIMITER,
7195 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7196 }
7197 else
7198 vrc = VERR_PATH_IS_RELATIVE;
7199 }
7200
7201 if (RT_FAILURE(vrc))
7202 {
7203 // fallback if VBOX_USER_LOGHOME is not set or invalid
7204 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7205 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7206 aLogFolder.append(RTPATH_DELIMITER);
7207 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7208 }
7209}
7210
7211/**
7212 * Returns the full path to the machine's log file for an given index.
7213 */
7214Utf8Str Machine::i_getLogFilename(ULONG idx)
7215{
7216 Utf8Str logFolder;
7217 getLogFolder(logFolder);
7218 Assert(logFolder.length());
7219
7220 Utf8Str log;
7221 if (idx == 0)
7222 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7223#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7224 else if (idx == 1)
7225 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7226 else
7227 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7228#else
7229 else
7230 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7231#endif
7232 return log;
7233}
7234
7235/**
7236 * Returns the full path to the machine's hardened log file.
7237 */
7238Utf8Str Machine::i_getHardeningLogFilename(void)
7239{
7240 Utf8Str strFilename;
7241 getLogFolder(strFilename);
7242 Assert(strFilename.length());
7243 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7244 return strFilename;
7245}
7246
7247/**
7248 * Returns the default NVRAM filename based on the location of the VM config.
7249 * Note that this is a relative path.
7250 */
7251Utf8Str Machine::i_getDefaultNVRAMFilename()
7252{
7253 AutoCaller autoCaller(this);
7254 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7255
7256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7257
7258 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7259 || i_isSnapshotMachine())
7260 return Utf8Str::Empty;
7261
7262 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7263 strNVRAMFilePath.stripPath();
7264 strNVRAMFilePath.stripSuffix();
7265 strNVRAMFilePath += ".nvram";
7266
7267 return strNVRAMFilePath;
7268}
7269
7270/**
7271 * Returns the NVRAM filename for a new snapshot. This intentionally works
7272 * similarly to the saved state file naming. Note that this is usually
7273 * a relative path, unless the snapshot folder is absolute.
7274 */
7275Utf8Str Machine::i_getSnapshotNVRAMFilename()
7276{
7277 AutoCaller autoCaller(this);
7278 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7279
7280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7281
7282 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7283 return Utf8Str::Empty;
7284
7285 RTTIMESPEC ts;
7286 RTTimeNow(&ts);
7287 RTTIME time;
7288 RTTimeExplode(&time, &ts);
7289
7290 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7291 strNVRAMFilePath += RTPATH_DELIMITER;
7292 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7293 time.i32Year, time.u8Month, time.u8MonthDay,
7294 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7295
7296 return strNVRAMFilePath;
7297}
7298
7299/**
7300 * Composes a unique saved state filename based on the current system time. The filename is
7301 * granular to the second so this will work so long as no more than one snapshot is taken on
7302 * a machine per second.
7303 *
7304 * Before version 4.1, we used this formula for saved state files:
7305 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7306 * which no longer works because saved state files can now be shared between the saved state of the
7307 * "saved" machine and an online snapshot, and the following would cause problems:
7308 * 1) save machine
7309 * 2) create online snapshot from that machine state --> reusing saved state file
7310 * 3) save machine again --> filename would be reused, breaking the online snapshot
7311 *
7312 * So instead we now use a timestamp.
7313 *
7314 * @param strStateFilePath
7315 */
7316
7317void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7318{
7319 AutoCaller autoCaller(this);
7320 AssertComRCReturnVoid(autoCaller.rc());
7321
7322 {
7323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7324 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7325 }
7326
7327 RTTIMESPEC ts;
7328 RTTimeNow(&ts);
7329 RTTIME time;
7330 RTTimeExplode(&time, &ts);
7331
7332 strStateFilePath += RTPATH_DELIMITER;
7333 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7334 time.i32Year, time.u8Month, time.u8MonthDay,
7335 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7336}
7337
7338/**
7339 * Returns whether at least one USB controller is present for the VM.
7340 */
7341bool Machine::i_isUSBControllerPresent()
7342{
7343 AutoCaller autoCaller(this);
7344 AssertComRCReturn(autoCaller.rc(), false);
7345
7346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7347
7348 return (mUSBControllers->size() > 0);
7349}
7350
7351
7352/**
7353 * @note Locks this object for writing, calls the client process
7354 * (inside the lock).
7355 */
7356HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7357 const Utf8Str &strFrontend,
7358 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7359 ProgressProxy *aProgress)
7360{
7361 LogFlowThisFuncEnter();
7362
7363 AssertReturn(aControl, E_FAIL);
7364 AssertReturn(aProgress, E_FAIL);
7365 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7366
7367 AutoCaller autoCaller(this);
7368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7369
7370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7371
7372 if (!mData->mRegistered)
7373 return setError(E_UNEXPECTED,
7374 tr("The machine '%s' is not registered"),
7375 mUserData->s.strName.c_str());
7376
7377 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7378
7379 /* The process started when launching a VM with separate UI/VM processes is always
7380 * the UI process, i.e. needs special handling as it won't claim the session. */
7381 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7382
7383 if (fSeparate)
7384 {
7385 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7386 return setError(VBOX_E_INVALID_OBJECT_STATE,
7387 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7388 mUserData->s.strName.c_str());
7389 }
7390 else
7391 {
7392 if ( mData->mSession.mState == SessionState_Locked
7393 || mData->mSession.mState == SessionState_Spawning
7394 || mData->mSession.mState == SessionState_Unlocking)
7395 return setError(VBOX_E_INVALID_OBJECT_STATE,
7396 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7397 mUserData->s.strName.c_str());
7398
7399 /* may not be busy */
7400 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7401 }
7402
7403 /* Hardening logging */
7404#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7405 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7406 {
7407 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7408 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7409 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7410 {
7411 Utf8Str strStartupLogDir = strHardeningLogFile;
7412 strStartupLogDir.stripFilename();
7413 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7414 file without stripping the file. */
7415 }
7416 strSupHardeningLogArg.append(strHardeningLogFile);
7417
7418 /* Remove legacy log filename to avoid confusion. */
7419 Utf8Str strOldStartupLogFile;
7420 getLogFolder(strOldStartupLogFile);
7421 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7422 RTFileDelete(strOldStartupLogFile.c_str());
7423 }
7424#else
7425 Utf8Str strSupHardeningLogArg;
7426#endif
7427
7428 Utf8Str strAppOverride;
7429#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7430 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7431#endif
7432
7433 bool fUseVBoxSDS = false;
7434 Utf8Str strCanonicalName;
7435 if (false)
7436 { }
7437#ifdef VBOX_WITH_QTGUI
7438 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7439 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7440 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7441 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7442 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7443 {
7444 strCanonicalName = "GUI/Qt";
7445 fUseVBoxSDS = true;
7446 }
7447#endif
7448#ifdef VBOX_WITH_VBOXSDL
7449 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7450 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7451 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7452 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7453 {
7454 strCanonicalName = "GUI/SDL";
7455 fUseVBoxSDS = true;
7456 }
7457#endif
7458#ifdef VBOX_WITH_HEADLESS
7459 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7460 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7461 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7462 {
7463 strCanonicalName = "headless";
7464 }
7465#endif
7466 else
7467 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7468
7469 Utf8Str idStr = mData->mUuid.toString();
7470 Utf8Str const &strMachineName = mUserData->s.strName;
7471 RTPROCESS pid = NIL_RTPROCESS;
7472
7473#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7474 RT_NOREF(fUseVBoxSDS);
7475#else
7476 DWORD idCallerSession = ~(DWORD)0;
7477 if (fUseVBoxSDS)
7478 {
7479 /*
7480 * The VBoxSDS should be used for process launching the VM with
7481 * GUI only if the caller and the VBoxSDS are in different Windows
7482 * sessions and the caller in the interactive one.
7483 */
7484 fUseVBoxSDS = false;
7485
7486 /* Get windows session of the current process. The process token used
7487 due to several reasons:
7488 1. The token is absent for the current thread except someone set it
7489 for us.
7490 2. Needs to get the id of the session where the process is started.
7491 We only need to do this once, though. */
7492 static DWORD s_idCurrentSession = ~(DWORD)0;
7493 DWORD idCurrentSession = s_idCurrentSession;
7494 if (idCurrentSession == ~(DWORD)0)
7495 {
7496 HANDLE hCurrentProcessToken = NULL;
7497 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7498 {
7499 DWORD cbIgn = 0;
7500 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7501 s_idCurrentSession = idCurrentSession;
7502 else
7503 {
7504 idCurrentSession = ~(DWORD)0;
7505 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7506 }
7507 CloseHandle(hCurrentProcessToken);
7508 }
7509 else
7510 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7511 }
7512
7513 /* get the caller's session */
7514 HRESULT hrc = CoImpersonateClient();
7515 if (SUCCEEDED(hrc))
7516 {
7517 HANDLE hCallerThreadToken;
7518 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7519 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7520 &hCallerThreadToken))
7521 {
7522 SetLastError(NO_ERROR);
7523 DWORD cbIgn = 0;
7524 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7525 {
7526 /* Only need to use SDS if the session ID differs: */
7527 if (idCurrentSession != idCallerSession)
7528 {
7529 fUseVBoxSDS = false;
7530
7531 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7532 DWORD cbTokenGroups = 0;
7533 PTOKEN_GROUPS pTokenGroups = NULL;
7534 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7535 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7536 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7537 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7538 {
7539 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7540 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7541 PSID pInteractiveSid = NULL;
7542 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7543 {
7544 /* Iterate over the groups looking for the interactive SID: */
7545 fUseVBoxSDS = false;
7546 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7547 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7548 {
7549 fUseVBoxSDS = true;
7550 break;
7551 }
7552 FreeSid(pInteractiveSid);
7553 }
7554 }
7555 else
7556 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7557 RTMemTmpFree(pTokenGroups);
7558 }
7559 }
7560 else
7561 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7562 CloseHandle(hCallerThreadToken);
7563 }
7564 else
7565 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7566 CoRevertToSelf();
7567 }
7568 else
7569 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7570 }
7571 if (fUseVBoxSDS)
7572 {
7573 /* connect to VBoxSDS */
7574 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7575 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7576 if (FAILED(rc))
7577 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7578 strMachineName.c_str());
7579
7580 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7581 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7582 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7583 service to access the files. */
7584 rc = CoSetProxyBlanket(pVBoxSDS,
7585 RPC_C_AUTHN_DEFAULT,
7586 RPC_C_AUTHZ_DEFAULT,
7587 COLE_DEFAULT_PRINCIPAL,
7588 RPC_C_AUTHN_LEVEL_DEFAULT,
7589 RPC_C_IMP_LEVEL_IMPERSONATE,
7590 NULL,
7591 EOAC_DEFAULT);
7592 if (FAILED(rc))
7593 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7594
7595 size_t const cEnvVars = aEnvironmentChanges.size();
7596 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7597 for (size_t i = 0; i < cEnvVars; i++)
7598 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7599
7600 ULONG uPid = 0;
7601 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7602 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7603 idCallerSession, &uPid);
7604 if (FAILED(rc))
7605 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7606 pid = (RTPROCESS)uPid;
7607 }
7608 else
7609#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7610 {
7611 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7612 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7613 if (RT_FAILURE(vrc))
7614 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7615 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7616 }
7617
7618 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7619 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7620
7621 if (!fSeparate)
7622 {
7623 /*
7624 * Note that we don't release the lock here before calling the client,
7625 * because it doesn't need to call us back if called with a NULL argument.
7626 * Releasing the lock here is dangerous because we didn't prepare the
7627 * launch data yet, but the client we've just started may happen to be
7628 * too fast and call LockMachine() that will fail (because of PID, etc.),
7629 * so that the Machine will never get out of the Spawning session state.
7630 */
7631
7632 /* inform the session that it will be a remote one */
7633 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7634#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7635 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7636#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7637 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7638#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7639 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7640
7641 if (FAILED(rc))
7642 {
7643 /* restore the session state */
7644 mData->mSession.mState = SessionState_Unlocked;
7645 alock.release();
7646 mParent->i_addProcessToReap(pid);
7647 /* The failure may occur w/o any error info (from RPC), so provide one */
7648 return setError(VBOX_E_VM_ERROR,
7649 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7650 }
7651
7652 /* attach launch data to the machine */
7653 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7654 mData->mSession.mRemoteControls.push_back(aControl);
7655 mData->mSession.mProgress = aProgress;
7656 mData->mSession.mPID = pid;
7657 mData->mSession.mState = SessionState_Spawning;
7658 Assert(strCanonicalName.isNotEmpty());
7659 mData->mSession.mName = strCanonicalName;
7660 }
7661 else
7662 {
7663 /* For separate UI process we declare the launch as completed instantly, as the
7664 * actual headless VM start may or may not come. No point in remembering anything
7665 * yet, as what matters for us is when the headless VM gets started. */
7666 aProgress->i_notifyComplete(S_OK);
7667 }
7668
7669 alock.release();
7670 mParent->i_addProcessToReap(pid);
7671
7672 LogFlowThisFuncLeave();
7673 return S_OK;
7674}
7675
7676/**
7677 * Returns @c true if the given session machine instance has an open direct
7678 * session (and optionally also for direct sessions which are closing) and
7679 * returns the session control machine instance if so.
7680 *
7681 * Note that when the method returns @c false, the arguments remain unchanged.
7682 *
7683 * @param aMachine Session machine object.
7684 * @param aControl Direct session control object (optional).
7685 * @param aRequireVM If true then only allow VM sessions.
7686 * @param aAllowClosing If true then additionally a session which is currently
7687 * being closed will also be allowed.
7688 *
7689 * @note locks this object for reading.
7690 */
7691bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7692 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7693 bool aRequireVM /*= false*/,
7694 bool aAllowClosing /*= false*/)
7695{
7696 AutoLimitedCaller autoCaller(this);
7697 AssertComRCReturn(autoCaller.rc(), false);
7698
7699 /* just return false for inaccessible machines */
7700 if (getObjectState().getState() != ObjectState::Ready)
7701 return false;
7702
7703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7704
7705 if ( ( mData->mSession.mState == SessionState_Locked
7706 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7707 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7708 )
7709 {
7710 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7711
7712 aMachine = mData->mSession.mMachine;
7713
7714 if (aControl != NULL)
7715 *aControl = mData->mSession.mDirectControl;
7716
7717 return true;
7718 }
7719
7720 return false;
7721}
7722
7723/**
7724 * Returns @c true if the given machine has an spawning direct session.
7725 *
7726 * @note locks this object for reading.
7727 */
7728bool Machine::i_isSessionSpawning()
7729{
7730 AutoLimitedCaller autoCaller(this);
7731 AssertComRCReturn(autoCaller.rc(), false);
7732
7733 /* just return false for inaccessible machines */
7734 if (getObjectState().getState() != ObjectState::Ready)
7735 return false;
7736
7737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7738
7739 if (mData->mSession.mState == SessionState_Spawning)
7740 return true;
7741
7742 return false;
7743}
7744
7745/**
7746 * Called from the client watcher thread to check for unexpected client process
7747 * death during Session_Spawning state (e.g. before it successfully opened a
7748 * direct session).
7749 *
7750 * On Win32 and on OS/2, this method is called only when we've got the
7751 * direct client's process termination notification, so it always returns @c
7752 * true.
7753 *
7754 * On other platforms, this method returns @c true if the client process is
7755 * terminated and @c false if it's still alive.
7756 *
7757 * @note Locks this object for writing.
7758 */
7759bool Machine::i_checkForSpawnFailure()
7760{
7761 AutoCaller autoCaller(this);
7762 if (!autoCaller.isOk())
7763 {
7764 /* nothing to do */
7765 LogFlowThisFunc(("Already uninitialized!\n"));
7766 return true;
7767 }
7768
7769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7770
7771 if (mData->mSession.mState != SessionState_Spawning)
7772 {
7773 /* nothing to do */
7774 LogFlowThisFunc(("Not spawning any more!\n"));
7775 return true;
7776 }
7777
7778 HRESULT rc = S_OK;
7779
7780 /* PID not yet initialized, skip check. */
7781 if (mData->mSession.mPID == NIL_RTPROCESS)
7782 return false;
7783
7784 RTPROCSTATUS status;
7785 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7786
7787 if (vrc != VERR_PROCESS_RUNNING)
7788 {
7789 Utf8Str strExtraInfo;
7790
7791#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7792 /* If the startup logfile exists and is of non-zero length, tell the
7793 user to look there for more details to encourage them to attach it
7794 when reporting startup issues. */
7795 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7796 uint64_t cbStartupLogFile = 0;
7797 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7798 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7799 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7800#endif
7801
7802 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7803 rc = setError(E_FAIL,
7804 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7805 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7806 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7807 rc = setError(E_FAIL,
7808 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7809 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7810 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7811 rc = setError(E_FAIL,
7812 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7813 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7814 else
7815 rc = setErrorBoth(E_FAIL, vrc,
7816 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7817 i_getName().c_str(), vrc, strExtraInfo.c_str());
7818 }
7819
7820 if (FAILED(rc))
7821 {
7822 /* Close the remote session, remove the remote control from the list
7823 * and reset session state to Closed (@note keep the code in sync with
7824 * the relevant part in LockMachine()). */
7825
7826 Assert(mData->mSession.mRemoteControls.size() == 1);
7827 if (mData->mSession.mRemoteControls.size() == 1)
7828 {
7829 ErrorInfoKeeper eik;
7830 mData->mSession.mRemoteControls.front()->Uninitialize();
7831 }
7832
7833 mData->mSession.mRemoteControls.clear();
7834 mData->mSession.mState = SessionState_Unlocked;
7835
7836 /* finalize the progress after setting the state */
7837 if (!mData->mSession.mProgress.isNull())
7838 {
7839 mData->mSession.mProgress->notifyComplete(rc);
7840 mData->mSession.mProgress.setNull();
7841 }
7842
7843 mData->mSession.mPID = NIL_RTPROCESS;
7844
7845 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7846 return true;
7847 }
7848
7849 return false;
7850}
7851
7852/**
7853 * Checks whether the machine can be registered. If so, commits and saves
7854 * all settings.
7855 *
7856 * @note Must be called from mParent's write lock. Locks this object and
7857 * children for writing.
7858 */
7859HRESULT Machine::i_prepareRegister()
7860{
7861 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7862
7863 AutoLimitedCaller autoCaller(this);
7864 AssertComRCReturnRC(autoCaller.rc());
7865
7866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7867
7868 /* wait for state dependents to drop to zero */
7869 i_ensureNoStateDependencies();
7870
7871 if (!mData->mAccessible)
7872 return setError(VBOX_E_INVALID_OBJECT_STATE,
7873 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7874 mUserData->s.strName.c_str(),
7875 mData->mUuid.toString().c_str());
7876
7877 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7878
7879 if (mData->mRegistered)
7880 return setError(VBOX_E_INVALID_OBJECT_STATE,
7881 tr("The machine '%s' with UUID {%s} is already registered"),
7882 mUserData->s.strName.c_str(),
7883 mData->mUuid.toString().c_str());
7884
7885 HRESULT rc = S_OK;
7886
7887 // Ensure the settings are saved. If we are going to be registered and
7888 // no config file exists yet, create it by calling i_saveSettings() too.
7889 if ( (mData->flModifications)
7890 || (!mData->pMachineConfigFile->fileExists())
7891 )
7892 {
7893 rc = i_saveSettings(NULL);
7894 // no need to check whether VirtualBox.xml needs saving too since
7895 // we can't have a machine XML file rename pending
7896 if (FAILED(rc)) return rc;
7897 }
7898
7899 /* more config checking goes here */
7900
7901 if (SUCCEEDED(rc))
7902 {
7903 /* we may have had implicit modifications we want to fix on success */
7904 i_commit();
7905
7906 mData->mRegistered = true;
7907 }
7908 else
7909 {
7910 /* we may have had implicit modifications we want to cancel on failure*/
7911 i_rollback(false /* aNotify */);
7912 }
7913
7914 return rc;
7915}
7916
7917/**
7918 * Increases the number of objects dependent on the machine state or on the
7919 * registered state. Guarantees that these two states will not change at least
7920 * until #i_releaseStateDependency() is called.
7921 *
7922 * Depending on the @a aDepType value, additional state checks may be made.
7923 * These checks will set extended error info on failure. See
7924 * #i_checkStateDependency() for more info.
7925 *
7926 * If this method returns a failure, the dependency is not added and the caller
7927 * is not allowed to rely on any particular machine state or registration state
7928 * value and may return the failed result code to the upper level.
7929 *
7930 * @param aDepType Dependency type to add.
7931 * @param aState Current machine state (NULL if not interested).
7932 * @param aRegistered Current registered state (NULL if not interested).
7933 *
7934 * @note Locks this object for writing.
7935 */
7936HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7937 MachineState_T *aState /* = NULL */,
7938 BOOL *aRegistered /* = NULL */)
7939{
7940 AutoCaller autoCaller(this);
7941 AssertComRCReturnRC(autoCaller.rc());
7942
7943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7944
7945 HRESULT rc = i_checkStateDependency(aDepType);
7946 if (FAILED(rc)) return rc;
7947
7948 {
7949 if (mData->mMachineStateChangePending != 0)
7950 {
7951 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7952 * drop to zero so don't add more. It may make sense to wait a bit
7953 * and retry before reporting an error (since the pending state
7954 * transition should be really quick) but let's just assert for
7955 * now to see if it ever happens on practice. */
7956
7957 AssertFailed();
7958
7959 return setError(E_ACCESSDENIED,
7960 tr("Machine state change is in progress. Please retry the operation later."));
7961 }
7962
7963 ++mData->mMachineStateDeps;
7964 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7965 }
7966
7967 if (aState)
7968 *aState = mData->mMachineState;
7969 if (aRegistered)
7970 *aRegistered = mData->mRegistered;
7971
7972 return S_OK;
7973}
7974
7975/**
7976 * Decreases the number of objects dependent on the machine state.
7977 * Must always complete the #i_addStateDependency() call after the state
7978 * dependency is no more necessary.
7979 */
7980void Machine::i_releaseStateDependency()
7981{
7982 AutoCaller autoCaller(this);
7983 AssertComRCReturnVoid(autoCaller.rc());
7984
7985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7986
7987 /* releaseStateDependency() w/o addStateDependency()? */
7988 AssertReturnVoid(mData->mMachineStateDeps != 0);
7989 -- mData->mMachineStateDeps;
7990
7991 if (mData->mMachineStateDeps == 0)
7992 {
7993 /* inform i_ensureNoStateDependencies() that there are no more deps */
7994 if (mData->mMachineStateChangePending != 0)
7995 {
7996 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7997 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7998 }
7999 }
8000}
8001
8002Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8003{
8004 /* start with nothing found */
8005 Utf8Str strResult("");
8006
8007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8008
8009 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8010 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8011 // found:
8012 strResult = it->second; // source is a Utf8Str
8013
8014 return strResult;
8015}
8016
8017// protected methods
8018/////////////////////////////////////////////////////////////////////////////
8019
8020/**
8021 * Performs machine state checks based on the @a aDepType value. If a check
8022 * fails, this method will set extended error info, otherwise it will return
8023 * S_OK. It is supposed, that on failure, the caller will immediately return
8024 * the return value of this method to the upper level.
8025 *
8026 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8027 *
8028 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8029 * current state of this machine object allows to change settings of the
8030 * machine (i.e. the machine is not registered, or registered but not running
8031 * and not saved). It is useful to call this method from Machine setters
8032 * before performing any change.
8033 *
8034 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8035 * as for MutableStateDep except that if the machine is saved, S_OK is also
8036 * returned. This is useful in setters which allow changing machine
8037 * properties when it is in the saved state.
8038 *
8039 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8040 * if the current state of this machine object allows to change runtime
8041 * changeable settings of the machine (i.e. the machine is not registered, or
8042 * registered but either running or not running and not saved). It is useful
8043 * to call this method from Machine setters before performing any changes to
8044 * runtime changeable settings.
8045 *
8046 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8047 * the same as for MutableOrRunningStateDep except that if the machine is
8048 * saved, S_OK is also returned. This is useful in setters which allow
8049 * changing runtime and saved state changeable machine properties.
8050 *
8051 * @param aDepType Dependency type to check.
8052 *
8053 * @note Non Machine based classes should use #i_addStateDependency() and
8054 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8055 * template.
8056 *
8057 * @note This method must be called from under this object's read or write
8058 * lock.
8059 */
8060HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8061{
8062 switch (aDepType)
8063 {
8064 case AnyStateDep:
8065 {
8066 break;
8067 }
8068 case MutableStateDep:
8069 {
8070 if ( mData->mRegistered
8071 && ( !i_isSessionMachine()
8072 || ( mData->mMachineState != MachineState_Aborted
8073 && mData->mMachineState != MachineState_Teleported
8074 && mData->mMachineState != MachineState_PoweredOff
8075 )
8076 )
8077 )
8078 return setError(VBOX_E_INVALID_VM_STATE,
8079 tr("The machine is not mutable (state is %s)"),
8080 Global::stringifyMachineState(mData->mMachineState));
8081 break;
8082 }
8083 case MutableOrSavedStateDep:
8084 {
8085 if ( mData->mRegistered
8086 && ( !i_isSessionMachine()
8087 || ( mData->mMachineState != MachineState_Aborted
8088 && mData->mMachineState != MachineState_Teleported
8089 && mData->mMachineState != MachineState_Saved
8090 && mData->mMachineState != MachineState_PoweredOff
8091 )
8092 )
8093 )
8094 return setError(VBOX_E_INVALID_VM_STATE,
8095 tr("The machine is not mutable or saved (state is %s)"),
8096 Global::stringifyMachineState(mData->mMachineState));
8097 break;
8098 }
8099 case MutableOrRunningStateDep:
8100 {
8101 if ( mData->mRegistered
8102 && ( !i_isSessionMachine()
8103 || ( mData->mMachineState != MachineState_Aborted
8104 && mData->mMachineState != MachineState_Teleported
8105 && mData->mMachineState != MachineState_PoweredOff
8106 && !Global::IsOnline(mData->mMachineState)
8107 )
8108 )
8109 )
8110 return setError(VBOX_E_INVALID_VM_STATE,
8111 tr("The machine is not mutable or running (state is %s)"),
8112 Global::stringifyMachineState(mData->mMachineState));
8113 break;
8114 }
8115 case MutableOrSavedOrRunningStateDep:
8116 {
8117 if ( mData->mRegistered
8118 && ( !i_isSessionMachine()
8119 || ( mData->mMachineState != MachineState_Aborted
8120 && mData->mMachineState != MachineState_Teleported
8121 && mData->mMachineState != MachineState_Saved
8122 && mData->mMachineState != MachineState_PoweredOff
8123 && !Global::IsOnline(mData->mMachineState)
8124 )
8125 )
8126 )
8127 return setError(VBOX_E_INVALID_VM_STATE,
8128 tr("The machine is not mutable, saved or running (state is %s)"),
8129 Global::stringifyMachineState(mData->mMachineState));
8130 break;
8131 }
8132 }
8133
8134 return S_OK;
8135}
8136
8137/**
8138 * Helper to initialize all associated child objects and allocate data
8139 * structures.
8140 *
8141 * This method must be called as a part of the object's initialization procedure
8142 * (usually done in the #init() method).
8143 *
8144 * @note Must be called only from #init() or from #i_registeredInit().
8145 */
8146HRESULT Machine::initDataAndChildObjects()
8147{
8148 AutoCaller autoCaller(this);
8149 AssertComRCReturnRC(autoCaller.rc());
8150 AssertReturn( getObjectState().getState() == ObjectState::InInit
8151 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8152
8153 AssertReturn(!mData->mAccessible, E_FAIL);
8154
8155 /* allocate data structures */
8156 mSSData.allocate();
8157 mUserData.allocate();
8158 mHWData.allocate();
8159 mMediumAttachments.allocate();
8160 mStorageControllers.allocate();
8161 mUSBControllers.allocate();
8162
8163 /* initialize mOSTypeId */
8164 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8165
8166/** @todo r=bird: init() methods never fails, right? Why don't we make them
8167 * return void then! */
8168
8169 /* create associated BIOS settings object */
8170 unconst(mBIOSSettings).createObject();
8171 mBIOSSettings->init(this);
8172
8173 /* create associated trusted platform module object */
8174 unconst(mTrustedPlatformModule).createObject();
8175 mTrustedPlatformModule->init(this);
8176
8177 /* create associated record settings object */
8178 unconst(mRecordingSettings).createObject();
8179 mRecordingSettings->init(this);
8180
8181 /* create the graphics adapter object (always present) */
8182 unconst(mGraphicsAdapter).createObject();
8183 mGraphicsAdapter->init(this);
8184
8185 /* create an associated VRDE object (default is disabled) */
8186 unconst(mVRDEServer).createObject();
8187 mVRDEServer->init(this);
8188
8189 /* create associated serial port objects */
8190 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8191 {
8192 unconst(mSerialPorts[slot]).createObject();
8193 mSerialPorts[slot]->init(this, slot);
8194 }
8195
8196 /* create associated parallel port objects */
8197 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8198 {
8199 unconst(mParallelPorts[slot]).createObject();
8200 mParallelPorts[slot]->init(this, slot);
8201 }
8202
8203 /* create the audio adapter object (always present, default is disabled) */
8204 unconst(mAudioAdapter).createObject();
8205 mAudioAdapter->init(this);
8206
8207 /* create the USB device filters object (always present) */
8208 unconst(mUSBDeviceFilters).createObject();
8209 mUSBDeviceFilters->init(this);
8210
8211 /* create associated network adapter objects */
8212 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8213 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8214 {
8215 unconst(mNetworkAdapters[slot]).createObject();
8216 mNetworkAdapters[slot]->init(this, slot);
8217 }
8218
8219 /* create the bandwidth control */
8220 unconst(mBandwidthControl).createObject();
8221 mBandwidthControl->init(this);
8222
8223 return S_OK;
8224}
8225
8226/**
8227 * Helper to uninitialize all associated child objects and to free all data
8228 * structures.
8229 *
8230 * This method must be called as a part of the object's uninitialization
8231 * procedure (usually done in the #uninit() method).
8232 *
8233 * @note Must be called only from #uninit() or from #i_registeredInit().
8234 */
8235void Machine::uninitDataAndChildObjects()
8236{
8237 AutoCaller autoCaller(this);
8238 AssertComRCReturnVoid(autoCaller.rc());
8239 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8240 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8241 || getObjectState().getState() == ObjectState::InUninit
8242 || getObjectState().getState() == ObjectState::Limited);
8243
8244 /* tell all our other child objects we've been uninitialized */
8245 if (mBandwidthControl)
8246 {
8247 mBandwidthControl->uninit();
8248 unconst(mBandwidthControl).setNull();
8249 }
8250
8251 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8252 {
8253 if (mNetworkAdapters[slot])
8254 {
8255 mNetworkAdapters[slot]->uninit();
8256 unconst(mNetworkAdapters[slot]).setNull();
8257 }
8258 }
8259
8260 if (mUSBDeviceFilters)
8261 {
8262 mUSBDeviceFilters->uninit();
8263 unconst(mUSBDeviceFilters).setNull();
8264 }
8265
8266 if (mAudioAdapter)
8267 {
8268 mAudioAdapter->uninit();
8269 unconst(mAudioAdapter).setNull();
8270 }
8271
8272 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8273 {
8274 if (mParallelPorts[slot])
8275 {
8276 mParallelPorts[slot]->uninit();
8277 unconst(mParallelPorts[slot]).setNull();
8278 }
8279 }
8280
8281 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8282 {
8283 if (mSerialPorts[slot])
8284 {
8285 mSerialPorts[slot]->uninit();
8286 unconst(mSerialPorts[slot]).setNull();
8287 }
8288 }
8289
8290 if (mVRDEServer)
8291 {
8292 mVRDEServer->uninit();
8293 unconst(mVRDEServer).setNull();
8294 }
8295
8296 if (mGraphicsAdapter)
8297 {
8298 mGraphicsAdapter->uninit();
8299 unconst(mGraphicsAdapter).setNull();
8300 }
8301
8302 if (mBIOSSettings)
8303 {
8304 mBIOSSettings->uninit();
8305 unconst(mBIOSSettings).setNull();
8306 }
8307
8308 if (mTrustedPlatformModule)
8309 {
8310 mTrustedPlatformModule->uninit();
8311 unconst(mTrustedPlatformModule).setNull();
8312 }
8313
8314 if (mRecordingSettings)
8315 {
8316 mRecordingSettings->uninit();
8317 unconst(mRecordingSettings).setNull();
8318 }
8319
8320 /* Deassociate media (only when a real Machine or a SnapshotMachine
8321 * instance is uninitialized; SessionMachine instances refer to real
8322 * Machine media). This is necessary for a clean re-initialization of
8323 * the VM after successfully re-checking the accessibility state. Note
8324 * that in case of normal Machine or SnapshotMachine uninitialization (as
8325 * a result of unregistering or deleting the snapshot), outdated media
8326 * attachments will already be uninitialized and deleted, so this
8327 * code will not affect them. */
8328 if ( !mMediumAttachments.isNull()
8329 && !i_isSessionMachine()
8330 )
8331 {
8332 for (MediumAttachmentList::const_iterator
8333 it = mMediumAttachments->begin();
8334 it != mMediumAttachments->end();
8335 ++it)
8336 {
8337 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8338 if (pMedium.isNull())
8339 continue;
8340 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8341 AssertComRC(rc);
8342 }
8343 }
8344
8345 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8346 {
8347 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8348 if (mData->mFirstSnapshot)
8349 {
8350 // snapshots tree is protected by machine write lock; strictly
8351 // this isn't necessary here since we're deleting the entire
8352 // machine, but otherwise we assert in Snapshot::uninit()
8353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8354 mData->mFirstSnapshot->uninit();
8355 mData->mFirstSnapshot.setNull();
8356 }
8357
8358 mData->mCurrentSnapshot.setNull();
8359 }
8360
8361 /* free data structures (the essential mData structure is not freed here
8362 * since it may be still in use) */
8363 mMediumAttachments.free();
8364 mStorageControllers.free();
8365 mUSBControllers.free();
8366 mHWData.free();
8367 mUserData.free();
8368 mSSData.free();
8369}
8370
8371/**
8372 * Returns a pointer to the Machine object for this machine that acts like a
8373 * parent for complex machine data objects such as shared folders, etc.
8374 *
8375 * For primary Machine objects and for SnapshotMachine objects, returns this
8376 * object's pointer itself. For SessionMachine objects, returns the peer
8377 * (primary) machine pointer.
8378 */
8379Machine *Machine::i_getMachine()
8380{
8381 if (i_isSessionMachine())
8382 return (Machine*)mPeer;
8383 return this;
8384}
8385
8386/**
8387 * Makes sure that there are no machine state dependents. If necessary, waits
8388 * for the number of dependents to drop to zero.
8389 *
8390 * Make sure this method is called from under this object's write lock to
8391 * guarantee that no new dependents may be added when this method returns
8392 * control to the caller.
8393 *
8394 * @note Locks this object for writing. The lock will be released while waiting
8395 * (if necessary).
8396 *
8397 * @warning To be used only in methods that change the machine state!
8398 */
8399void Machine::i_ensureNoStateDependencies()
8400{
8401 AssertReturnVoid(isWriteLockOnCurrentThread());
8402
8403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8404
8405 /* Wait for all state dependents if necessary */
8406 if (mData->mMachineStateDeps != 0)
8407 {
8408 /* lazy semaphore creation */
8409 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8410 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8411
8412 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8413 mData->mMachineStateDeps));
8414
8415 ++mData->mMachineStateChangePending;
8416
8417 /* reset the semaphore before waiting, the last dependent will signal
8418 * it */
8419 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8420
8421 alock.release();
8422
8423 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8424
8425 alock.acquire();
8426
8427 -- mData->mMachineStateChangePending;
8428 }
8429}
8430
8431/**
8432 * Changes the machine state and informs callbacks.
8433 *
8434 * This method is not intended to fail so it either returns S_OK or asserts (and
8435 * returns a failure).
8436 *
8437 * @note Locks this object for writing.
8438 */
8439HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8440{
8441 LogFlowThisFuncEnter();
8442 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8443 Assert(aMachineState != MachineState_Null);
8444
8445 AutoCaller autoCaller(this);
8446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8447
8448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8449
8450 /* wait for state dependents to drop to zero */
8451 i_ensureNoStateDependencies();
8452
8453 MachineState_T const enmOldState = mData->mMachineState;
8454 if (enmOldState != aMachineState)
8455 {
8456 mData->mMachineState = aMachineState;
8457 RTTimeNow(&mData->mLastStateChange);
8458
8459#ifdef VBOX_WITH_DTRACE_R3_MAIN
8460 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8461#endif
8462 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8463 }
8464
8465 LogFlowThisFuncLeave();
8466 return S_OK;
8467}
8468
8469/**
8470 * Searches for a shared folder with the given logical name
8471 * in the collection of shared folders.
8472 *
8473 * @param aName logical name of the shared folder
8474 * @param aSharedFolder where to return the found object
8475 * @param aSetError whether to set the error info if the folder is
8476 * not found
8477 * @return
8478 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8479 *
8480 * @note
8481 * must be called from under the object's lock!
8482 */
8483HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8484 ComObjPtr<SharedFolder> &aSharedFolder,
8485 bool aSetError /* = false */)
8486{
8487 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8488 for (HWData::SharedFolderList::const_iterator
8489 it = mHWData->mSharedFolders.begin();
8490 it != mHWData->mSharedFolders.end();
8491 ++it)
8492 {
8493 SharedFolder *pSF = *it;
8494 AutoCaller autoCaller(pSF);
8495 if (pSF->i_getName() == aName)
8496 {
8497 aSharedFolder = pSF;
8498 rc = S_OK;
8499 break;
8500 }
8501 }
8502
8503 if (aSetError && FAILED(rc))
8504 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8505
8506 return rc;
8507}
8508
8509/**
8510 * Initializes all machine instance data from the given settings structures
8511 * from XML. The exception is the machine UUID which needs special handling
8512 * depending on the caller's use case, so the caller needs to set that herself.
8513 *
8514 * This gets called in several contexts during machine initialization:
8515 *
8516 * -- When machine XML exists on disk already and needs to be loaded into memory,
8517 * for example, from #i_registeredInit() to load all registered machines on
8518 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8519 * attached to the machine should be part of some media registry already.
8520 *
8521 * -- During OVF import, when a machine config has been constructed from an
8522 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8523 * ensure that the media listed as attachments in the config (which have
8524 * been imported from the OVF) receive the correct registry ID.
8525 *
8526 * -- During VM cloning.
8527 *
8528 * @param config Machine settings from XML.
8529 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8530 * for each attached medium in the config.
8531 * @return
8532 */
8533HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8534 const Guid *puuidRegistry)
8535{
8536 // copy name, description, OS type, teleporter, UTC etc.
8537 mUserData->s = config.machineUserData;
8538
8539 // look up the object by Id to check it is valid
8540 ComObjPtr<GuestOSType> pGuestOSType;
8541 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8542 if (!pGuestOSType.isNull())
8543 mUserData->s.strOsType = pGuestOSType->i_id();
8544
8545 // stateFile (optional)
8546 if (config.strStateFile.isEmpty())
8547 mSSData->strStateFilePath.setNull();
8548 else
8549 {
8550 Utf8Str stateFilePathFull(config.strStateFile);
8551 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8552 if (RT_FAILURE(vrc))
8553 return setErrorBoth(E_FAIL, vrc,
8554 tr("Invalid saved state file path '%s' (%Rrc)"),
8555 config.strStateFile.c_str(),
8556 vrc);
8557 mSSData->strStateFilePath = stateFilePathFull;
8558 }
8559
8560 // snapshot folder needs special processing so set it again
8561 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8562 if (FAILED(rc)) return rc;
8563
8564 /* Copy the extra data items (config may or may not be the same as
8565 * mData->pMachineConfigFile) if necessary. When loading the XML files
8566 * from disk they are the same, but not for OVF import. */
8567 if (mData->pMachineConfigFile != &config)
8568 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8569
8570 /* currentStateModified (optional, default is true) */
8571 mData->mCurrentStateModified = config.fCurrentStateModified;
8572
8573 mData->mLastStateChange = config.timeLastStateChange;
8574
8575 /*
8576 * note: all mUserData members must be assigned prior this point because
8577 * we need to commit changes in order to let mUserData be shared by all
8578 * snapshot machine instances.
8579 */
8580 mUserData.commitCopy();
8581
8582 // machine registry, if present (must be loaded before snapshots)
8583 if (config.canHaveOwnMediaRegistry())
8584 {
8585 // determine machine folder
8586 Utf8Str strMachineFolder = i_getSettingsFileFull();
8587 strMachineFolder.stripFilename();
8588 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8589 config.mediaRegistry,
8590 strMachineFolder);
8591 if (FAILED(rc)) return rc;
8592 }
8593
8594 /* Snapshot node (optional) */
8595 size_t cRootSnapshots;
8596 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8597 {
8598 // there must be only one root snapshot
8599 Assert(cRootSnapshots == 1);
8600
8601 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8602
8603 rc = i_loadSnapshot(snap,
8604 config.uuidCurrentSnapshot,
8605 NULL); // no parent == first snapshot
8606 if (FAILED(rc)) return rc;
8607 }
8608
8609 // hardware data
8610 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8611 if (FAILED(rc)) return rc;
8612
8613 /*
8614 * NOTE: the assignment below must be the last thing to do,
8615 * otherwise it will be not possible to change the settings
8616 * somewhere in the code above because all setters will be
8617 * blocked by i_checkStateDependency(MutableStateDep).
8618 */
8619
8620 /* set the machine state to Aborted or Saved when appropriate */
8621 if (config.fAborted)
8622 {
8623 mSSData->strStateFilePath.setNull();
8624
8625 /* no need to use i_setMachineState() during init() */
8626 mData->mMachineState = MachineState_Aborted;
8627 }
8628 else if (!mSSData->strStateFilePath.isEmpty())
8629 {
8630 /* no need to use i_setMachineState() during init() */
8631 mData->mMachineState = MachineState_Saved;
8632 }
8633
8634 // after loading settings, we are no longer different from the XML on disk
8635 mData->flModifications = 0;
8636
8637 return S_OK;
8638}
8639
8640/**
8641 * Recursively loads all snapshots starting from the given.
8642 *
8643 * @param data snapshot settings.
8644 * @param aCurSnapshotId Current snapshot ID from the settings file.
8645 * @param aParentSnapshot Parent snapshot.
8646 */
8647HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8648 const Guid &aCurSnapshotId,
8649 Snapshot *aParentSnapshot)
8650{
8651 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8652 AssertReturn(!i_isSessionMachine(), E_FAIL);
8653
8654 HRESULT rc = S_OK;
8655
8656 Utf8Str strStateFile;
8657 if (!data.strStateFile.isEmpty())
8658 {
8659 /* optional */
8660 strStateFile = data.strStateFile;
8661 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8662 if (RT_FAILURE(vrc))
8663 return setErrorBoth(E_FAIL, vrc,
8664 tr("Invalid saved state file path '%s' (%Rrc)"),
8665 strStateFile.c_str(),
8666 vrc);
8667 }
8668
8669 /* create a snapshot machine object */
8670 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8671 pSnapshotMachine.createObject();
8672 rc = pSnapshotMachine->initFromSettings(this,
8673 data.hardware,
8674 &data.debugging,
8675 &data.autostart,
8676 data.uuid.ref(),
8677 strStateFile);
8678 if (FAILED(rc)) return rc;
8679
8680 /* create a snapshot object */
8681 ComObjPtr<Snapshot> pSnapshot;
8682 pSnapshot.createObject();
8683 /* initialize the snapshot */
8684 rc = pSnapshot->init(mParent, // VirtualBox object
8685 data.uuid,
8686 data.strName,
8687 data.strDescription,
8688 data.timestamp,
8689 pSnapshotMachine,
8690 aParentSnapshot);
8691 if (FAILED(rc)) return rc;
8692
8693 /* memorize the first snapshot if necessary */
8694 if (!mData->mFirstSnapshot)
8695 mData->mFirstSnapshot = pSnapshot;
8696
8697 /* memorize the current snapshot when appropriate */
8698 if ( !mData->mCurrentSnapshot
8699 && pSnapshot->i_getId() == aCurSnapshotId
8700 )
8701 mData->mCurrentSnapshot = pSnapshot;
8702
8703 // now create the children
8704 for (settings::SnapshotsList::const_iterator
8705 it = data.llChildSnapshots.begin();
8706 it != data.llChildSnapshots.end();
8707 ++it)
8708 {
8709 const settings::Snapshot &childData = *it;
8710 // recurse
8711 rc = i_loadSnapshot(childData,
8712 aCurSnapshotId,
8713 pSnapshot); // parent = the one we created above
8714 if (FAILED(rc)) return rc;
8715 }
8716
8717 return rc;
8718}
8719
8720/**
8721 * Loads settings into mHWData.
8722 *
8723 * @param puuidRegistry Registry ID.
8724 * @param puuidSnapshot Snapshot ID
8725 * @param data Reference to the hardware settings.
8726 * @param pDbg Pointer to the debugging settings.
8727 * @param pAutostart Pointer to the autostart settings.
8728 */
8729HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8730 const Guid *puuidSnapshot,
8731 const settings::Hardware &data,
8732 const settings::Debugging *pDbg,
8733 const settings::Autostart *pAutostart)
8734{
8735 AssertReturn(!i_isSessionMachine(), E_FAIL);
8736
8737 HRESULT rc = S_OK;
8738
8739 try
8740 {
8741 ComObjPtr<GuestOSType> pGuestOSType;
8742 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8743
8744 /* The hardware version attribute (optional). */
8745 mHWData->mHWVersion = data.strVersion;
8746 mHWData->mHardwareUUID = data.uuid;
8747
8748 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8749 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8750 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8751 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8752 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8753 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8754 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8755 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8756 mHWData->mPAEEnabled = data.fPAE;
8757 mHWData->mLongMode = data.enmLongMode;
8758 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8759 mHWData->mAPIC = data.fAPIC;
8760 mHWData->mX2APIC = data.fX2APIC;
8761 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8762 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8763 mHWData->mSpecCtrl = data.fSpecCtrl;
8764 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8765 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8766 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8767 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8768 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8769 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8770 mHWData->mCPUCount = data.cCPUs;
8771 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8772 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8773 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8774 mHWData->mCpuProfile = data.strCpuProfile;
8775
8776 // cpu
8777 if (mHWData->mCPUHotPlugEnabled)
8778 {
8779 for (settings::CpuList::const_iterator
8780 it = data.llCpus.begin();
8781 it != data.llCpus.end();
8782 ++it)
8783 {
8784 const settings::Cpu &cpu = *it;
8785
8786 mHWData->mCPUAttached[cpu.ulId] = true;
8787 }
8788 }
8789
8790 // cpuid leafs
8791 for (settings::CpuIdLeafsList::const_iterator
8792 it = data.llCpuIdLeafs.begin();
8793 it != data.llCpuIdLeafs.end();
8794 ++it)
8795 {
8796 const settings::CpuIdLeaf &rLeaf= *it;
8797 if ( rLeaf.idx < UINT32_C(0x20)
8798 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8799 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8800 mHWData->mCpuIdLeafList.push_back(rLeaf);
8801 /* else: just ignore */
8802 }
8803
8804 mHWData->mMemorySize = data.ulMemorySizeMB;
8805 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8806
8807 // boot order
8808 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8809 {
8810 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8811 if (it == data.mapBootOrder.end())
8812 mHWData->mBootOrder[i] = DeviceType_Null;
8813 else
8814 mHWData->mBootOrder[i] = it->second;
8815 }
8816
8817 mHWData->mFirmwareType = data.firmwareType;
8818 mHWData->mPointingHIDType = data.pointingHIDType;
8819 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8820 mHWData->mChipsetType = data.chipsetType;
8821 mHWData->mIommuType = data.iommuType;
8822 mHWData->mParavirtProvider = data.paravirtProvider;
8823 mHWData->mParavirtDebug = data.strParavirtDebug;
8824 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8825 mHWData->mHPETEnabled = data.fHPETEnabled;
8826
8827 /* GraphicsAdapter */
8828 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8829 if (FAILED(rc)) return rc;
8830
8831 /* VRDEServer */
8832 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8833 if (FAILED(rc)) return rc;
8834
8835 /* BIOS */
8836 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8837 if (FAILED(rc)) return rc;
8838
8839 /* Trusted Platform Module */
8840 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8841 if (FAILED(rc)) return rc;
8842
8843 /* Recording settings */
8844 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8845 if (FAILED(rc)) return rc;
8846
8847 // Bandwidth control (must come before network adapters)
8848 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8849 if (FAILED(rc)) return rc;
8850
8851 /* USB controllers */
8852 for (settings::USBControllerList::const_iterator
8853 it = data.usbSettings.llUSBControllers.begin();
8854 it != data.usbSettings.llUSBControllers.end();
8855 ++it)
8856 {
8857 const settings::USBController &settingsCtrl = *it;
8858 ComObjPtr<USBController> newCtrl;
8859
8860 newCtrl.createObject();
8861 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8862 mUSBControllers->push_back(newCtrl);
8863 }
8864
8865 /* USB device filters */
8866 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8867 if (FAILED(rc)) return rc;
8868
8869 // network adapters (establish array size first and apply defaults, to
8870 // ensure reading the same settings as we saved, since the list skips
8871 // adapters having defaults)
8872 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8873 size_t oldCount = mNetworkAdapters.size();
8874 if (newCount > oldCount)
8875 {
8876 mNetworkAdapters.resize(newCount);
8877 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8878 {
8879 unconst(mNetworkAdapters[slot]).createObject();
8880 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8881 }
8882 }
8883 else if (newCount < oldCount)
8884 mNetworkAdapters.resize(newCount);
8885 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8886 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8887 for (settings::NetworkAdaptersList::const_iterator
8888 it = data.llNetworkAdapters.begin();
8889 it != data.llNetworkAdapters.end();
8890 ++it)
8891 {
8892 const settings::NetworkAdapter &nic = *it;
8893
8894 /* slot uniqueness is guaranteed by XML Schema */
8895 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8896 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8897 if (FAILED(rc)) return rc;
8898 }
8899
8900 // serial ports (establish defaults first, to ensure reading the same
8901 // settings as we saved, since the list skips ports having defaults)
8902 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8903 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8904 for (settings::SerialPortsList::const_iterator
8905 it = data.llSerialPorts.begin();
8906 it != data.llSerialPorts.end();
8907 ++it)
8908 {
8909 const settings::SerialPort &s = *it;
8910
8911 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8912 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8913 if (FAILED(rc)) return rc;
8914 }
8915
8916 // parallel ports (establish defaults first, to ensure reading the same
8917 // settings as we saved, since the list skips ports having defaults)
8918 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8919 mParallelPorts[i]->i_applyDefaults();
8920 for (settings::ParallelPortsList::const_iterator
8921 it = data.llParallelPorts.begin();
8922 it != data.llParallelPorts.end();
8923 ++it)
8924 {
8925 const settings::ParallelPort &p = *it;
8926
8927 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8928 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8929 if (FAILED(rc)) return rc;
8930 }
8931
8932 /* AudioAdapter */
8933 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8934 if (FAILED(rc)) return rc;
8935
8936 /* storage controllers */
8937 rc = i_loadStorageControllers(data.storage,
8938 puuidRegistry,
8939 puuidSnapshot);
8940 if (FAILED(rc)) return rc;
8941
8942 /* Shared folders */
8943 for (settings::SharedFoldersList::const_iterator
8944 it = data.llSharedFolders.begin();
8945 it != data.llSharedFolders.end();
8946 ++it)
8947 {
8948 const settings::SharedFolder &sf = *it;
8949
8950 ComObjPtr<SharedFolder> sharedFolder;
8951 /* Check for double entries. Not allowed! */
8952 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8953 if (SUCCEEDED(rc))
8954 return setError(VBOX_E_OBJECT_IN_USE,
8955 tr("Shared folder named '%s' already exists"),
8956 sf.strName.c_str());
8957
8958 /* Create the new shared folder. Don't break on error. This will be
8959 * reported when the machine starts. */
8960 sharedFolder.createObject();
8961 rc = sharedFolder->init(i_getMachine(),
8962 sf.strName,
8963 sf.strHostPath,
8964 RT_BOOL(sf.fWritable),
8965 RT_BOOL(sf.fAutoMount),
8966 sf.strAutoMountPoint,
8967 false /* fFailOnError */);
8968 if (FAILED(rc)) return rc;
8969 mHWData->mSharedFolders.push_back(sharedFolder);
8970 }
8971
8972 // Clipboard
8973 mHWData->mClipboardMode = data.clipboardMode;
8974 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8975
8976 // drag'n'drop
8977 mHWData->mDnDMode = data.dndMode;
8978
8979 // guest settings
8980 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8981
8982 // IO settings
8983 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8984 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8985
8986 // Host PCI devices
8987 for (settings::HostPCIDeviceAttachmentList::const_iterator
8988 it = data.pciAttachments.begin();
8989 it != data.pciAttachments.end();
8990 ++it)
8991 {
8992 const settings::HostPCIDeviceAttachment &hpda = *it;
8993 ComObjPtr<PCIDeviceAttachment> pda;
8994
8995 pda.createObject();
8996 pda->i_loadSettings(this, hpda);
8997 mHWData->mPCIDeviceAssignments.push_back(pda);
8998 }
8999
9000 /*
9001 * (The following isn't really real hardware, but it lives in HWData
9002 * for reasons of convenience.)
9003 */
9004
9005#ifdef VBOX_WITH_GUEST_PROPS
9006 /* Guest properties (optional) */
9007
9008 /* Only load transient guest properties for configs which have saved
9009 * state, because there shouldn't be any for powered off VMs. The same
9010 * logic applies for snapshots, as offline snapshots shouldn't have
9011 * any such properties. They confuse the code in various places.
9012 * Note: can't rely on the machine state, as it isn't set yet. */
9013 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9014 /* apologies for the hacky unconst() usage, but this needs hacking
9015 * actually inconsistent settings into consistency, otherwise there
9016 * will be some corner cases where the inconsistency survives
9017 * surprisingly long without getting fixed, especially for snapshots
9018 * as there are no config changes. */
9019 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9020 for (settings::GuestPropertiesList::iterator
9021 it = llGuestProperties.begin();
9022 it != llGuestProperties.end();
9023 /*nothing*/)
9024 {
9025 const settings::GuestProperty &prop = *it;
9026 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9027 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9028 if ( fSkipTransientGuestProperties
9029 && ( fFlags & GUEST_PROP_F_TRANSIENT
9030 || fFlags & GUEST_PROP_F_TRANSRESET))
9031 {
9032 it = llGuestProperties.erase(it);
9033 continue;
9034 }
9035 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9036 mHWData->mGuestProperties[prop.strName] = property;
9037 ++it;
9038 }
9039#endif /* VBOX_WITH_GUEST_PROPS defined */
9040
9041 rc = i_loadDebugging(pDbg);
9042 if (FAILED(rc))
9043 return rc;
9044
9045 mHWData->mAutostart = *pAutostart;
9046
9047 /* default frontend */
9048 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9049 }
9050 catch (std::bad_alloc &)
9051 {
9052 return E_OUTOFMEMORY;
9053 }
9054
9055 AssertComRC(rc);
9056 return rc;
9057}
9058
9059/**
9060 * Called from i_loadHardware() to load the debugging settings of the
9061 * machine.
9062 *
9063 * @param pDbg Pointer to the settings.
9064 */
9065HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9066{
9067 mHWData->mDebugging = *pDbg;
9068 /* no more processing currently required, this will probably change. */
9069 return S_OK;
9070}
9071
9072/**
9073 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9074 *
9075 * @param data storage settings.
9076 * @param puuidRegistry media registry ID to set media to or NULL;
9077 * see Machine::i_loadMachineDataFromSettings()
9078 * @param puuidSnapshot snapshot ID
9079 * @return
9080 */
9081HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9082 const Guid *puuidRegistry,
9083 const Guid *puuidSnapshot)
9084{
9085 AssertReturn(!i_isSessionMachine(), E_FAIL);
9086
9087 HRESULT rc = S_OK;
9088
9089 for (settings::StorageControllersList::const_iterator
9090 it = data.llStorageControllers.begin();
9091 it != data.llStorageControllers.end();
9092 ++it)
9093 {
9094 const settings::StorageController &ctlData = *it;
9095
9096 ComObjPtr<StorageController> pCtl;
9097 /* Try to find one with the name first. */
9098 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9099 if (SUCCEEDED(rc))
9100 return setError(VBOX_E_OBJECT_IN_USE,
9101 tr("Storage controller named '%s' already exists"),
9102 ctlData.strName.c_str());
9103
9104 pCtl.createObject();
9105 rc = pCtl->init(this,
9106 ctlData.strName,
9107 ctlData.storageBus,
9108 ctlData.ulInstance,
9109 ctlData.fBootable);
9110 if (FAILED(rc)) return rc;
9111
9112 mStorageControllers->push_back(pCtl);
9113
9114 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9115 if (FAILED(rc)) return rc;
9116
9117 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9118 if (FAILED(rc)) return rc;
9119
9120 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9121 if (FAILED(rc)) return rc;
9122
9123 /* Load the attached devices now. */
9124 rc = i_loadStorageDevices(pCtl,
9125 ctlData,
9126 puuidRegistry,
9127 puuidSnapshot);
9128 if (FAILED(rc)) return rc;
9129 }
9130
9131 return S_OK;
9132}
9133
9134/**
9135 * Called from i_loadStorageControllers for a controller's devices.
9136 *
9137 * @param aStorageController
9138 * @param data
9139 * @param puuidRegistry media registry ID to set media to or NULL; see
9140 * Machine::i_loadMachineDataFromSettings()
9141 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9142 * @return
9143 */
9144HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9145 const settings::StorageController &data,
9146 const Guid *puuidRegistry,
9147 const Guid *puuidSnapshot)
9148{
9149 HRESULT rc = S_OK;
9150
9151 /* paranoia: detect duplicate attachments */
9152 for (settings::AttachedDevicesList::const_iterator
9153 it = data.llAttachedDevices.begin();
9154 it != data.llAttachedDevices.end();
9155 ++it)
9156 {
9157 const settings::AttachedDevice &ad = *it;
9158
9159 for (settings::AttachedDevicesList::const_iterator it2 = it;
9160 it2 != data.llAttachedDevices.end();
9161 ++it2)
9162 {
9163 if (it == it2)
9164 continue;
9165
9166 const settings::AttachedDevice &ad2 = *it2;
9167
9168 if ( ad.lPort == ad2.lPort
9169 && ad.lDevice == ad2.lDevice)
9170 {
9171 return setError(E_FAIL,
9172 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9173 aStorageController->i_getName().c_str(),
9174 ad.lPort,
9175 ad.lDevice,
9176 mUserData->s.strName.c_str());
9177 }
9178 }
9179 }
9180
9181 for (settings::AttachedDevicesList::const_iterator
9182 it = data.llAttachedDevices.begin();
9183 it != data.llAttachedDevices.end();
9184 ++it)
9185 {
9186 const settings::AttachedDevice &dev = *it;
9187 ComObjPtr<Medium> medium;
9188
9189 switch (dev.deviceType)
9190 {
9191 case DeviceType_Floppy:
9192 case DeviceType_DVD:
9193 if (dev.strHostDriveSrc.isNotEmpty())
9194 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9195 false /* fRefresh */, medium);
9196 else
9197 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9198 dev.uuid,
9199 false /* fRefresh */,
9200 false /* aSetError */,
9201 medium);
9202 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9203 // This is not an error. The host drive or UUID might have vanished, so just go
9204 // ahead without this removeable medium attachment
9205 rc = S_OK;
9206 break;
9207
9208 case DeviceType_HardDisk:
9209 {
9210 /* find a hard disk by UUID */
9211 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9212 if (FAILED(rc))
9213 {
9214 if (i_isSnapshotMachine())
9215 {
9216 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9217 // so the user knows that the bad disk is in a snapshot somewhere
9218 com::ErrorInfo info;
9219 return setError(E_FAIL,
9220 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9221 puuidSnapshot->raw(),
9222 info.getText().raw());
9223 }
9224 else
9225 return rc;
9226 }
9227
9228 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9229
9230 if (medium->i_getType() == MediumType_Immutable)
9231 {
9232 if (i_isSnapshotMachine())
9233 return setError(E_FAIL,
9234 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9235 "of the virtual machine '%s' ('%s')"),
9236 medium->i_getLocationFull().c_str(),
9237 dev.uuid.raw(),
9238 puuidSnapshot->raw(),
9239 mUserData->s.strName.c_str(),
9240 mData->m_strConfigFileFull.c_str());
9241
9242 return setError(E_FAIL,
9243 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9244 medium->i_getLocationFull().c_str(),
9245 dev.uuid.raw(),
9246 mUserData->s.strName.c_str(),
9247 mData->m_strConfigFileFull.c_str());
9248 }
9249
9250 if (medium->i_getType() == MediumType_MultiAttach)
9251 {
9252 if (i_isSnapshotMachine())
9253 return setError(E_FAIL,
9254 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9255 "of the virtual machine '%s' ('%s')"),
9256 medium->i_getLocationFull().c_str(),
9257 dev.uuid.raw(),
9258 puuidSnapshot->raw(),
9259 mUserData->s.strName.c_str(),
9260 mData->m_strConfigFileFull.c_str());
9261
9262 return setError(E_FAIL,
9263 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9264 medium->i_getLocationFull().c_str(),
9265 dev.uuid.raw(),
9266 mUserData->s.strName.c_str(),
9267 mData->m_strConfigFileFull.c_str());
9268 }
9269
9270 if ( !i_isSnapshotMachine()
9271 && medium->i_getChildren().size() != 0
9272 )
9273 return setError(E_FAIL,
9274 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9275 "because it has %d differencing child hard disks"),
9276 medium->i_getLocationFull().c_str(),
9277 dev.uuid.raw(),
9278 mUserData->s.strName.c_str(),
9279 mData->m_strConfigFileFull.c_str(),
9280 medium->i_getChildren().size());
9281
9282 if (i_findAttachment(*mMediumAttachments.data(),
9283 medium))
9284 return setError(E_FAIL,
9285 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9286 medium->i_getLocationFull().c_str(),
9287 dev.uuid.raw(),
9288 mUserData->s.strName.c_str(),
9289 mData->m_strConfigFileFull.c_str());
9290
9291 break;
9292 }
9293
9294 default:
9295 return setError(E_FAIL,
9296 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9297 medium->i_getLocationFull().c_str(),
9298 mUserData->s.strName.c_str(),
9299 mData->m_strConfigFileFull.c_str());
9300 }
9301
9302 if (FAILED(rc))
9303 break;
9304
9305 /* Bandwidth groups are loaded at this point. */
9306 ComObjPtr<BandwidthGroup> pBwGroup;
9307
9308 if (!dev.strBwGroup.isEmpty())
9309 {
9310 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9311 if (FAILED(rc))
9312 return setError(E_FAIL,
9313 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9314 medium->i_getLocationFull().c_str(),
9315 dev.strBwGroup.c_str(),
9316 mUserData->s.strName.c_str(),
9317 mData->m_strConfigFileFull.c_str());
9318 pBwGroup->i_reference();
9319 }
9320
9321 const Utf8Str controllerName = aStorageController->i_getName();
9322 ComObjPtr<MediumAttachment> pAttachment;
9323 pAttachment.createObject();
9324 rc = pAttachment->init(this,
9325 medium,
9326 controllerName,
9327 dev.lPort,
9328 dev.lDevice,
9329 dev.deviceType,
9330 false,
9331 dev.fPassThrough,
9332 dev.fTempEject,
9333 dev.fNonRotational,
9334 dev.fDiscard,
9335 dev.fHotPluggable,
9336 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9337 if (FAILED(rc)) break;
9338
9339 /* associate the medium with this machine and snapshot */
9340 if (!medium.isNull())
9341 {
9342 AutoCaller medCaller(medium);
9343 if (FAILED(medCaller.rc())) return medCaller.rc();
9344 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9345
9346 if (i_isSnapshotMachine())
9347 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9348 else
9349 rc = medium->i_addBackReference(mData->mUuid);
9350 /* If the medium->addBackReference fails it sets an appropriate
9351 * error message, so no need to do any guesswork here. */
9352
9353 if (puuidRegistry)
9354 // caller wants registry ID to be set on all attached media (OVF import case)
9355 medium->i_addRegistry(*puuidRegistry);
9356 }
9357
9358 if (FAILED(rc))
9359 break;
9360
9361 /* back up mMediumAttachments to let registeredInit() properly rollback
9362 * on failure (= limited accessibility) */
9363 i_setModified(IsModified_Storage);
9364 mMediumAttachments.backup();
9365 mMediumAttachments->push_back(pAttachment);
9366 }
9367
9368 return rc;
9369}
9370
9371/**
9372 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9373 *
9374 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9375 * @param aSnapshot where to return the found snapshot
9376 * @param aSetError true to set extended error info on failure
9377 */
9378HRESULT Machine::i_findSnapshotById(const Guid &aId,
9379 ComObjPtr<Snapshot> &aSnapshot,
9380 bool aSetError /* = false */)
9381{
9382 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9383
9384 if (!mData->mFirstSnapshot)
9385 {
9386 if (aSetError)
9387 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9388 return E_FAIL;
9389 }
9390
9391 if (aId.isZero())
9392 aSnapshot = mData->mFirstSnapshot;
9393 else
9394 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9395
9396 if (!aSnapshot)
9397 {
9398 if (aSetError)
9399 return setError(E_FAIL,
9400 tr("Could not find a snapshot with UUID {%s}"),
9401 aId.toString().c_str());
9402 return E_FAIL;
9403 }
9404
9405 return S_OK;
9406}
9407
9408/**
9409 * Returns the snapshot with the given name or fails of no such snapshot.
9410 *
9411 * @param strName snapshot name to find
9412 * @param aSnapshot where to return the found snapshot
9413 * @param aSetError true to set extended error info on failure
9414 */
9415HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9416 ComObjPtr<Snapshot> &aSnapshot,
9417 bool aSetError /* = false */)
9418{
9419 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9420
9421 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9422
9423 if (!mData->mFirstSnapshot)
9424 {
9425 if (aSetError)
9426 return setError(VBOX_E_OBJECT_NOT_FOUND,
9427 tr("This machine does not have any snapshots"));
9428 return VBOX_E_OBJECT_NOT_FOUND;
9429 }
9430
9431 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9432
9433 if (!aSnapshot)
9434 {
9435 if (aSetError)
9436 return setError(VBOX_E_OBJECT_NOT_FOUND,
9437 tr("Could not find a snapshot named '%s'"), strName.c_str());
9438 return VBOX_E_OBJECT_NOT_FOUND;
9439 }
9440
9441 return S_OK;
9442}
9443
9444/**
9445 * Returns a storage controller object with the given name.
9446 *
9447 * @param aName storage controller name to find
9448 * @param aStorageController where to return the found storage controller
9449 * @param aSetError true to set extended error info on failure
9450 */
9451HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9452 ComObjPtr<StorageController> &aStorageController,
9453 bool aSetError /* = false */)
9454{
9455 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9456
9457 for (StorageControllerList::const_iterator
9458 it = mStorageControllers->begin();
9459 it != mStorageControllers->end();
9460 ++it)
9461 {
9462 if ((*it)->i_getName() == aName)
9463 {
9464 aStorageController = (*it);
9465 return S_OK;
9466 }
9467 }
9468
9469 if (aSetError)
9470 return setError(VBOX_E_OBJECT_NOT_FOUND,
9471 tr("Could not find a storage controller named '%s'"),
9472 aName.c_str());
9473 return VBOX_E_OBJECT_NOT_FOUND;
9474}
9475
9476/**
9477 * Returns a USB controller object with the given name.
9478 *
9479 * @param aName USB controller name to find
9480 * @param aUSBController where to return the found USB controller
9481 * @param aSetError true to set extended error info on failure
9482 */
9483HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9484 ComObjPtr<USBController> &aUSBController,
9485 bool aSetError /* = false */)
9486{
9487 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9488
9489 for (USBControllerList::const_iterator
9490 it = mUSBControllers->begin();
9491 it != mUSBControllers->end();
9492 ++it)
9493 {
9494 if ((*it)->i_getName() == aName)
9495 {
9496 aUSBController = (*it);
9497 return S_OK;
9498 }
9499 }
9500
9501 if (aSetError)
9502 return setError(VBOX_E_OBJECT_NOT_FOUND,
9503 tr("Could not find a storage controller named '%s'"),
9504 aName.c_str());
9505 return VBOX_E_OBJECT_NOT_FOUND;
9506}
9507
9508/**
9509 * Returns the number of USB controller instance of the given type.
9510 *
9511 * @param enmType USB controller type.
9512 */
9513ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9514{
9515 ULONG cCtrls = 0;
9516
9517 for (USBControllerList::const_iterator
9518 it = mUSBControllers->begin();
9519 it != mUSBControllers->end();
9520 ++it)
9521 {
9522 if ((*it)->i_getControllerType() == enmType)
9523 cCtrls++;
9524 }
9525
9526 return cCtrls;
9527}
9528
9529HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9530 MediumAttachmentList &atts)
9531{
9532 AutoCaller autoCaller(this);
9533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9534
9535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9536
9537 for (MediumAttachmentList::const_iterator
9538 it = mMediumAttachments->begin();
9539 it != mMediumAttachments->end();
9540 ++it)
9541 {
9542 const ComObjPtr<MediumAttachment> &pAtt = *it;
9543 // should never happen, but deal with NULL pointers in the list.
9544 AssertContinue(!pAtt.isNull());
9545
9546 // getControllerName() needs caller+read lock
9547 AutoCaller autoAttCaller(pAtt);
9548 if (FAILED(autoAttCaller.rc()))
9549 {
9550 atts.clear();
9551 return autoAttCaller.rc();
9552 }
9553 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9554
9555 if (pAtt->i_getControllerName() == aName)
9556 atts.push_back(pAtt);
9557 }
9558
9559 return S_OK;
9560}
9561
9562
9563/**
9564 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9565 * file if the machine name was changed and about creating a new settings file
9566 * if this is a new machine.
9567 *
9568 * @note Must be never called directly but only from #saveSettings().
9569 */
9570HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9571{
9572 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9573
9574 HRESULT rc = S_OK;
9575
9576 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9577
9578 /// @todo need to handle primary group change, too
9579
9580 /* attempt to rename the settings file if machine name is changed */
9581 if ( mUserData->s.fNameSync
9582 && mUserData.isBackedUp()
9583 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9584 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9585 )
9586 {
9587 bool dirRenamed = false;
9588 bool fileRenamed = false;
9589
9590 Utf8Str configFile, newConfigFile;
9591 Utf8Str configFilePrev, newConfigFilePrev;
9592 Utf8Str NVRAMFile, newNVRAMFile;
9593 Utf8Str configDir, newConfigDir;
9594
9595 do
9596 {
9597 int vrc = VINF_SUCCESS;
9598
9599 Utf8Str name = mUserData.backedUpData()->s.strName;
9600 Utf8Str newName = mUserData->s.strName;
9601 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9602 if (group == "/")
9603 group.setNull();
9604 Utf8Str newGroup = mUserData->s.llGroups.front();
9605 if (newGroup == "/")
9606 newGroup.setNull();
9607
9608 configFile = mData->m_strConfigFileFull;
9609
9610 /* first, rename the directory if it matches the group and machine name */
9611 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9612 /** @todo hack, make somehow use of ComposeMachineFilename */
9613 if (mUserData->s.fDirectoryIncludesUUID)
9614 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9615 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9616 /** @todo hack, make somehow use of ComposeMachineFilename */
9617 if (mUserData->s.fDirectoryIncludesUUID)
9618 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9619 configDir = configFile;
9620 configDir.stripFilename();
9621 newConfigDir = configDir;
9622 if ( configDir.length() >= groupPlusName.length()
9623 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9624 groupPlusName.c_str()))
9625 {
9626 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9627 Utf8Str newConfigBaseDir(newConfigDir);
9628 newConfigDir.append(newGroupPlusName);
9629 /* consistency: use \ if appropriate on the platform */
9630 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9631 /* new dir and old dir cannot be equal here because of 'if'
9632 * above and because name != newName */
9633 Assert(configDir != newConfigDir);
9634 if (!fSettingsFileIsNew)
9635 {
9636 /* perform real rename only if the machine is not new */
9637 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9638 if ( vrc == VERR_FILE_NOT_FOUND
9639 || vrc == VERR_PATH_NOT_FOUND)
9640 {
9641 /* create the parent directory, then retry renaming */
9642 Utf8Str parent(newConfigDir);
9643 parent.stripFilename();
9644 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9645 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9646 }
9647 if (RT_FAILURE(vrc))
9648 {
9649 rc = setErrorBoth(E_FAIL, vrc,
9650 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9651 configDir.c_str(),
9652 newConfigDir.c_str(),
9653 vrc);
9654 break;
9655 }
9656 /* delete subdirectories which are no longer needed */
9657 Utf8Str dir(configDir);
9658 dir.stripFilename();
9659 while (dir != newConfigBaseDir && dir != ".")
9660 {
9661 vrc = RTDirRemove(dir.c_str());
9662 if (RT_FAILURE(vrc))
9663 break;
9664 dir.stripFilename();
9665 }
9666 dirRenamed = true;
9667 }
9668 }
9669
9670 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9671
9672 /* then try to rename the settings file itself */
9673 if (newConfigFile != configFile)
9674 {
9675 /* get the path to old settings file in renamed directory */
9676 Assert(mData->m_strConfigFileFull == configFile);
9677 configFile.printf("%s%c%s",
9678 newConfigDir.c_str(),
9679 RTPATH_DELIMITER,
9680 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9681 if (!fSettingsFileIsNew)
9682 {
9683 /* perform real rename only if the machine is not new */
9684 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9685 if (RT_FAILURE(vrc))
9686 {
9687 rc = setErrorBoth(E_FAIL, vrc,
9688 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9689 configFile.c_str(),
9690 newConfigFile.c_str(),
9691 vrc);
9692 break;
9693 }
9694 fileRenamed = true;
9695 configFilePrev = configFile;
9696 configFilePrev += "-prev";
9697 newConfigFilePrev = newConfigFile;
9698 newConfigFilePrev += "-prev";
9699 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9700 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9701 if (NVRAMFile.isNotEmpty())
9702 {
9703 // in the NVRAM file path, replace the old directory with the new directory
9704 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9705 {
9706 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9707 NVRAMFile = newConfigDir + strNVRAMFile;
9708 }
9709 newNVRAMFile = newConfigFile;
9710 newNVRAMFile.stripSuffix();
9711 newNVRAMFile += ".nvram";
9712 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9713 }
9714 }
9715 }
9716
9717 // update m_strConfigFileFull amd mConfigFile
9718 mData->m_strConfigFileFull = newConfigFile;
9719 // compute the relative path too
9720 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9721
9722 // store the old and new so that VirtualBox::i_saveSettings() can update
9723 // the media registry
9724 if ( mData->mRegistered
9725 && (configDir != newConfigDir || configFile != newConfigFile))
9726 {
9727 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9728
9729 if (pfNeedsGlobalSaveSettings)
9730 *pfNeedsGlobalSaveSettings = true;
9731 }
9732
9733 // in the saved state file path, replace the old directory with the new directory
9734 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9735 {
9736 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9737 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9738 }
9739 if (newNVRAMFile.isNotEmpty())
9740 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9741
9742 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9743 if (mData->mFirstSnapshot)
9744 {
9745 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9746 newConfigDir.c_str());
9747 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9748 newConfigDir.c_str());
9749 }
9750 }
9751 while (0);
9752
9753 if (FAILED(rc))
9754 {
9755 /* silently try to rename everything back */
9756 if (fileRenamed)
9757 {
9758 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9759 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9760 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9761 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9762 }
9763 if (dirRenamed)
9764 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9765 }
9766
9767 if (FAILED(rc)) return rc;
9768 }
9769
9770 if (fSettingsFileIsNew)
9771 {
9772 /* create a virgin config file */
9773 int vrc = VINF_SUCCESS;
9774
9775 /* ensure the settings directory exists */
9776 Utf8Str path(mData->m_strConfigFileFull);
9777 path.stripFilename();
9778 if (!RTDirExists(path.c_str()))
9779 {
9780 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9781 if (RT_FAILURE(vrc))
9782 {
9783 return setErrorBoth(E_FAIL, vrc,
9784 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9785 path.c_str(),
9786 vrc);
9787 }
9788 }
9789
9790 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9791 path = Utf8Str(mData->m_strConfigFileFull);
9792 RTFILE f = NIL_RTFILE;
9793 vrc = RTFileOpen(&f, path.c_str(),
9794 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9795 if (RT_FAILURE(vrc))
9796 return setErrorBoth(E_FAIL, vrc,
9797 tr("Could not create the settings file '%s' (%Rrc)"),
9798 path.c_str(),
9799 vrc);
9800 RTFileClose(f);
9801 }
9802
9803 return rc;
9804}
9805
9806/**
9807 * Saves and commits machine data, user data and hardware data.
9808 *
9809 * Note that on failure, the data remains uncommitted.
9810 *
9811 * @a aFlags may combine the following flags:
9812 *
9813 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9814 * Used when saving settings after an operation that makes them 100%
9815 * correspond to the settings from the current snapshot.
9816 * - SaveS_Force: settings will be saved without doing a deep compare of the
9817 * settings structures. This is used when this is called because snapshots
9818 * have changed to avoid the overhead of the deep compare.
9819 *
9820 * @note Must be called from under this object's write lock. Locks children for
9821 * writing.
9822 *
9823 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9824 * initialized to false and that will be set to true by this function if
9825 * the caller must invoke VirtualBox::i_saveSettings() because the global
9826 * settings have changed. This will happen if a machine rename has been
9827 * saved and the global machine and media registries will therefore need
9828 * updating.
9829 * @param aFlags Flags.
9830 */
9831HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9832 int aFlags /*= 0*/)
9833{
9834 LogFlowThisFuncEnter();
9835
9836 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9837
9838 /* make sure child objects are unable to modify the settings while we are
9839 * saving them */
9840 i_ensureNoStateDependencies();
9841
9842 AssertReturn(!i_isSnapshotMachine(),
9843 E_FAIL);
9844
9845 if (!mData->mAccessible)
9846 return setError(VBOX_E_INVALID_VM_STATE,
9847 tr("The machine is not accessible, so cannot save settings"));
9848
9849 HRESULT rc = S_OK;
9850 bool fNeedsWrite = false;
9851
9852 /* First, prepare to save settings. It will care about renaming the
9853 * settings directory and file if the machine name was changed and about
9854 * creating a new settings file if this is a new machine. */
9855 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9856 if (FAILED(rc)) return rc;
9857
9858 // keep a pointer to the current settings structures
9859 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9860 settings::MachineConfigFile *pNewConfig = NULL;
9861
9862 try
9863 {
9864 // make a fresh one to have everyone write stuff into
9865 pNewConfig = new settings::MachineConfigFile(NULL);
9866 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9867
9868 // now go and copy all the settings data from COM to the settings structures
9869 // (this calls i_saveSettings() on all the COM objects in the machine)
9870 i_copyMachineDataToSettings(*pNewConfig);
9871
9872 if (aFlags & SaveS_ResetCurStateModified)
9873 {
9874 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9875 mData->mCurrentStateModified = FALSE;
9876 fNeedsWrite = true; // always, no need to compare
9877 }
9878 else if (aFlags & SaveS_Force)
9879 {
9880 fNeedsWrite = true; // always, no need to compare
9881 }
9882 else
9883 {
9884 if (!mData->mCurrentStateModified)
9885 {
9886 // do a deep compare of the settings that we just saved with the settings
9887 // previously stored in the config file; this invokes MachineConfigFile::operator==
9888 // which does a deep compare of all the settings, which is expensive but less expensive
9889 // than writing out XML in vain
9890 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9891
9892 // could still be modified if any settings changed
9893 mData->mCurrentStateModified = fAnySettingsChanged;
9894
9895 fNeedsWrite = fAnySettingsChanged;
9896 }
9897 else
9898 fNeedsWrite = true;
9899 }
9900
9901 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9902
9903 if (fNeedsWrite)
9904 // now spit it all out!
9905 pNewConfig->write(mData->m_strConfigFileFull);
9906
9907 mData->pMachineConfigFile = pNewConfig;
9908 delete pOldConfig;
9909 i_commit();
9910
9911 // after saving settings, we are no longer different from the XML on disk
9912 mData->flModifications = 0;
9913 }
9914 catch (HRESULT err)
9915 {
9916 // we assume that error info is set by the thrower
9917 rc = err;
9918
9919 // restore old config
9920 delete pNewConfig;
9921 mData->pMachineConfigFile = pOldConfig;
9922 }
9923 catch (...)
9924 {
9925 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9926 }
9927
9928 if (fNeedsWrite)
9929 {
9930 /* Fire the data change event, even on failure (since we've already
9931 * committed all data). This is done only for SessionMachines because
9932 * mutable Machine instances are always not registered (i.e. private
9933 * to the client process that creates them) and thus don't need to
9934 * inform callbacks. */
9935 if (i_isSessionMachine())
9936 mParent->i_onMachineDataChanged(mData->mUuid);
9937 }
9938
9939 LogFlowThisFunc(("rc=%08X\n", rc));
9940 LogFlowThisFuncLeave();
9941 return rc;
9942}
9943
9944/**
9945 * Implementation for saving the machine settings into the given
9946 * settings::MachineConfigFile instance. This copies machine extradata
9947 * from the previous machine config file in the instance data, if any.
9948 *
9949 * This gets called from two locations:
9950 *
9951 * -- Machine::i_saveSettings(), during the regular XML writing;
9952 *
9953 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9954 * exported to OVF and we write the VirtualBox proprietary XML
9955 * into a <vbox:Machine> tag.
9956 *
9957 * This routine fills all the fields in there, including snapshots, *except*
9958 * for the following:
9959 *
9960 * -- fCurrentStateModified. There is some special logic associated with that.
9961 *
9962 * The caller can then call MachineConfigFile::write() or do something else
9963 * with it.
9964 *
9965 * Caller must hold the machine lock!
9966 *
9967 * This throws XML errors and HRESULT, so the caller must have a catch block!
9968 */
9969void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9970{
9971 // deep copy extradata, being extra careful with self assignment (the STL
9972 // map assignment on Mac OS X clang based Xcode isn't checking)
9973 if (&config != mData->pMachineConfigFile)
9974 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9975
9976 config.uuid = mData->mUuid;
9977
9978 // copy name, description, OS type, teleport, UTC etc.
9979 config.machineUserData = mUserData->s;
9980
9981 if ( mData->mMachineState == MachineState_Saved
9982 || mData->mMachineState == MachineState_Restoring
9983 // when doing certain snapshot operations we may or may not have
9984 // a saved state in the current state, so keep everything as is
9985 || ( ( mData->mMachineState == MachineState_Snapshotting
9986 || mData->mMachineState == MachineState_DeletingSnapshot
9987 || mData->mMachineState == MachineState_RestoringSnapshot)
9988 && (!mSSData->strStateFilePath.isEmpty())
9989 )
9990 )
9991 {
9992 Assert(!mSSData->strStateFilePath.isEmpty());
9993 /* try to make the file name relative to the settings file dir */
9994 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9995 }
9996 else
9997 {
9998 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9999 config.strStateFile.setNull();
10000 }
10001
10002 if (mData->mCurrentSnapshot)
10003 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10004 else
10005 config.uuidCurrentSnapshot.clear();
10006
10007 config.timeLastStateChange = mData->mLastStateChange;
10008 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10009 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10010
10011 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10012 if (FAILED(rc)) throw rc;
10013
10014 // save machine's media registry if this is VirtualBox 4.0 or later
10015 if (config.canHaveOwnMediaRegistry())
10016 {
10017 // determine machine folder
10018 Utf8Str strMachineFolder = i_getSettingsFileFull();
10019 strMachineFolder.stripFilename();
10020 mParent->i_saveMediaRegistry(config.mediaRegistry,
10021 i_getId(), // only media with registry ID == machine UUID
10022 strMachineFolder);
10023 // this throws HRESULT
10024 }
10025
10026 // save snapshots
10027 rc = i_saveAllSnapshots(config);
10028 if (FAILED(rc)) throw rc;
10029}
10030
10031/**
10032 * Saves all snapshots of the machine into the given machine config file. Called
10033 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10034 * @param config
10035 * @return
10036 */
10037HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10038{
10039 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10040
10041 HRESULT rc = S_OK;
10042
10043 try
10044 {
10045 config.llFirstSnapshot.clear();
10046
10047 if (mData->mFirstSnapshot)
10048 {
10049 // the settings use a list for "the first snapshot"
10050 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10051
10052 // get reference to the snapshot on the list and work on that
10053 // element straight in the list to avoid excessive copying later
10054 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10055 if (FAILED(rc)) throw rc;
10056 }
10057
10058// if (mType == IsSessionMachine)
10059// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10060
10061 }
10062 catch (HRESULT err)
10063 {
10064 /* we assume that error info is set by the thrower */
10065 rc = err;
10066 }
10067 catch (...)
10068 {
10069 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10070 }
10071
10072 return rc;
10073}
10074
10075/**
10076 * Saves the VM hardware configuration. It is assumed that the
10077 * given node is empty.
10078 *
10079 * @param data Reference to the settings object for the hardware config.
10080 * @param pDbg Pointer to the settings object for the debugging config
10081 * which happens to live in mHWData.
10082 * @param pAutostart Pointer to the settings object for the autostart config
10083 * which happens to live in mHWData.
10084 */
10085HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10086 settings::Autostart *pAutostart)
10087{
10088 HRESULT rc = S_OK;
10089
10090 try
10091 {
10092 /* The hardware version attribute (optional).
10093 Automatically upgrade from 1 to current default hardware version
10094 when there is no saved state. (ugly!) */
10095 if ( mHWData->mHWVersion == "1"
10096 && mSSData->strStateFilePath.isEmpty()
10097 )
10098 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10099
10100 data.strVersion = mHWData->mHWVersion;
10101 data.uuid = mHWData->mHardwareUUID;
10102
10103 // CPU
10104 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10105 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10106 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10107 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10108 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10109 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10110 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10111 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10112 data.fPAE = !!mHWData->mPAEEnabled;
10113 data.enmLongMode = mHWData->mLongMode;
10114 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10115 data.fAPIC = !!mHWData->mAPIC;
10116 data.fX2APIC = !!mHWData->mX2APIC;
10117 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10118 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10119 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10120 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10121 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10122 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10123 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10124 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10125 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10126 data.cCPUs = mHWData->mCPUCount;
10127 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10128 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10129 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10130 data.strCpuProfile = mHWData->mCpuProfile;
10131
10132 data.llCpus.clear();
10133 if (data.fCpuHotPlug)
10134 {
10135 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10136 {
10137 if (mHWData->mCPUAttached[idx])
10138 {
10139 settings::Cpu cpu;
10140 cpu.ulId = idx;
10141 data.llCpus.push_back(cpu);
10142 }
10143 }
10144 }
10145
10146 /* Standard and Extended CPUID leafs. */
10147 data.llCpuIdLeafs.clear();
10148 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10149
10150 // memory
10151 data.ulMemorySizeMB = mHWData->mMemorySize;
10152 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10153
10154 // firmware
10155 data.firmwareType = mHWData->mFirmwareType;
10156
10157 // HID
10158 data.pointingHIDType = mHWData->mPointingHIDType;
10159 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10160
10161 // chipset
10162 data.chipsetType = mHWData->mChipsetType;
10163
10164 // iommu
10165 data.iommuType = mHWData->mIommuType;
10166
10167 // paravirt
10168 data.paravirtProvider = mHWData->mParavirtProvider;
10169 data.strParavirtDebug = mHWData->mParavirtDebug;
10170
10171 // emulated USB card reader
10172 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10173
10174 // HPET
10175 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10176
10177 // boot order
10178 data.mapBootOrder.clear();
10179 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10180 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10181
10182 /* VRDEServer settings (optional) */
10183 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10184 if (FAILED(rc)) throw rc;
10185
10186 /* BIOS settings (required) */
10187 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10188 if (FAILED(rc)) throw rc;
10189
10190 /* Trusted Platform Module settings (required) */
10191 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10192 if (FAILED(rc)) throw rc;
10193
10194 /* Recording settings (required) */
10195 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10196 if (FAILED(rc)) throw rc;
10197
10198 /* GraphicsAdapter settings (required) */
10199 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10200 if (FAILED(rc)) throw rc;
10201
10202 /* USB Controller (required) */
10203 data.usbSettings.llUSBControllers.clear();
10204 for (USBControllerList::const_iterator
10205 it = mUSBControllers->begin();
10206 it != mUSBControllers->end();
10207 ++it)
10208 {
10209 ComObjPtr<USBController> ctrl = *it;
10210 settings::USBController settingsCtrl;
10211
10212 settingsCtrl.strName = ctrl->i_getName();
10213 settingsCtrl.enmType = ctrl->i_getControllerType();
10214
10215 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10216 }
10217
10218 /* USB device filters (required) */
10219 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10220 if (FAILED(rc)) throw rc;
10221
10222 /* Network adapters (required) */
10223 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10224 data.llNetworkAdapters.clear();
10225 /* Write out only the nominal number of network adapters for this
10226 * chipset type. Since Machine::commit() hasn't been called there
10227 * may be extra NIC settings in the vector. */
10228 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10229 {
10230 settings::NetworkAdapter nic;
10231 nic.ulSlot = (uint32_t)slot;
10232 /* paranoia check... must not be NULL, but must not crash either. */
10233 if (mNetworkAdapters[slot])
10234 {
10235 if (mNetworkAdapters[slot]->i_hasDefaults())
10236 continue;
10237
10238 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10239 if (FAILED(rc)) throw rc;
10240
10241 data.llNetworkAdapters.push_back(nic);
10242 }
10243 }
10244
10245 /* Serial ports */
10246 data.llSerialPorts.clear();
10247 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10248 {
10249 if (mSerialPorts[slot]->i_hasDefaults())
10250 continue;
10251
10252 settings::SerialPort s;
10253 s.ulSlot = slot;
10254 rc = mSerialPorts[slot]->i_saveSettings(s);
10255 if (FAILED(rc)) return rc;
10256
10257 data.llSerialPorts.push_back(s);
10258 }
10259
10260 /* Parallel ports */
10261 data.llParallelPorts.clear();
10262 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10263 {
10264 if (mParallelPorts[slot]->i_hasDefaults())
10265 continue;
10266
10267 settings::ParallelPort p;
10268 p.ulSlot = slot;
10269 rc = mParallelPorts[slot]->i_saveSettings(p);
10270 if (FAILED(rc)) return rc;
10271
10272 data.llParallelPorts.push_back(p);
10273 }
10274
10275 /* Audio adapter */
10276 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10277 if (FAILED(rc)) return rc;
10278
10279 rc = i_saveStorageControllers(data.storage);
10280 if (FAILED(rc)) return rc;
10281
10282 /* Shared folders */
10283 data.llSharedFolders.clear();
10284 for (HWData::SharedFolderList::const_iterator
10285 it = mHWData->mSharedFolders.begin();
10286 it != mHWData->mSharedFolders.end();
10287 ++it)
10288 {
10289 SharedFolder *pSF = *it;
10290 AutoCaller sfCaller(pSF);
10291 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10292 settings::SharedFolder sf;
10293 sf.strName = pSF->i_getName();
10294 sf.strHostPath = pSF->i_getHostPath();
10295 sf.fWritable = !!pSF->i_isWritable();
10296 sf.fAutoMount = !!pSF->i_isAutoMounted();
10297 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10298
10299 data.llSharedFolders.push_back(sf);
10300 }
10301
10302 // clipboard
10303 data.clipboardMode = mHWData->mClipboardMode;
10304 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10305
10306 // drag'n'drop
10307 data.dndMode = mHWData->mDnDMode;
10308
10309 /* Guest */
10310 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10311
10312 // IO settings
10313 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10314 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10315
10316 /* BandwidthControl (required) */
10317 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10318 if (FAILED(rc)) throw rc;
10319
10320 /* Host PCI devices */
10321 data.pciAttachments.clear();
10322 for (HWData::PCIDeviceAssignmentList::const_iterator
10323 it = mHWData->mPCIDeviceAssignments.begin();
10324 it != mHWData->mPCIDeviceAssignments.end();
10325 ++it)
10326 {
10327 ComObjPtr<PCIDeviceAttachment> pda = *it;
10328 settings::HostPCIDeviceAttachment hpda;
10329
10330 rc = pda->i_saveSettings(hpda);
10331 if (FAILED(rc)) throw rc;
10332
10333 data.pciAttachments.push_back(hpda);
10334 }
10335
10336 // guest properties
10337 data.llGuestProperties.clear();
10338#ifdef VBOX_WITH_GUEST_PROPS
10339 for (HWData::GuestPropertyMap::const_iterator
10340 it = mHWData->mGuestProperties.begin();
10341 it != mHWData->mGuestProperties.end();
10342 ++it)
10343 {
10344 HWData::GuestProperty property = it->second;
10345
10346 /* Remove transient guest properties at shutdown unless we
10347 * are saving state. Note that restoring snapshot intentionally
10348 * keeps them, they will be removed if appropriate once the final
10349 * machine state is set (as crashes etc. need to work). */
10350 if ( ( mData->mMachineState == MachineState_PoweredOff
10351 || mData->mMachineState == MachineState_Aborted
10352 || mData->mMachineState == MachineState_Teleported)
10353 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10354 continue;
10355 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10356 prop.strName = it->first;
10357 prop.strValue = property.strValue;
10358 prop.timestamp = (uint64_t)property.mTimestamp;
10359 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10360 GuestPropWriteFlags(property.mFlags, szFlags);
10361 prop.strFlags = szFlags;
10362
10363 data.llGuestProperties.push_back(prop);
10364 }
10365
10366 /* I presume this doesn't require a backup(). */
10367 mData->mGuestPropertiesModified = FALSE;
10368#endif /* VBOX_WITH_GUEST_PROPS defined */
10369
10370 *pDbg = mHWData->mDebugging;
10371 *pAutostart = mHWData->mAutostart;
10372
10373 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10374 }
10375 catch (std::bad_alloc &)
10376 {
10377 return E_OUTOFMEMORY;
10378 }
10379
10380 AssertComRC(rc);
10381 return rc;
10382}
10383
10384/**
10385 * Saves the storage controller configuration.
10386 *
10387 * @param data storage settings.
10388 */
10389HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10390{
10391 data.llStorageControllers.clear();
10392
10393 for (StorageControllerList::const_iterator
10394 it = mStorageControllers->begin();
10395 it != mStorageControllers->end();
10396 ++it)
10397 {
10398 HRESULT rc;
10399 ComObjPtr<StorageController> pCtl = *it;
10400
10401 settings::StorageController ctl;
10402 ctl.strName = pCtl->i_getName();
10403 ctl.controllerType = pCtl->i_getControllerType();
10404 ctl.storageBus = pCtl->i_getStorageBus();
10405 ctl.ulInstance = pCtl->i_getInstance();
10406 ctl.fBootable = pCtl->i_getBootable();
10407
10408 /* Save the port count. */
10409 ULONG portCount;
10410 rc = pCtl->COMGETTER(PortCount)(&portCount);
10411 ComAssertComRCRet(rc, rc);
10412 ctl.ulPortCount = portCount;
10413
10414 /* Save fUseHostIOCache */
10415 BOOL fUseHostIOCache;
10416 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10417 ComAssertComRCRet(rc, rc);
10418 ctl.fUseHostIOCache = !!fUseHostIOCache;
10419
10420 /* save the devices now. */
10421 rc = i_saveStorageDevices(pCtl, ctl);
10422 ComAssertComRCRet(rc, rc);
10423
10424 data.llStorageControllers.push_back(ctl);
10425 }
10426
10427 return S_OK;
10428}
10429
10430/**
10431 * Saves the hard disk configuration.
10432 */
10433HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10434 settings::StorageController &data)
10435{
10436 MediumAttachmentList atts;
10437
10438 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10439 if (FAILED(rc)) return rc;
10440
10441 data.llAttachedDevices.clear();
10442 for (MediumAttachmentList::const_iterator
10443 it = atts.begin();
10444 it != atts.end();
10445 ++it)
10446 {
10447 settings::AttachedDevice dev;
10448 IMediumAttachment *iA = *it;
10449 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10450 Medium *pMedium = pAttach->i_getMedium();
10451
10452 dev.deviceType = pAttach->i_getType();
10453 dev.lPort = pAttach->i_getPort();
10454 dev.lDevice = pAttach->i_getDevice();
10455 dev.fPassThrough = pAttach->i_getPassthrough();
10456 dev.fHotPluggable = pAttach->i_getHotPluggable();
10457 if (pMedium)
10458 {
10459 if (pMedium->i_isHostDrive())
10460 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10461 else
10462 dev.uuid = pMedium->i_getId();
10463 dev.fTempEject = pAttach->i_getTempEject();
10464 dev.fNonRotational = pAttach->i_getNonRotational();
10465 dev.fDiscard = pAttach->i_getDiscard();
10466 }
10467
10468 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10469
10470 data.llAttachedDevices.push_back(dev);
10471 }
10472
10473 return S_OK;
10474}
10475
10476/**
10477 * Saves machine state settings as defined by aFlags
10478 * (SaveSTS_* values).
10479 *
10480 * @param aFlags Combination of SaveSTS_* flags.
10481 *
10482 * @note Locks objects for writing.
10483 */
10484HRESULT Machine::i_saveStateSettings(int aFlags)
10485{
10486 if (aFlags == 0)
10487 return S_OK;
10488
10489 AutoCaller autoCaller(this);
10490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10491
10492 /* This object's write lock is also necessary to serialize file access
10493 * (prevent concurrent reads and writes) */
10494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10495
10496 HRESULT rc = S_OK;
10497
10498 Assert(mData->pMachineConfigFile);
10499
10500 try
10501 {
10502 if (aFlags & SaveSTS_CurStateModified)
10503 mData->pMachineConfigFile->fCurrentStateModified = true;
10504
10505 if (aFlags & SaveSTS_StateFilePath)
10506 {
10507 if (!mSSData->strStateFilePath.isEmpty())
10508 /* try to make the file name relative to the settings file dir */
10509 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10510 else
10511 mData->pMachineConfigFile->strStateFile.setNull();
10512 }
10513
10514 if (aFlags & SaveSTS_StateTimeStamp)
10515 {
10516 Assert( mData->mMachineState != MachineState_Aborted
10517 || mSSData->strStateFilePath.isEmpty());
10518
10519 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10520
10521 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10522/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10523 }
10524
10525 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10526 }
10527 catch (...)
10528 {
10529 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10530 }
10531
10532 return rc;
10533}
10534
10535/**
10536 * Ensures that the given medium is added to a media registry. If this machine
10537 * was created with 4.0 or later, then the machine registry is used. Otherwise
10538 * the global VirtualBox media registry is used.
10539 *
10540 * Caller must NOT hold machine lock, media tree or any medium locks!
10541 *
10542 * @param pMedium
10543 */
10544void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10545{
10546 /* Paranoia checks: do not hold machine or media tree locks. */
10547 AssertReturnVoid(!isWriteLockOnCurrentThread());
10548 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10549
10550 ComObjPtr<Medium> pBase;
10551 {
10552 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10553 pBase = pMedium->i_getBase();
10554 }
10555
10556 /* Paranoia checks: do not hold medium locks. */
10557 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10558 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10559
10560 // decide which medium registry to use now that the medium is attached:
10561 Guid uuid;
10562 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10563 if (fCanHaveOwnMediaRegistry)
10564 // machine XML is VirtualBox 4.0 or higher:
10565 uuid = i_getId(); // machine UUID
10566 else
10567 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10568
10569 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10570 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10571 if (pMedium->i_addRegistry(uuid))
10572 mParent->i_markRegistryModified(uuid);
10573
10574 /* For more complex hard disk structures it can happen that the base
10575 * medium isn't yet associated with any medium registry. Do that now. */
10576 if (pMedium != pBase)
10577 {
10578 /* Tree lock needed by Medium::addRegistry when recursing. */
10579 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10580 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10581 {
10582 treeLock.release();
10583 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10584 treeLock.acquire();
10585 }
10586 if (pBase->i_addRegistryRecursive(uuid))
10587 {
10588 treeLock.release();
10589 mParent->i_markRegistryModified(uuid);
10590 }
10591 }
10592}
10593
10594/**
10595 * Creates differencing hard disks for all normal hard disks attached to this
10596 * machine and a new set of attachments to refer to created disks.
10597 *
10598 * Used when taking a snapshot or when deleting the current state. Gets called
10599 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10600 *
10601 * This method assumes that mMediumAttachments contains the original hard disk
10602 * attachments it needs to create diffs for. On success, these attachments will
10603 * be replaced with the created diffs.
10604 *
10605 * Attachments with non-normal hard disks are left as is.
10606 *
10607 * If @a aOnline is @c false then the original hard disks that require implicit
10608 * diffs will be locked for reading. Otherwise it is assumed that they are
10609 * already locked for writing (when the VM was started). Note that in the latter
10610 * case it is responsibility of the caller to lock the newly created diffs for
10611 * writing if this method succeeds.
10612 *
10613 * @param aProgress Progress object to run (must contain at least as
10614 * many operations left as the number of hard disks
10615 * attached).
10616 * @param aWeight Weight of this operation.
10617 * @param aOnline Whether the VM was online prior to this operation.
10618 *
10619 * @note The progress object is not marked as completed, neither on success nor
10620 * on failure. This is a responsibility of the caller.
10621 *
10622 * @note Locks this object and the media tree for writing.
10623 */
10624HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10625 ULONG aWeight,
10626 bool aOnline)
10627{
10628 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10629
10630 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10631 AssertReturn(!!pProgressControl, E_INVALIDARG);
10632
10633 AutoCaller autoCaller(this);
10634 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10635
10636 AutoMultiWriteLock2 alock(this->lockHandle(),
10637 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10638
10639 /* must be in a protective state because we release the lock below */
10640 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10641 || mData->mMachineState == MachineState_OnlineSnapshotting
10642 || mData->mMachineState == MachineState_LiveSnapshotting
10643 || mData->mMachineState == MachineState_RestoringSnapshot
10644 || mData->mMachineState == MachineState_DeletingSnapshot
10645 , E_FAIL);
10646
10647 HRESULT rc = S_OK;
10648
10649 // use appropriate locked media map (online or offline)
10650 MediumLockListMap lockedMediaOffline;
10651 MediumLockListMap *lockedMediaMap;
10652 if (aOnline)
10653 lockedMediaMap = &mData->mSession.mLockedMedia;
10654 else
10655 lockedMediaMap = &lockedMediaOffline;
10656
10657 try
10658 {
10659 if (!aOnline)
10660 {
10661 /* lock all attached hard disks early to detect "in use"
10662 * situations before creating actual diffs */
10663 for (MediumAttachmentList::const_iterator
10664 it = mMediumAttachments->begin();
10665 it != mMediumAttachments->end();
10666 ++it)
10667 {
10668 MediumAttachment *pAtt = *it;
10669 if (pAtt->i_getType() == DeviceType_HardDisk)
10670 {
10671 Medium *pMedium = pAtt->i_getMedium();
10672 Assert(pMedium);
10673
10674 MediumLockList *pMediumLockList(new MediumLockList());
10675 alock.release();
10676 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10677 NULL /* pToLockWrite */,
10678 false /* fMediumLockWriteAll */,
10679 NULL,
10680 *pMediumLockList);
10681 alock.acquire();
10682 if (FAILED(rc))
10683 {
10684 delete pMediumLockList;
10685 throw rc;
10686 }
10687 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10688 if (FAILED(rc))
10689 {
10690 throw setError(rc,
10691 tr("Collecting locking information for all attached media failed"));
10692 }
10693 }
10694 }
10695
10696 /* Now lock all media. If this fails, nothing is locked. */
10697 alock.release();
10698 rc = lockedMediaMap->Lock();
10699 alock.acquire();
10700 if (FAILED(rc))
10701 {
10702 throw setError(rc,
10703 tr("Locking of attached media failed"));
10704 }
10705 }
10706
10707 /* remember the current list (note that we don't use backup() since
10708 * mMediumAttachments may be already backed up) */
10709 MediumAttachmentList atts = *mMediumAttachments.data();
10710
10711 /* start from scratch */
10712 mMediumAttachments->clear();
10713
10714 /* go through remembered attachments and create diffs for normal hard
10715 * disks and attach them */
10716 for (MediumAttachmentList::const_iterator
10717 it = atts.begin();
10718 it != atts.end();
10719 ++it)
10720 {
10721 MediumAttachment *pAtt = *it;
10722
10723 DeviceType_T devType = pAtt->i_getType();
10724 Medium *pMedium = pAtt->i_getMedium();
10725
10726 if ( devType != DeviceType_HardDisk
10727 || pMedium == NULL
10728 || pMedium->i_getType() != MediumType_Normal)
10729 {
10730 /* copy the attachment as is */
10731
10732 /** @todo the progress object created in SessionMachine::TakeSnaphot
10733 * only expects operations for hard disks. Later other
10734 * device types need to show up in the progress as well. */
10735 if (devType == DeviceType_HardDisk)
10736 {
10737 if (pMedium == NULL)
10738 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10739 aWeight); // weight
10740 else
10741 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10742 pMedium->i_getBase()->i_getName().c_str()).raw(),
10743 aWeight); // weight
10744 }
10745
10746 mMediumAttachments->push_back(pAtt);
10747 continue;
10748 }
10749
10750 /* need a diff */
10751 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10752 pMedium->i_getBase()->i_getName().c_str()).raw(),
10753 aWeight); // weight
10754
10755 Utf8Str strFullSnapshotFolder;
10756 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10757
10758 ComObjPtr<Medium> diff;
10759 diff.createObject();
10760 // store the diff in the same registry as the parent
10761 // (this cannot fail here because we can't create implicit diffs for
10762 // unregistered images)
10763 Guid uuidRegistryParent;
10764 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10765 Assert(fInRegistry); NOREF(fInRegistry);
10766 rc = diff->init(mParent,
10767 pMedium->i_getPreferredDiffFormat(),
10768 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10769 uuidRegistryParent,
10770 DeviceType_HardDisk);
10771 if (FAILED(rc)) throw rc;
10772
10773 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10774 * the push_back? Looks like we're going to release medium with the
10775 * wrong kind of lock (general issue with if we fail anywhere at all)
10776 * and an orphaned VDI in the snapshots folder. */
10777
10778 /* update the appropriate lock list */
10779 MediumLockList *pMediumLockList;
10780 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10781 AssertComRCThrowRC(rc);
10782 if (aOnline)
10783 {
10784 alock.release();
10785 /* The currently attached medium will be read-only, change
10786 * the lock type to read. */
10787 rc = pMediumLockList->Update(pMedium, false);
10788 alock.acquire();
10789 AssertComRCThrowRC(rc);
10790 }
10791
10792 /* release the locks before the potentially lengthy operation */
10793 alock.release();
10794 rc = pMedium->i_createDiffStorage(diff,
10795 pMedium->i_getPreferredDiffVariant(),
10796 pMediumLockList,
10797 NULL /* aProgress */,
10798 true /* aWait */,
10799 false /* aNotify */);
10800 alock.acquire();
10801 if (FAILED(rc)) throw rc;
10802
10803 /* actual lock list update is done in Machine::i_commitMedia */
10804
10805 rc = diff->i_addBackReference(mData->mUuid);
10806 AssertComRCThrowRC(rc);
10807
10808 /* add a new attachment */
10809 ComObjPtr<MediumAttachment> attachment;
10810 attachment.createObject();
10811 rc = attachment->init(this,
10812 diff,
10813 pAtt->i_getControllerName(),
10814 pAtt->i_getPort(),
10815 pAtt->i_getDevice(),
10816 DeviceType_HardDisk,
10817 true /* aImplicit */,
10818 false /* aPassthrough */,
10819 false /* aTempEject */,
10820 pAtt->i_getNonRotational(),
10821 pAtt->i_getDiscard(),
10822 pAtt->i_getHotPluggable(),
10823 pAtt->i_getBandwidthGroup());
10824 if (FAILED(rc)) throw rc;
10825
10826 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10827 AssertComRCThrowRC(rc);
10828 mMediumAttachments->push_back(attachment);
10829 }
10830 }
10831 catch (HRESULT aRC) { rc = aRC; }
10832
10833 /* unlock all hard disks we locked when there is no VM */
10834 if (!aOnline)
10835 {
10836 ErrorInfoKeeper eik;
10837
10838 HRESULT rc1 = lockedMediaMap->Clear();
10839 AssertComRC(rc1);
10840 }
10841
10842 return rc;
10843}
10844
10845/**
10846 * Deletes implicit differencing hard disks created either by
10847 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10848 * mMediumAttachments.
10849 *
10850 * Note that to delete hard disks created by #attachDevice() this method is
10851 * called from #i_rollbackMedia() when the changes are rolled back.
10852 *
10853 * @note Locks this object and the media tree for writing.
10854 */
10855HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10856{
10857 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10858
10859 AutoCaller autoCaller(this);
10860 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10861
10862 AutoMultiWriteLock2 alock(this->lockHandle(),
10863 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10864
10865 /* We absolutely must have backed up state. */
10866 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10867
10868 /* Check if there are any implicitly created diff images. */
10869 bool fImplicitDiffs = false;
10870 for (MediumAttachmentList::const_iterator
10871 it = mMediumAttachments->begin();
10872 it != mMediumAttachments->end();
10873 ++it)
10874 {
10875 const ComObjPtr<MediumAttachment> &pAtt = *it;
10876 if (pAtt->i_isImplicit())
10877 {
10878 fImplicitDiffs = true;
10879 break;
10880 }
10881 }
10882 /* If there is nothing to do, leave early. This saves lots of image locking
10883 * effort. It also avoids a MachineStateChanged event without real reason.
10884 * This is important e.g. when loading a VM config, because there should be
10885 * no events. Otherwise API clients can become thoroughly confused for
10886 * inaccessible VMs (the code for loading VM configs uses this method for
10887 * cleanup if the config makes no sense), as they take such events as an
10888 * indication that the VM is alive, and they would force the VM config to
10889 * be reread, leading to an endless loop. */
10890 if (!fImplicitDiffs)
10891 return S_OK;
10892
10893 HRESULT rc = S_OK;
10894 MachineState_T oldState = mData->mMachineState;
10895
10896 /* will release the lock before the potentially lengthy operation,
10897 * so protect with the special state (unless already protected) */
10898 if ( oldState != MachineState_Snapshotting
10899 && oldState != MachineState_OnlineSnapshotting
10900 && oldState != MachineState_LiveSnapshotting
10901 && oldState != MachineState_RestoringSnapshot
10902 && oldState != MachineState_DeletingSnapshot
10903 && oldState != MachineState_DeletingSnapshotOnline
10904 && oldState != MachineState_DeletingSnapshotPaused
10905 )
10906 i_setMachineState(MachineState_SettingUp);
10907
10908 // use appropriate locked media map (online or offline)
10909 MediumLockListMap lockedMediaOffline;
10910 MediumLockListMap *lockedMediaMap;
10911 if (aOnline)
10912 lockedMediaMap = &mData->mSession.mLockedMedia;
10913 else
10914 lockedMediaMap = &lockedMediaOffline;
10915
10916 try
10917 {
10918 if (!aOnline)
10919 {
10920 /* lock all attached hard disks early to detect "in use"
10921 * situations before deleting actual diffs */
10922 for (MediumAttachmentList::const_iterator
10923 it = mMediumAttachments->begin();
10924 it != mMediumAttachments->end();
10925 ++it)
10926 {
10927 MediumAttachment *pAtt = *it;
10928 if (pAtt->i_getType() == DeviceType_HardDisk)
10929 {
10930 Medium *pMedium = pAtt->i_getMedium();
10931 Assert(pMedium);
10932
10933 MediumLockList *pMediumLockList(new MediumLockList());
10934 alock.release();
10935 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10936 NULL /* pToLockWrite */,
10937 false /* fMediumLockWriteAll */,
10938 NULL,
10939 *pMediumLockList);
10940 alock.acquire();
10941
10942 if (FAILED(rc))
10943 {
10944 delete pMediumLockList;
10945 throw rc;
10946 }
10947
10948 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10949 if (FAILED(rc))
10950 throw rc;
10951 }
10952 }
10953
10954 if (FAILED(rc))
10955 throw rc;
10956 } // end of offline
10957
10958 /* Lock lists are now up to date and include implicitly created media */
10959
10960 /* Go through remembered attachments and delete all implicitly created
10961 * diffs and fix up the attachment information */
10962 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10963 MediumAttachmentList implicitAtts;
10964 for (MediumAttachmentList::const_iterator
10965 it = mMediumAttachments->begin();
10966 it != mMediumAttachments->end();
10967 ++it)
10968 {
10969 ComObjPtr<MediumAttachment> pAtt = *it;
10970 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10971 if (pMedium.isNull())
10972 continue;
10973
10974 // Implicit attachments go on the list for deletion and back references are removed.
10975 if (pAtt->i_isImplicit())
10976 {
10977 /* Deassociate and mark for deletion */
10978 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10979 rc = pMedium->i_removeBackReference(mData->mUuid);
10980 if (FAILED(rc))
10981 throw rc;
10982 implicitAtts.push_back(pAtt);
10983 continue;
10984 }
10985
10986 /* Was this medium attached before? */
10987 if (!i_findAttachment(oldAtts, pMedium))
10988 {
10989 /* no: de-associate */
10990 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10991 rc = pMedium->i_removeBackReference(mData->mUuid);
10992 if (FAILED(rc))
10993 throw rc;
10994 continue;
10995 }
10996 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10997 }
10998
10999 /* If there are implicit attachments to delete, throw away the lock
11000 * map contents (which will unlock all media) since the medium
11001 * attachments will be rolled back. Below we need to completely
11002 * recreate the lock map anyway since it is infinitely complex to
11003 * do this incrementally (would need reconstructing each attachment
11004 * change, which would be extremely hairy). */
11005 if (implicitAtts.size() != 0)
11006 {
11007 ErrorInfoKeeper eik;
11008
11009 HRESULT rc1 = lockedMediaMap->Clear();
11010 AssertComRC(rc1);
11011 }
11012
11013 /* rollback hard disk changes */
11014 mMediumAttachments.rollback();
11015
11016 MultiResult mrc(S_OK);
11017
11018 // Delete unused implicit diffs.
11019 if (implicitAtts.size() != 0)
11020 {
11021 alock.release();
11022
11023 for (MediumAttachmentList::const_iterator
11024 it = implicitAtts.begin();
11025 it != implicitAtts.end();
11026 ++it)
11027 {
11028 // Remove medium associated with this attachment.
11029 ComObjPtr<MediumAttachment> pAtt = *it;
11030 Assert(pAtt);
11031 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11032 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11033 Assert(pMedium);
11034
11035 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11036 // continue on delete failure, just collect error messages
11037 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11038 pMedium->i_getLocationFull().c_str() ));
11039 mrc = rc;
11040 }
11041 // Clear the list of deleted implicit attachments now, while not
11042 // holding the lock, as it will ultimately trigger Medium::uninit()
11043 // calls which assume that the media tree lock isn't held.
11044 implicitAtts.clear();
11045
11046 alock.acquire();
11047
11048 /* if there is a VM recreate media lock map as mentioned above,
11049 * otherwise it is a waste of time and we leave things unlocked */
11050 if (aOnline)
11051 {
11052 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11053 /* must never be NULL, but better safe than sorry */
11054 if (!pMachine.isNull())
11055 {
11056 alock.release();
11057 rc = mData->mSession.mMachine->i_lockMedia();
11058 alock.acquire();
11059 if (FAILED(rc))
11060 throw rc;
11061 }
11062 }
11063 }
11064 }
11065 catch (HRESULT aRC) {rc = aRC;}
11066
11067 if (mData->mMachineState == MachineState_SettingUp)
11068 i_setMachineState(oldState);
11069
11070 /* unlock all hard disks we locked when there is no VM */
11071 if (!aOnline)
11072 {
11073 ErrorInfoKeeper eik;
11074
11075 HRESULT rc1 = lockedMediaMap->Clear();
11076 AssertComRC(rc1);
11077 }
11078
11079 return rc;
11080}
11081
11082
11083/**
11084 * Looks through the given list of media attachments for one with the given parameters
11085 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11086 * can be searched as well if needed.
11087 *
11088 * @param ll
11089 * @param aControllerName
11090 * @param aControllerPort
11091 * @param aDevice
11092 * @return
11093 */
11094MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11095 const Utf8Str &aControllerName,
11096 LONG aControllerPort,
11097 LONG aDevice)
11098{
11099 for (MediumAttachmentList::const_iterator
11100 it = ll.begin();
11101 it != ll.end();
11102 ++it)
11103 {
11104 MediumAttachment *pAttach = *it;
11105 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11106 return pAttach;
11107 }
11108
11109 return NULL;
11110}
11111
11112/**
11113 * Looks through the given list of media attachments for one with the given parameters
11114 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11115 * can be searched as well if needed.
11116 *
11117 * @param ll
11118 * @param pMedium
11119 * @return
11120 */
11121MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11122 ComObjPtr<Medium> pMedium)
11123{
11124 for (MediumAttachmentList::const_iterator
11125 it = ll.begin();
11126 it != ll.end();
11127 ++it)
11128 {
11129 MediumAttachment *pAttach = *it;
11130 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11131 if (pMediumThis == pMedium)
11132 return pAttach;
11133 }
11134
11135 return NULL;
11136}
11137
11138/**
11139 * Looks through the given list of media attachments for one with the given parameters
11140 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11141 * can be searched as well if needed.
11142 *
11143 * @param ll
11144 * @param id
11145 * @return
11146 */
11147MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11148 Guid &id)
11149{
11150 for (MediumAttachmentList::const_iterator
11151 it = ll.begin();
11152 it != ll.end();
11153 ++it)
11154 {
11155 MediumAttachment *pAttach = *it;
11156 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11157 if (pMediumThis->i_getId() == id)
11158 return pAttach;
11159 }
11160
11161 return NULL;
11162}
11163
11164/**
11165 * Main implementation for Machine::DetachDevice. This also gets called
11166 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11167 *
11168 * @param pAttach Medium attachment to detach.
11169 * @param writeLock Machine write lock which the caller must have locked once.
11170 * This may be released temporarily in here.
11171 * @param pSnapshot If NULL, then the detachment is for the current machine.
11172 * Otherwise this is for a SnapshotMachine, and this must be
11173 * its snapshot.
11174 * @return
11175 */
11176HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11177 AutoWriteLock &writeLock,
11178 Snapshot *pSnapshot)
11179{
11180 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11181 DeviceType_T mediumType = pAttach->i_getType();
11182
11183 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11184
11185 if (pAttach->i_isImplicit())
11186 {
11187 /* attempt to implicitly delete the implicitly created diff */
11188
11189 /// @todo move the implicit flag from MediumAttachment to Medium
11190 /// and forbid any hard disk operation when it is implicit. Or maybe
11191 /// a special media state for it to make it even more simple.
11192
11193 Assert(mMediumAttachments.isBackedUp());
11194
11195 /* will release the lock before the potentially lengthy operation, so
11196 * protect with the special state */
11197 MachineState_T oldState = mData->mMachineState;
11198 i_setMachineState(MachineState_SettingUp);
11199
11200 writeLock.release();
11201
11202 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11203 true /*aWait*/,
11204 false /*aNotify*/);
11205
11206 writeLock.acquire();
11207
11208 i_setMachineState(oldState);
11209
11210 if (FAILED(rc)) return rc;
11211 }
11212
11213 i_setModified(IsModified_Storage);
11214 mMediumAttachments.backup();
11215 mMediumAttachments->remove(pAttach);
11216
11217 if (!oldmedium.isNull())
11218 {
11219 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11220 if (pSnapshot)
11221 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11222 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11223 else if (mediumType != DeviceType_HardDisk)
11224 oldmedium->i_removeBackReference(mData->mUuid);
11225 }
11226
11227 return S_OK;
11228}
11229
11230/**
11231 * Goes thru all media of the given list and
11232 *
11233 * 1) calls i_detachDevice() on each of them for this machine and
11234 * 2) adds all Medium objects found in the process to the given list,
11235 * depending on cleanupMode.
11236 *
11237 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11238 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11239 * media to the list.
11240 *
11241 * This gets called from Machine::Unregister, both for the actual Machine and
11242 * the SnapshotMachine objects that might be found in the snapshots.
11243 *
11244 * Requires caller and locking. The machine lock must be passed in because it
11245 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11246 *
11247 * @param writeLock Machine lock from top-level caller; this gets passed to
11248 * i_detachDevice.
11249 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11250 * object if called for a SnapshotMachine.
11251 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11252 * added to llMedia; if Full, then all media get added;
11253 * otherwise no media get added.
11254 * @param llMedia Caller's list to receive Medium objects which got detached so
11255 * caller can close() them, depending on cleanupMode.
11256 * @return
11257 */
11258HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11259 Snapshot *pSnapshot,
11260 CleanupMode_T cleanupMode,
11261 MediaList &llMedia)
11262{
11263 Assert(isWriteLockOnCurrentThread());
11264
11265 HRESULT rc;
11266
11267 // make a temporary list because i_detachDevice invalidates iterators into
11268 // mMediumAttachments
11269 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11270
11271 for (MediumAttachmentList::iterator
11272 it = llAttachments2.begin();
11273 it != llAttachments2.end();
11274 ++it)
11275 {
11276 ComObjPtr<MediumAttachment> &pAttach = *it;
11277 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11278
11279 if (!pMedium.isNull())
11280 {
11281 AutoCaller mac(pMedium);
11282 if (FAILED(mac.rc())) return mac.rc();
11283 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11284 DeviceType_T devType = pMedium->i_getDeviceType();
11285 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11286 && devType == DeviceType_HardDisk)
11287 || (cleanupMode == CleanupMode_Full)
11288 )
11289 {
11290 llMedia.push_back(pMedium);
11291 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11292 /* Not allowed to keep this lock as below we need the parent
11293 * medium lock, and the lock order is parent to child. */
11294 lock.release();
11295 /*
11296 * Search for medias which are not attached to any machine, but
11297 * in the chain to an attached disk. Mediums are only consided
11298 * if they are:
11299 * - have only one child
11300 * - no references to any machines
11301 * - are of normal medium type
11302 */
11303 while (!pParent.isNull())
11304 {
11305 AutoCaller mac1(pParent);
11306 if (FAILED(mac1.rc())) return mac1.rc();
11307 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11308 if (pParent->i_getChildren().size() == 1)
11309 {
11310 if ( pParent->i_getMachineBackRefCount() == 0
11311 && pParent->i_getType() == MediumType_Normal
11312 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11313 llMedia.push_back(pParent);
11314 }
11315 else
11316 break;
11317 pParent = pParent->i_getParent();
11318 }
11319 }
11320 }
11321
11322 // real machine: then we need to use the proper method
11323 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11324
11325 if (FAILED(rc))
11326 return rc;
11327 }
11328
11329 return S_OK;
11330}
11331
11332/**
11333 * Perform deferred hard disk detachments.
11334 *
11335 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11336 * changed (not backed up).
11337 *
11338 * If @a aOnline is @c true then this method will also unlock the old hard
11339 * disks for which the new implicit diffs were created and will lock these new
11340 * diffs for writing.
11341 *
11342 * @param aOnline Whether the VM was online prior to this operation.
11343 *
11344 * @note Locks this object for writing!
11345 */
11346void Machine::i_commitMedia(bool aOnline /*= false*/)
11347{
11348 AutoCaller autoCaller(this);
11349 AssertComRCReturnVoid(autoCaller.rc());
11350
11351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11352
11353 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11354
11355 HRESULT rc = S_OK;
11356
11357 /* no attach/detach operations -- nothing to do */
11358 if (!mMediumAttachments.isBackedUp())
11359 return;
11360
11361 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11362 bool fMediaNeedsLocking = false;
11363
11364 /* enumerate new attachments */
11365 for (MediumAttachmentList::const_iterator
11366 it = mMediumAttachments->begin();
11367 it != mMediumAttachments->end();
11368 ++it)
11369 {
11370 MediumAttachment *pAttach = *it;
11371
11372 pAttach->i_commit();
11373
11374 Medium *pMedium = pAttach->i_getMedium();
11375 bool fImplicit = pAttach->i_isImplicit();
11376
11377 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11378 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11379 fImplicit));
11380
11381 /** @todo convert all this Machine-based voodoo to MediumAttachment
11382 * based commit logic. */
11383 if (fImplicit)
11384 {
11385 /* convert implicit attachment to normal */
11386 pAttach->i_setImplicit(false);
11387
11388 if ( aOnline
11389 && pMedium
11390 && pAttach->i_getType() == DeviceType_HardDisk
11391 )
11392 {
11393 /* update the appropriate lock list */
11394 MediumLockList *pMediumLockList;
11395 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11396 AssertComRC(rc);
11397 if (pMediumLockList)
11398 {
11399 /* unlock if there's a need to change the locking */
11400 if (!fMediaNeedsLocking)
11401 {
11402 rc = mData->mSession.mLockedMedia.Unlock();
11403 AssertComRC(rc);
11404 fMediaNeedsLocking = true;
11405 }
11406 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11407 AssertComRC(rc);
11408 rc = pMediumLockList->Append(pMedium, true);
11409 AssertComRC(rc);
11410 }
11411 }
11412
11413 continue;
11414 }
11415
11416 if (pMedium)
11417 {
11418 /* was this medium attached before? */
11419 for (MediumAttachmentList::iterator
11420 oldIt = oldAtts.begin();
11421 oldIt != oldAtts.end();
11422 ++oldIt)
11423 {
11424 MediumAttachment *pOldAttach = *oldIt;
11425 if (pOldAttach->i_getMedium() == pMedium)
11426 {
11427 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11428
11429 /* yes: remove from old to avoid de-association */
11430 oldAtts.erase(oldIt);
11431 break;
11432 }
11433 }
11434 }
11435 }
11436
11437 /* enumerate remaining old attachments and de-associate from the
11438 * current machine state */
11439 for (MediumAttachmentList::const_iterator
11440 it = oldAtts.begin();
11441 it != oldAtts.end();
11442 ++it)
11443 {
11444 MediumAttachment *pAttach = *it;
11445 Medium *pMedium = pAttach->i_getMedium();
11446
11447 /* Detach only hard disks, since DVD/floppy media is detached
11448 * instantly in MountMedium. */
11449 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11450 {
11451 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11452
11453 /* now de-associate from the current machine state */
11454 rc = pMedium->i_removeBackReference(mData->mUuid);
11455 AssertComRC(rc);
11456
11457 if (aOnline)
11458 {
11459 /* unlock since medium is not used anymore */
11460 MediumLockList *pMediumLockList;
11461 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11462 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11463 {
11464 /* this happens for online snapshots, there the attachment
11465 * is changing, but only to a diff image created under
11466 * the old one, so there is no separate lock list */
11467 Assert(!pMediumLockList);
11468 }
11469 else
11470 {
11471 AssertComRC(rc);
11472 if (pMediumLockList)
11473 {
11474 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11475 AssertComRC(rc);
11476 }
11477 }
11478 }
11479 }
11480 }
11481
11482 /* take media locks again so that the locking state is consistent */
11483 if (fMediaNeedsLocking)
11484 {
11485 Assert(aOnline);
11486 rc = mData->mSession.mLockedMedia.Lock();
11487 AssertComRC(rc);
11488 }
11489
11490 /* commit the hard disk changes */
11491 mMediumAttachments.commit();
11492
11493 if (i_isSessionMachine())
11494 {
11495 /*
11496 * Update the parent machine to point to the new owner.
11497 * This is necessary because the stored parent will point to the
11498 * session machine otherwise and cause crashes or errors later
11499 * when the session machine gets invalid.
11500 */
11501 /** @todo Change the MediumAttachment class to behave like any other
11502 * class in this regard by creating peer MediumAttachment
11503 * objects for session machines and share the data with the peer
11504 * machine.
11505 */
11506 for (MediumAttachmentList::const_iterator
11507 it = mMediumAttachments->begin();
11508 it != mMediumAttachments->end();
11509 ++it)
11510 (*it)->i_updateParentMachine(mPeer);
11511
11512 /* attach new data to the primary machine and reshare it */
11513 mPeer->mMediumAttachments.attach(mMediumAttachments);
11514 }
11515
11516 return;
11517}
11518
11519/**
11520 * Perform deferred deletion of implicitly created diffs.
11521 *
11522 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11523 * changed (not backed up).
11524 *
11525 * @note Locks this object for writing!
11526 */
11527void Machine::i_rollbackMedia()
11528{
11529 AutoCaller autoCaller(this);
11530 AssertComRCReturnVoid(autoCaller.rc());
11531
11532 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11533 LogFlowThisFunc(("Entering rollbackMedia\n"));
11534
11535 HRESULT rc = S_OK;
11536
11537 /* no attach/detach operations -- nothing to do */
11538 if (!mMediumAttachments.isBackedUp())
11539 return;
11540
11541 /* enumerate new attachments */
11542 for (MediumAttachmentList::const_iterator
11543 it = mMediumAttachments->begin();
11544 it != mMediumAttachments->end();
11545 ++it)
11546 {
11547 MediumAttachment *pAttach = *it;
11548 /* Fix up the backrefs for DVD/floppy media. */
11549 if (pAttach->i_getType() != DeviceType_HardDisk)
11550 {
11551 Medium *pMedium = pAttach->i_getMedium();
11552 if (pMedium)
11553 {
11554 rc = pMedium->i_removeBackReference(mData->mUuid);
11555 AssertComRC(rc);
11556 }
11557 }
11558
11559 (*it)->i_rollback();
11560
11561 pAttach = *it;
11562 /* Fix up the backrefs for DVD/floppy media. */
11563 if (pAttach->i_getType() != DeviceType_HardDisk)
11564 {
11565 Medium *pMedium = pAttach->i_getMedium();
11566 if (pMedium)
11567 {
11568 rc = pMedium->i_addBackReference(mData->mUuid);
11569 AssertComRC(rc);
11570 }
11571 }
11572 }
11573
11574 /** @todo convert all this Machine-based voodoo to MediumAttachment
11575 * based rollback logic. */
11576 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11577
11578 return;
11579}
11580
11581/**
11582 * Returns true if the settings file is located in the directory named exactly
11583 * as the machine; this means, among other things, that the machine directory
11584 * should be auto-renamed.
11585 *
11586 * @param aSettingsDir if not NULL, the full machine settings file directory
11587 * name will be assigned there.
11588 *
11589 * @note Doesn't lock anything.
11590 * @note Not thread safe (must be called from this object's lock).
11591 */
11592bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11593{
11594 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11595 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11596 if (aSettingsDir)
11597 *aSettingsDir = strMachineDirName;
11598 strMachineDirName.stripPath(); // vmname
11599 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11600 strConfigFileOnly.stripPath() // vmname.vbox
11601 .stripSuffix(); // vmname
11602 /** @todo hack, make somehow use of ComposeMachineFilename */
11603 if (mUserData->s.fDirectoryIncludesUUID)
11604 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11605
11606 AssertReturn(!strMachineDirName.isEmpty(), false);
11607 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11608
11609 return strMachineDirName == strConfigFileOnly;
11610}
11611
11612/**
11613 * Discards all changes to machine settings.
11614 *
11615 * @param aNotify Whether to notify the direct session about changes or not.
11616 *
11617 * @note Locks objects for writing!
11618 */
11619void Machine::i_rollback(bool aNotify)
11620{
11621 AutoCaller autoCaller(this);
11622 AssertComRCReturn(autoCaller.rc(), (void)0);
11623
11624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11625
11626 if (!mStorageControllers.isNull())
11627 {
11628 if (mStorageControllers.isBackedUp())
11629 {
11630 /* unitialize all new devices (absent in the backed up list). */
11631 StorageControllerList *backedList = mStorageControllers.backedUpData();
11632 for (StorageControllerList::const_iterator
11633 it = mStorageControllers->begin();
11634 it != mStorageControllers->end();
11635 ++it)
11636 {
11637 if ( std::find(backedList->begin(), backedList->end(), *it)
11638 == backedList->end()
11639 )
11640 {
11641 (*it)->uninit();
11642 }
11643 }
11644
11645 /* restore the list */
11646 mStorageControllers.rollback();
11647 }
11648
11649 /* rollback any changes to devices after restoring the list */
11650 if (mData->flModifications & IsModified_Storage)
11651 {
11652 for (StorageControllerList::const_iterator
11653 it = mStorageControllers->begin();
11654 it != mStorageControllers->end();
11655 ++it)
11656 {
11657 (*it)->i_rollback();
11658 }
11659 }
11660 }
11661
11662 if (!mUSBControllers.isNull())
11663 {
11664 if (mUSBControllers.isBackedUp())
11665 {
11666 /* unitialize all new devices (absent in the backed up list). */
11667 USBControllerList *backedList = mUSBControllers.backedUpData();
11668 for (USBControllerList::const_iterator
11669 it = mUSBControllers->begin();
11670 it != mUSBControllers->end();
11671 ++it)
11672 {
11673 if ( std::find(backedList->begin(), backedList->end(), *it)
11674 == backedList->end()
11675 )
11676 {
11677 (*it)->uninit();
11678 }
11679 }
11680
11681 /* restore the list */
11682 mUSBControllers.rollback();
11683 }
11684
11685 /* rollback any changes to devices after restoring the list */
11686 if (mData->flModifications & IsModified_USB)
11687 {
11688 for (USBControllerList::const_iterator
11689 it = mUSBControllers->begin();
11690 it != mUSBControllers->end();
11691 ++it)
11692 {
11693 (*it)->i_rollback();
11694 }
11695 }
11696 }
11697
11698 mUserData.rollback();
11699
11700 mHWData.rollback();
11701
11702 if (mData->flModifications & IsModified_Storage)
11703 i_rollbackMedia();
11704
11705 if (mBIOSSettings)
11706 mBIOSSettings->i_rollback();
11707
11708 if (mTrustedPlatformModule)
11709 mTrustedPlatformModule->i_rollback();
11710
11711 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11712 mRecordingSettings->i_rollback();
11713
11714 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11715 mGraphicsAdapter->i_rollback();
11716
11717 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11718 mVRDEServer->i_rollback();
11719
11720 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11721 mAudioAdapter->i_rollback();
11722
11723 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11724 mUSBDeviceFilters->i_rollback();
11725
11726 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11727 mBandwidthControl->i_rollback();
11728
11729 if (!mHWData.isNull())
11730 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11731 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11732 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11733 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11734
11735 if (mData->flModifications & IsModified_NetworkAdapters)
11736 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11737 if ( mNetworkAdapters[slot]
11738 && mNetworkAdapters[slot]->i_isModified())
11739 {
11740 mNetworkAdapters[slot]->i_rollback();
11741 networkAdapters[slot] = mNetworkAdapters[slot];
11742 }
11743
11744 if (mData->flModifications & IsModified_SerialPorts)
11745 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11746 if ( mSerialPorts[slot]
11747 && mSerialPorts[slot]->i_isModified())
11748 {
11749 mSerialPorts[slot]->i_rollback();
11750 serialPorts[slot] = mSerialPorts[slot];
11751 }
11752
11753 if (mData->flModifications & IsModified_ParallelPorts)
11754 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11755 if ( mParallelPorts[slot]
11756 && mParallelPorts[slot]->i_isModified())
11757 {
11758 mParallelPorts[slot]->i_rollback();
11759 parallelPorts[slot] = mParallelPorts[slot];
11760 }
11761
11762 if (aNotify)
11763 {
11764 /* inform the direct session about changes */
11765
11766 ComObjPtr<Machine> that = this;
11767 uint32_t flModifications = mData->flModifications;
11768 alock.release();
11769
11770 if (flModifications & IsModified_SharedFolders)
11771 that->i_onSharedFolderChange();
11772
11773 if (flModifications & IsModified_VRDEServer)
11774 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11775 if (flModifications & IsModified_USB)
11776 that->i_onUSBControllerChange();
11777
11778 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11779 if (networkAdapters[slot])
11780 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11781 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11782 if (serialPorts[slot])
11783 that->i_onSerialPortChange(serialPorts[slot]);
11784 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11785 if (parallelPorts[slot])
11786 that->i_onParallelPortChange(parallelPorts[slot]);
11787
11788 if (flModifications & IsModified_Storage)
11789 {
11790 for (StorageControllerList::const_iterator
11791 it = mStorageControllers->begin();
11792 it != mStorageControllers->end();
11793 ++it)
11794 {
11795 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11796 }
11797 }
11798
11799
11800#if 0
11801 if (flModifications & IsModified_BandwidthControl)
11802 that->onBandwidthControlChange();
11803#endif
11804 }
11805}
11806
11807/**
11808 * Commits all the changes to machine settings.
11809 *
11810 * Note that this operation is supposed to never fail.
11811 *
11812 * @note Locks this object and children for writing.
11813 */
11814void Machine::i_commit()
11815{
11816 AutoCaller autoCaller(this);
11817 AssertComRCReturnVoid(autoCaller.rc());
11818
11819 AutoCaller peerCaller(mPeer);
11820 AssertComRCReturnVoid(peerCaller.rc());
11821
11822 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11823
11824 /*
11825 * use safe commit to ensure Snapshot machines (that share mUserData)
11826 * will still refer to a valid memory location
11827 */
11828 mUserData.commitCopy();
11829
11830 mHWData.commit();
11831
11832 if (mMediumAttachments.isBackedUp())
11833 i_commitMedia(Global::IsOnline(mData->mMachineState));
11834
11835 mBIOSSettings->i_commit();
11836 mTrustedPlatformModule->i_commit();
11837 mRecordingSettings->i_commit();
11838 mGraphicsAdapter->i_commit();
11839 mVRDEServer->i_commit();
11840 mAudioAdapter->i_commit();
11841 mUSBDeviceFilters->i_commit();
11842 mBandwidthControl->i_commit();
11843
11844 /* Since mNetworkAdapters is a list which might have been changed (resized)
11845 * without using the Backupable<> template we need to handle the copying
11846 * of the list entries manually, including the creation of peers for the
11847 * new objects. */
11848 bool commitNetworkAdapters = false;
11849 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11850 if (mPeer)
11851 {
11852 /* commit everything, even the ones which will go away */
11853 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11854 mNetworkAdapters[slot]->i_commit();
11855 /* copy over the new entries, creating a peer and uninit the original */
11856 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11857 for (size_t slot = 0; slot < newSize; slot++)
11858 {
11859 /* look if this adapter has a peer device */
11860 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11861 if (!peer)
11862 {
11863 /* no peer means the adapter is a newly created one;
11864 * create a peer owning data this data share it with */
11865 peer.createObject();
11866 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11867 }
11868 mPeer->mNetworkAdapters[slot] = peer;
11869 }
11870 /* uninit any no longer needed network adapters */
11871 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11872 mNetworkAdapters[slot]->uninit();
11873 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11874 {
11875 if (mPeer->mNetworkAdapters[slot])
11876 mPeer->mNetworkAdapters[slot]->uninit();
11877 }
11878 /* Keep the original network adapter count until this point, so that
11879 * discarding a chipset type change will not lose settings. */
11880 mNetworkAdapters.resize(newSize);
11881 mPeer->mNetworkAdapters.resize(newSize);
11882 }
11883 else
11884 {
11885 /* we have no peer (our parent is the newly created machine);
11886 * just commit changes to the network adapters */
11887 commitNetworkAdapters = true;
11888 }
11889 if (commitNetworkAdapters)
11890 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11891 mNetworkAdapters[slot]->i_commit();
11892
11893 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11894 mSerialPorts[slot]->i_commit();
11895 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11896 mParallelPorts[slot]->i_commit();
11897
11898 bool commitStorageControllers = false;
11899
11900 if (mStorageControllers.isBackedUp())
11901 {
11902 mStorageControllers.commit();
11903
11904 if (mPeer)
11905 {
11906 /* Commit all changes to new controllers (this will reshare data with
11907 * peers for those who have peers) */
11908 StorageControllerList *newList = new StorageControllerList();
11909 for (StorageControllerList::const_iterator
11910 it = mStorageControllers->begin();
11911 it != mStorageControllers->end();
11912 ++it)
11913 {
11914 (*it)->i_commit();
11915
11916 /* look if this controller has a peer device */
11917 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11918 if (!peer)
11919 {
11920 /* no peer means the device is a newly created one;
11921 * create a peer owning data this device share it with */
11922 peer.createObject();
11923 peer->init(mPeer, *it, true /* aReshare */);
11924 }
11925 else
11926 {
11927 /* remove peer from the old list */
11928 mPeer->mStorageControllers->remove(peer);
11929 }
11930 /* and add it to the new list */
11931 newList->push_back(peer);
11932 }
11933
11934 /* uninit old peer's controllers that are left */
11935 for (StorageControllerList::const_iterator
11936 it = mPeer->mStorageControllers->begin();
11937 it != mPeer->mStorageControllers->end();
11938 ++it)
11939 {
11940 (*it)->uninit();
11941 }
11942
11943 /* attach new list of controllers to our peer */
11944 mPeer->mStorageControllers.attach(newList);
11945 }
11946 else
11947 {
11948 /* we have no peer (our parent is the newly created machine);
11949 * just commit changes to devices */
11950 commitStorageControllers = true;
11951 }
11952 }
11953 else
11954 {
11955 /* the list of controllers itself is not changed,
11956 * just commit changes to controllers themselves */
11957 commitStorageControllers = true;
11958 }
11959
11960 if (commitStorageControllers)
11961 {
11962 for (StorageControllerList::const_iterator
11963 it = mStorageControllers->begin();
11964 it != mStorageControllers->end();
11965 ++it)
11966 {
11967 (*it)->i_commit();
11968 }
11969 }
11970
11971 bool commitUSBControllers = false;
11972
11973 if (mUSBControllers.isBackedUp())
11974 {
11975 mUSBControllers.commit();
11976
11977 if (mPeer)
11978 {
11979 /* Commit all changes to new controllers (this will reshare data with
11980 * peers for those who have peers) */
11981 USBControllerList *newList = new USBControllerList();
11982 for (USBControllerList::const_iterator
11983 it = mUSBControllers->begin();
11984 it != mUSBControllers->end();
11985 ++it)
11986 {
11987 (*it)->i_commit();
11988
11989 /* look if this controller has a peer device */
11990 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11991 if (!peer)
11992 {
11993 /* no peer means the device is a newly created one;
11994 * create a peer owning data this device share it with */
11995 peer.createObject();
11996 peer->init(mPeer, *it, true /* aReshare */);
11997 }
11998 else
11999 {
12000 /* remove peer from the old list */
12001 mPeer->mUSBControllers->remove(peer);
12002 }
12003 /* and add it to the new list */
12004 newList->push_back(peer);
12005 }
12006
12007 /* uninit old peer's controllers that are left */
12008 for (USBControllerList::const_iterator
12009 it = mPeer->mUSBControllers->begin();
12010 it != mPeer->mUSBControllers->end();
12011 ++it)
12012 {
12013 (*it)->uninit();
12014 }
12015
12016 /* attach new list of controllers to our peer */
12017 mPeer->mUSBControllers.attach(newList);
12018 }
12019 else
12020 {
12021 /* we have no peer (our parent is the newly created machine);
12022 * just commit changes to devices */
12023 commitUSBControllers = true;
12024 }
12025 }
12026 else
12027 {
12028 /* the list of controllers itself is not changed,
12029 * just commit changes to controllers themselves */
12030 commitUSBControllers = true;
12031 }
12032
12033 if (commitUSBControllers)
12034 {
12035 for (USBControllerList::const_iterator
12036 it = mUSBControllers->begin();
12037 it != mUSBControllers->end();
12038 ++it)
12039 {
12040 (*it)->i_commit();
12041 }
12042 }
12043
12044 if (i_isSessionMachine())
12045 {
12046 /* attach new data to the primary machine and reshare it */
12047 mPeer->mUserData.attach(mUserData);
12048 mPeer->mHWData.attach(mHWData);
12049 /* mmMediumAttachments is reshared by fixupMedia */
12050 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12051 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12052 }
12053}
12054
12055/**
12056 * Copies all the hardware data from the given machine.
12057 *
12058 * Currently, only called when the VM is being restored from a snapshot. In
12059 * particular, this implies that the VM is not running during this method's
12060 * call.
12061 *
12062 * @note This method must be called from under this object's lock.
12063 *
12064 * @note This method doesn't call #i_commit(), so all data remains backed up and
12065 * unsaved.
12066 */
12067void Machine::i_copyFrom(Machine *aThat)
12068{
12069 AssertReturnVoid(!i_isSnapshotMachine());
12070 AssertReturnVoid(aThat->i_isSnapshotMachine());
12071
12072 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12073
12074 mHWData.assignCopy(aThat->mHWData);
12075
12076 // create copies of all shared folders (mHWData after attaching a copy
12077 // contains just references to original objects)
12078 for (HWData::SharedFolderList::iterator
12079 it = mHWData->mSharedFolders.begin();
12080 it != mHWData->mSharedFolders.end();
12081 ++it)
12082 {
12083 ComObjPtr<SharedFolder> folder;
12084 folder.createObject();
12085 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12086 AssertComRC(rc);
12087 *it = folder;
12088 }
12089
12090 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12091 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12092 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12093 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12094 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12095 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12096 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12097 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12098
12099 /* create private copies of all controllers */
12100 mStorageControllers.backup();
12101 mStorageControllers->clear();
12102 for (StorageControllerList::const_iterator
12103 it = aThat->mStorageControllers->begin();
12104 it != aThat->mStorageControllers->end();
12105 ++it)
12106 {
12107 ComObjPtr<StorageController> ctrl;
12108 ctrl.createObject();
12109 ctrl->initCopy(this, *it);
12110 mStorageControllers->push_back(ctrl);
12111 }
12112
12113 /* create private copies of all USB controllers */
12114 mUSBControllers.backup();
12115 mUSBControllers->clear();
12116 for (USBControllerList::const_iterator
12117 it = aThat->mUSBControllers->begin();
12118 it != aThat->mUSBControllers->end();
12119 ++it)
12120 {
12121 ComObjPtr<USBController> ctrl;
12122 ctrl.createObject();
12123 ctrl->initCopy(this, *it);
12124 mUSBControllers->push_back(ctrl);
12125 }
12126
12127 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12128 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12129 {
12130 if (mNetworkAdapters[slot].isNotNull())
12131 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12132 else
12133 {
12134 unconst(mNetworkAdapters[slot]).createObject();
12135 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12136 }
12137 }
12138 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12139 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12140 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12141 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12142}
12143
12144/**
12145 * Returns whether the given storage controller is hotplug capable.
12146 *
12147 * @returns true if the controller supports hotplugging
12148 * false otherwise.
12149 * @param enmCtrlType The controller type to check for.
12150 */
12151bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12152{
12153 ComPtr<ISystemProperties> systemProperties;
12154 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12155 if (FAILED(rc))
12156 return false;
12157
12158 BOOL aHotplugCapable = FALSE;
12159 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12160
12161 return RT_BOOL(aHotplugCapable);
12162}
12163
12164#ifdef VBOX_WITH_RESOURCE_USAGE_API
12165
12166void Machine::i_getDiskList(MediaList &list)
12167{
12168 for (MediumAttachmentList::const_iterator
12169 it = mMediumAttachments->begin();
12170 it != mMediumAttachments->end();
12171 ++it)
12172 {
12173 MediumAttachment *pAttach = *it;
12174 /* just in case */
12175 AssertContinue(pAttach);
12176
12177 AutoCaller localAutoCallerA(pAttach);
12178 if (FAILED(localAutoCallerA.rc())) continue;
12179
12180 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12181
12182 if (pAttach->i_getType() == DeviceType_HardDisk)
12183 list.push_back(pAttach->i_getMedium());
12184 }
12185}
12186
12187void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12188{
12189 AssertReturnVoid(isWriteLockOnCurrentThread());
12190 AssertPtrReturnVoid(aCollector);
12191
12192 pm::CollectorHAL *hal = aCollector->getHAL();
12193 /* Create sub metrics */
12194 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12195 "Percentage of processor time spent in user mode by the VM process.");
12196 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12197 "Percentage of processor time spent in kernel mode by the VM process.");
12198 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12199 "Size of resident portion of VM process in memory.");
12200 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12201 "Actual size of all VM disks combined.");
12202 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12203 "Network receive rate.");
12204 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12205 "Network transmit rate.");
12206 /* Create and register base metrics */
12207 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12208 cpuLoadUser, cpuLoadKernel);
12209 aCollector->registerBaseMetric(cpuLoad);
12210 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12211 ramUsageUsed);
12212 aCollector->registerBaseMetric(ramUsage);
12213 MediaList disks;
12214 i_getDiskList(disks);
12215 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12216 diskUsageUsed);
12217 aCollector->registerBaseMetric(diskUsage);
12218
12219 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12220 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12221 new pm::AggregateAvg()));
12222 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12223 new pm::AggregateMin()));
12224 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12225 new pm::AggregateMax()));
12226 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12228 new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12230 new pm::AggregateMin()));
12231 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12232 new pm::AggregateMax()));
12233
12234 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12235 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12236 new pm::AggregateAvg()));
12237 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12238 new pm::AggregateMin()));
12239 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12240 new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12243 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12244 new pm::AggregateAvg()));
12245 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12246 new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12248 new pm::AggregateMax()));
12249
12250
12251 /* Guest metrics collector */
12252 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12253 aCollector->registerGuest(mCollectorGuest);
12254 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12255
12256 /* Create sub metrics */
12257 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12258 "Percentage of processor time spent in user mode as seen by the guest.");
12259 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12260 "Percentage of processor time spent in kernel mode as seen by the guest.");
12261 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12262 "Percentage of processor time spent idling as seen by the guest.");
12263
12264 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12265 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12266 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12267 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12268 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12269 pm::SubMetric *guestMemCache = new pm::SubMetric(
12270 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12271
12272 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12273 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12274
12275 /* Create and register base metrics */
12276 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12277 machineNetRx, machineNetTx);
12278 aCollector->registerBaseMetric(machineNetRate);
12279
12280 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12281 guestLoadUser, guestLoadKernel, guestLoadIdle);
12282 aCollector->registerBaseMetric(guestCpuLoad);
12283
12284 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12285 guestMemTotal, guestMemFree,
12286 guestMemBalloon, guestMemShared,
12287 guestMemCache, guestPagedTotal);
12288 aCollector->registerBaseMetric(guestCpuMem);
12289
12290 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12291 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12296 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12301 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12306 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12314
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12319
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12329
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12344}
12345
12346void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12347{
12348 AssertReturnVoid(isWriteLockOnCurrentThread());
12349
12350 if (aCollector)
12351 {
12352 aCollector->unregisterMetricsFor(aMachine);
12353 aCollector->unregisterBaseMetricsFor(aMachine);
12354 }
12355}
12356
12357#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12358
12359
12360////////////////////////////////////////////////////////////////////////////////
12361
12362DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12363
12364HRESULT SessionMachine::FinalConstruct()
12365{
12366 LogFlowThisFunc(("\n"));
12367
12368 mClientToken = NULL;
12369
12370 return BaseFinalConstruct();
12371}
12372
12373void SessionMachine::FinalRelease()
12374{
12375 LogFlowThisFunc(("\n"));
12376
12377 Assert(!mClientToken);
12378 /* paranoia, should not hang around any more */
12379 if (mClientToken)
12380 {
12381 delete mClientToken;
12382 mClientToken = NULL;
12383 }
12384
12385 uninit(Uninit::Unexpected);
12386
12387 BaseFinalRelease();
12388}
12389
12390/**
12391 * @note Must be called only by Machine::LockMachine() from its own write lock.
12392 */
12393HRESULT SessionMachine::init(Machine *aMachine)
12394{
12395 LogFlowThisFuncEnter();
12396 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12397
12398 AssertReturn(aMachine, E_INVALIDARG);
12399
12400 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12401
12402 /* Enclose the state transition NotReady->InInit->Ready */
12403 AutoInitSpan autoInitSpan(this);
12404 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12405
12406 HRESULT rc = S_OK;
12407
12408 RT_ZERO(mAuthLibCtx);
12409
12410 /* create the machine client token */
12411 try
12412 {
12413 mClientToken = new ClientToken(aMachine, this);
12414 if (!mClientToken->isReady())
12415 {
12416 delete mClientToken;
12417 mClientToken = NULL;
12418 rc = E_FAIL;
12419 }
12420 }
12421 catch (std::bad_alloc &)
12422 {
12423 rc = E_OUTOFMEMORY;
12424 }
12425 if (FAILED(rc))
12426 return rc;
12427
12428 /* memorize the peer Machine */
12429 unconst(mPeer) = aMachine;
12430 /* share the parent pointer */
12431 unconst(mParent) = aMachine->mParent;
12432
12433 /* take the pointers to data to share */
12434 mData.share(aMachine->mData);
12435 mSSData.share(aMachine->mSSData);
12436
12437 mUserData.share(aMachine->mUserData);
12438 mHWData.share(aMachine->mHWData);
12439 mMediumAttachments.share(aMachine->mMediumAttachments);
12440
12441 mStorageControllers.allocate();
12442 for (StorageControllerList::const_iterator
12443 it = aMachine->mStorageControllers->begin();
12444 it != aMachine->mStorageControllers->end();
12445 ++it)
12446 {
12447 ComObjPtr<StorageController> ctl;
12448 ctl.createObject();
12449 ctl->init(this, *it);
12450 mStorageControllers->push_back(ctl);
12451 }
12452
12453 mUSBControllers.allocate();
12454 for (USBControllerList::const_iterator
12455 it = aMachine->mUSBControllers->begin();
12456 it != aMachine->mUSBControllers->end();
12457 ++it)
12458 {
12459 ComObjPtr<USBController> ctl;
12460 ctl.createObject();
12461 ctl->init(this, *it);
12462 mUSBControllers->push_back(ctl);
12463 }
12464
12465 unconst(mBIOSSettings).createObject();
12466 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12467
12468 unconst(mTrustedPlatformModule).createObject();
12469 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12470
12471 unconst(mRecordingSettings).createObject();
12472 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12473 /* create another GraphicsAdapter object that will be mutable */
12474 unconst(mGraphicsAdapter).createObject();
12475 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12476 /* create another VRDEServer object that will be mutable */
12477 unconst(mVRDEServer).createObject();
12478 mVRDEServer->init(this, aMachine->mVRDEServer);
12479 /* create another audio adapter object that will be mutable */
12480 unconst(mAudioAdapter).createObject();
12481 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12482 /* create a list of serial ports that will be mutable */
12483 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12484 {
12485 unconst(mSerialPorts[slot]).createObject();
12486 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12487 }
12488 /* create a list of parallel ports that will be mutable */
12489 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12490 {
12491 unconst(mParallelPorts[slot]).createObject();
12492 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12493 }
12494
12495 /* create another USB device filters object that will be mutable */
12496 unconst(mUSBDeviceFilters).createObject();
12497 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12498
12499 /* create a list of network adapters that will be mutable */
12500 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12501 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12502 {
12503 unconst(mNetworkAdapters[slot]).createObject();
12504 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12505 }
12506
12507 /* create another bandwidth control object that will be mutable */
12508 unconst(mBandwidthControl).createObject();
12509 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12510
12511 /* default is to delete saved state on Saved -> PoweredOff transition */
12512 mRemoveSavedState = true;
12513
12514 /* Confirm a successful initialization when it's the case */
12515 autoInitSpan.setSucceeded();
12516
12517 miNATNetworksStarted = 0;
12518
12519 LogFlowThisFuncLeave();
12520 return rc;
12521}
12522
12523/**
12524 * Uninitializes this session object. If the reason is other than
12525 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12526 * or the client watcher code.
12527 *
12528 * @param aReason uninitialization reason
12529 *
12530 * @note Locks mParent + this object for writing.
12531 */
12532void SessionMachine::uninit(Uninit::Reason aReason)
12533{
12534 LogFlowThisFuncEnter();
12535 LogFlowThisFunc(("reason=%d\n", aReason));
12536
12537 /*
12538 * Strongly reference ourselves to prevent this object deletion after
12539 * mData->mSession.mMachine.setNull() below (which can release the last
12540 * reference and call the destructor). Important: this must be done before
12541 * accessing any members (and before AutoUninitSpan that does it as well).
12542 * This self reference will be released as the very last step on return.
12543 */
12544 ComObjPtr<SessionMachine> selfRef;
12545 if (aReason != Uninit::Unexpected)
12546 selfRef = this;
12547
12548 /* Enclose the state transition Ready->InUninit->NotReady */
12549 AutoUninitSpan autoUninitSpan(this);
12550 if (autoUninitSpan.uninitDone())
12551 {
12552 LogFlowThisFunc(("Already uninitialized\n"));
12553 LogFlowThisFuncLeave();
12554 return;
12555 }
12556
12557 if (autoUninitSpan.initFailed())
12558 {
12559 /* We've been called by init() because it's failed. It's not really
12560 * necessary (nor it's safe) to perform the regular uninit sequence
12561 * below, the following is enough.
12562 */
12563 LogFlowThisFunc(("Initialization failed.\n"));
12564 /* destroy the machine client token */
12565 if (mClientToken)
12566 {
12567 delete mClientToken;
12568 mClientToken = NULL;
12569 }
12570 uninitDataAndChildObjects();
12571 mData.free();
12572 unconst(mParent) = NULL;
12573 unconst(mPeer) = NULL;
12574 LogFlowThisFuncLeave();
12575 return;
12576 }
12577
12578 MachineState_T lastState;
12579 {
12580 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12581 lastState = mData->mMachineState;
12582 }
12583 NOREF(lastState);
12584
12585#ifdef VBOX_WITH_USB
12586 // release all captured USB devices, but do this before requesting the locks below
12587 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12588 {
12589 /* Console::captureUSBDevices() is called in the VM process only after
12590 * setting the machine state to Starting or Restoring.
12591 * Console::detachAllUSBDevices() will be called upon successful
12592 * termination. So, we need to release USB devices only if there was
12593 * an abnormal termination of a running VM.
12594 *
12595 * This is identical to SessionMachine::DetachAllUSBDevices except
12596 * for the aAbnormal argument. */
12597 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12598 AssertComRC(rc);
12599 NOREF(rc);
12600
12601 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12602 if (service)
12603 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12604 }
12605#endif /* VBOX_WITH_USB */
12606
12607 // we need to lock this object in uninit() because the lock is shared
12608 // with mPeer (as well as data we modify below). mParent lock is needed
12609 // by several calls to it.
12610 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12611
12612#ifdef VBOX_WITH_RESOURCE_USAGE_API
12613 /*
12614 * It is safe to call Machine::i_unregisterMetrics() here because
12615 * PerformanceCollector::samplerCallback no longer accesses guest methods
12616 * holding the lock.
12617 */
12618 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12619 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12620 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12621 if (mCollectorGuest)
12622 {
12623 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12624 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12625 mCollectorGuest = NULL;
12626 }
12627#endif
12628
12629 if (aReason == Uninit::Abnormal)
12630 {
12631 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12632
12633 /* reset the state to Aborted */
12634 if (mData->mMachineState != MachineState_Aborted)
12635 i_setMachineState(MachineState_Aborted);
12636 }
12637
12638 // any machine settings modified?
12639 if (mData->flModifications)
12640 {
12641 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12642 i_rollback(false /* aNotify */);
12643 }
12644
12645 mData->mSession.mPID = NIL_RTPROCESS;
12646
12647 if (aReason == Uninit::Unexpected)
12648 {
12649 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12650 * client watcher thread to update the set of machines that have open
12651 * sessions. */
12652 mParent->i_updateClientWatcher();
12653 }
12654
12655 /* uninitialize all remote controls */
12656 if (mData->mSession.mRemoteControls.size())
12657 {
12658 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12659 mData->mSession.mRemoteControls.size()));
12660
12661 /* Always restart a the beginning, since the iterator is invalidated
12662 * by using erase(). */
12663 for (Data::Session::RemoteControlList::iterator
12664 it = mData->mSession.mRemoteControls.begin();
12665 it != mData->mSession.mRemoteControls.end();
12666 it = mData->mSession.mRemoteControls.begin())
12667 {
12668 ComPtr<IInternalSessionControl> pControl = *it;
12669 mData->mSession.mRemoteControls.erase(it);
12670 multilock.release();
12671 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12672 HRESULT rc = pControl->Uninitialize();
12673 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12674 if (FAILED(rc))
12675 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12676 multilock.acquire();
12677 }
12678 mData->mSession.mRemoteControls.clear();
12679 }
12680
12681 /* Remove all references to the NAT network service. The service will stop
12682 * if all references (also from other VMs) are removed. */
12683 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12684 {
12685 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12686 {
12687 BOOL enabled;
12688 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12689 if ( FAILED(hrc)
12690 || !enabled)
12691 continue;
12692
12693 NetworkAttachmentType_T type;
12694 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12695 if ( SUCCEEDED(hrc)
12696 && type == NetworkAttachmentType_NATNetwork)
12697 {
12698 Bstr name;
12699 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12700 if (SUCCEEDED(hrc))
12701 {
12702 multilock.release();
12703 Utf8Str strName(name);
12704 LogRel(("VM '%s' stops using NAT network '%s'\n",
12705 mUserData->s.strName.c_str(), strName.c_str()));
12706 mParent->i_natNetworkRefDec(strName);
12707 multilock.acquire();
12708 }
12709 }
12710 }
12711 }
12712
12713 /*
12714 * An expected uninitialization can come only from #i_checkForDeath().
12715 * Otherwise it means that something's gone really wrong (for example,
12716 * the Session implementation has released the VirtualBox reference
12717 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12718 * etc). However, it's also possible, that the client releases the IPC
12719 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12720 * but the VirtualBox release event comes first to the server process.
12721 * This case is practically possible, so we should not assert on an
12722 * unexpected uninit, just log a warning.
12723 */
12724
12725 if (aReason == Uninit::Unexpected)
12726 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12727
12728 if (aReason != Uninit::Normal)
12729 {
12730 mData->mSession.mDirectControl.setNull();
12731 }
12732 else
12733 {
12734 /* this must be null here (see #OnSessionEnd()) */
12735 Assert(mData->mSession.mDirectControl.isNull());
12736 Assert(mData->mSession.mState == SessionState_Unlocking);
12737 Assert(!mData->mSession.mProgress.isNull());
12738 }
12739 if (mData->mSession.mProgress)
12740 {
12741 if (aReason == Uninit::Normal)
12742 mData->mSession.mProgress->i_notifyComplete(S_OK);
12743 else
12744 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12745 COM_IIDOF(ISession),
12746 getComponentName(),
12747 tr("The VM session was aborted"));
12748 mData->mSession.mProgress.setNull();
12749 }
12750
12751 if (mConsoleTaskData.mProgress)
12752 {
12753 Assert(aReason == Uninit::Abnormal);
12754 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12755 COM_IIDOF(ISession),
12756 getComponentName(),
12757 tr("The VM session was aborted"));
12758 mConsoleTaskData.mProgress.setNull();
12759 }
12760
12761 /* remove the association between the peer machine and this session machine */
12762 Assert( (SessionMachine*)mData->mSession.mMachine == this
12763 || aReason == Uninit::Unexpected);
12764
12765 /* reset the rest of session data */
12766 mData->mSession.mLockType = LockType_Null;
12767 mData->mSession.mMachine.setNull();
12768 mData->mSession.mState = SessionState_Unlocked;
12769 mData->mSession.mName.setNull();
12770
12771 /* destroy the machine client token before leaving the exclusive lock */
12772 if (mClientToken)
12773 {
12774 delete mClientToken;
12775 mClientToken = NULL;
12776 }
12777
12778 /* fire an event */
12779 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12780
12781 uninitDataAndChildObjects();
12782
12783 /* free the essential data structure last */
12784 mData.free();
12785
12786 /* release the exclusive lock before setting the below two to NULL */
12787 multilock.release();
12788
12789 unconst(mParent) = NULL;
12790 unconst(mPeer) = NULL;
12791
12792 AuthLibUnload(&mAuthLibCtx);
12793
12794 LogFlowThisFuncLeave();
12795}
12796
12797// util::Lockable interface
12798////////////////////////////////////////////////////////////////////////////////
12799
12800/**
12801 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12802 * with the primary Machine instance (mPeer).
12803 */
12804RWLockHandle *SessionMachine::lockHandle() const
12805{
12806 AssertReturn(mPeer != NULL, NULL);
12807 return mPeer->lockHandle();
12808}
12809
12810// IInternalMachineControl methods
12811////////////////////////////////////////////////////////////////////////////////
12812
12813/**
12814 * Passes collected guest statistics to performance collector object
12815 */
12816HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12817 ULONG aCpuKernel, ULONG aCpuIdle,
12818 ULONG aMemTotal, ULONG aMemFree,
12819 ULONG aMemBalloon, ULONG aMemShared,
12820 ULONG aMemCache, ULONG aPageTotal,
12821 ULONG aAllocVMM, ULONG aFreeVMM,
12822 ULONG aBalloonedVMM, ULONG aSharedVMM,
12823 ULONG aVmNetRx, ULONG aVmNetTx)
12824{
12825#ifdef VBOX_WITH_RESOURCE_USAGE_API
12826 if (mCollectorGuest)
12827 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12828 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12829 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12830 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12831
12832 return S_OK;
12833#else
12834 NOREF(aValidStats);
12835 NOREF(aCpuUser);
12836 NOREF(aCpuKernel);
12837 NOREF(aCpuIdle);
12838 NOREF(aMemTotal);
12839 NOREF(aMemFree);
12840 NOREF(aMemBalloon);
12841 NOREF(aMemShared);
12842 NOREF(aMemCache);
12843 NOREF(aPageTotal);
12844 NOREF(aAllocVMM);
12845 NOREF(aFreeVMM);
12846 NOREF(aBalloonedVMM);
12847 NOREF(aSharedVMM);
12848 NOREF(aVmNetRx);
12849 NOREF(aVmNetTx);
12850 return E_NOTIMPL;
12851#endif
12852}
12853
12854////////////////////////////////////////////////////////////////////////////////
12855//
12856// SessionMachine task records
12857//
12858////////////////////////////////////////////////////////////////////////////////
12859
12860/**
12861 * Task record for saving the machine state.
12862 */
12863class SessionMachine::SaveStateTask
12864 : public Machine::Task
12865{
12866public:
12867 SaveStateTask(SessionMachine *m,
12868 Progress *p,
12869 const Utf8Str &t,
12870 Reason_T enmReason,
12871 const Utf8Str &strStateFilePath)
12872 : Task(m, p, t),
12873 m_enmReason(enmReason),
12874 m_strStateFilePath(strStateFilePath)
12875 {}
12876
12877private:
12878 void handler()
12879 {
12880 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12881 }
12882
12883 Reason_T m_enmReason;
12884 Utf8Str m_strStateFilePath;
12885
12886 friend class SessionMachine;
12887};
12888
12889/**
12890 * Task thread implementation for SessionMachine::SaveState(), called from
12891 * SessionMachine::taskHandler().
12892 *
12893 * @note Locks this object for writing.
12894 *
12895 * @param task
12896 * @return
12897 */
12898void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12899{
12900 LogFlowThisFuncEnter();
12901
12902 AutoCaller autoCaller(this);
12903 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12904 if (FAILED(autoCaller.rc()))
12905 {
12906 /* we might have been uninitialized because the session was accidentally
12907 * closed by the client, so don't assert */
12908 HRESULT rc = setError(E_FAIL,
12909 tr("The session has been accidentally closed"));
12910 task.m_pProgress->i_notifyComplete(rc);
12911 LogFlowThisFuncLeave();
12912 return;
12913 }
12914
12915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12916
12917 HRESULT rc = S_OK;
12918
12919 try
12920 {
12921 ComPtr<IInternalSessionControl> directControl;
12922 if (mData->mSession.mLockType == LockType_VM)
12923 directControl = mData->mSession.mDirectControl;
12924 if (directControl.isNull())
12925 throw setError(VBOX_E_INVALID_VM_STATE,
12926 tr("Trying to save state without a running VM"));
12927 alock.release();
12928 BOOL fSuspendedBySave;
12929 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12930 Assert(!fSuspendedBySave);
12931 alock.acquire();
12932
12933 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12934 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12935 throw E_FAIL);
12936
12937 if (SUCCEEDED(rc))
12938 {
12939 mSSData->strStateFilePath = task.m_strStateFilePath;
12940
12941 /* save all VM settings */
12942 rc = i_saveSettings(NULL);
12943 // no need to check whether VirtualBox.xml needs saving also since
12944 // we can't have a name change pending at this point
12945 }
12946 else
12947 {
12948 // On failure, set the state to the state we had at the beginning.
12949 i_setMachineState(task.m_machineStateBackup);
12950 i_updateMachineStateOnClient();
12951
12952 // Delete the saved state file (might have been already created).
12953 // No need to check whether this is shared with a snapshot here
12954 // because we certainly created a fresh saved state file here.
12955 RTFileDelete(task.m_strStateFilePath.c_str());
12956 }
12957 }
12958 catch (HRESULT aRC) { rc = aRC; }
12959
12960 task.m_pProgress->i_notifyComplete(rc);
12961
12962 LogFlowThisFuncLeave();
12963}
12964
12965/**
12966 * @note Locks this object for writing.
12967 */
12968HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12969{
12970 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12971}
12972
12973HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12974{
12975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12976
12977 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12978 if (FAILED(rc)) return rc;
12979
12980 if ( mData->mMachineState != MachineState_Running
12981 && mData->mMachineState != MachineState_Paused
12982 )
12983 return setError(VBOX_E_INVALID_VM_STATE,
12984 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12985 Global::stringifyMachineState(mData->mMachineState));
12986
12987 ComObjPtr<Progress> pProgress;
12988 pProgress.createObject();
12989 rc = pProgress->init(i_getVirtualBox(),
12990 static_cast<IMachine *>(this) /* aInitiator */,
12991 tr("Saving the execution state of the virtual machine"),
12992 FALSE /* aCancelable */);
12993 if (FAILED(rc))
12994 return rc;
12995
12996 Utf8Str strStateFilePath;
12997 i_composeSavedStateFilename(strStateFilePath);
12998
12999 /* create and start the task on a separate thread (note that it will not
13000 * start working until we release alock) */
13001 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13002 rc = pTask->createThread();
13003 if (FAILED(rc))
13004 return rc;
13005
13006 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13007 i_setMachineState(MachineState_Saving);
13008 i_updateMachineStateOnClient();
13009
13010 pProgress.queryInterfaceTo(aProgress.asOutParam());
13011
13012 return S_OK;
13013}
13014
13015/**
13016 * @note Locks this object for writing.
13017 */
13018HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13019{
13020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13021
13022 HRESULT rc = i_checkStateDependency(MutableStateDep);
13023 if (FAILED(rc)) return rc;
13024
13025 if ( mData->mMachineState != MachineState_PoweredOff
13026 && mData->mMachineState != MachineState_Teleported
13027 && mData->mMachineState != MachineState_Aborted
13028 )
13029 return setError(VBOX_E_INVALID_VM_STATE,
13030 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13031 Global::stringifyMachineState(mData->mMachineState));
13032
13033 com::Utf8Str stateFilePathFull;
13034 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13035 if (RT_FAILURE(vrc))
13036 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13037 tr("Invalid saved state file path '%s' (%Rrc)"),
13038 aSavedStateFile.c_str(),
13039 vrc);
13040
13041 mSSData->strStateFilePath = stateFilePathFull;
13042
13043 /* The below i_setMachineState() will detect the state transition and will
13044 * update the settings file */
13045
13046 return i_setMachineState(MachineState_Saved);
13047}
13048
13049/**
13050 * @note Locks this object for writing.
13051 */
13052HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13053{
13054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13055
13056 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13057 if (FAILED(rc)) return rc;
13058
13059 if (mData->mMachineState != MachineState_Saved)
13060 return setError(VBOX_E_INVALID_VM_STATE,
13061 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13062 Global::stringifyMachineState(mData->mMachineState));
13063
13064 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13065
13066 /*
13067 * Saved -> PoweredOff transition will be detected in the SessionMachine
13068 * and properly handled.
13069 */
13070 rc = i_setMachineState(MachineState_PoweredOff);
13071 return rc;
13072}
13073
13074
13075/**
13076 * @note Locks the same as #i_setMachineState() does.
13077 */
13078HRESULT SessionMachine::updateState(MachineState_T aState)
13079{
13080 return i_setMachineState(aState);
13081}
13082
13083/**
13084 * @note Locks this object for writing.
13085 */
13086HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13087{
13088 IProgress *pProgress(aProgress);
13089
13090 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13091
13092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13093
13094 if (mData->mSession.mState != SessionState_Locked)
13095 return VBOX_E_INVALID_OBJECT_STATE;
13096
13097 if (!mData->mSession.mProgress.isNull())
13098 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13099
13100 /* If we didn't reference the NAT network service yet, add a reference to
13101 * force a start */
13102 if (miNATNetworksStarted < 1)
13103 {
13104 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13105 {
13106 BOOL enabled;
13107 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13108 if ( FAILED(hrc)
13109 || !enabled)
13110 continue;
13111
13112 NetworkAttachmentType_T type;
13113 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13114 if ( SUCCEEDED(hrc)
13115 && type == NetworkAttachmentType_NATNetwork)
13116 {
13117 Bstr name;
13118 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13119 if (SUCCEEDED(hrc))
13120 {
13121 Utf8Str strName(name);
13122 LogRel(("VM '%s' starts using NAT network '%s'\n",
13123 mUserData->s.strName.c_str(), strName.c_str()));
13124 mPeer->lockHandle()->unlockWrite();
13125 mParent->i_natNetworkRefInc(strName);
13126#ifdef RT_LOCK_STRICT
13127 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13128#else
13129 mPeer->lockHandle()->lockWrite();
13130#endif
13131 }
13132 }
13133 }
13134 miNATNetworksStarted++;
13135 }
13136
13137 LogFlowThisFunc(("returns S_OK.\n"));
13138 return S_OK;
13139}
13140
13141/**
13142 * @note Locks this object for writing.
13143 */
13144HRESULT SessionMachine::endPowerUp(LONG aResult)
13145{
13146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13147
13148 if (mData->mSession.mState != SessionState_Locked)
13149 return VBOX_E_INVALID_OBJECT_STATE;
13150
13151 /* Finalize the LaunchVMProcess progress object. */
13152 if (mData->mSession.mProgress)
13153 {
13154 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13155 mData->mSession.mProgress.setNull();
13156 }
13157
13158 if (SUCCEEDED((HRESULT)aResult))
13159 {
13160#ifdef VBOX_WITH_RESOURCE_USAGE_API
13161 /* The VM has been powered up successfully, so it makes sense
13162 * now to offer the performance metrics for a running machine
13163 * object. Doing it earlier wouldn't be safe. */
13164 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13165 mData->mSession.mPID);
13166#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13167 }
13168
13169 return S_OK;
13170}
13171
13172/**
13173 * @note Locks this object for writing.
13174 */
13175HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13176{
13177 LogFlowThisFuncEnter();
13178
13179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13180
13181 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13182 E_FAIL);
13183
13184 /* create a progress object to track operation completion */
13185 ComObjPtr<Progress> pProgress;
13186 pProgress.createObject();
13187 pProgress->init(i_getVirtualBox(),
13188 static_cast<IMachine *>(this) /* aInitiator */,
13189 tr("Stopping the virtual machine"),
13190 FALSE /* aCancelable */);
13191
13192 /* fill in the console task data */
13193 mConsoleTaskData.mLastState = mData->mMachineState;
13194 mConsoleTaskData.mProgress = pProgress;
13195
13196 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13197 i_setMachineState(MachineState_Stopping);
13198
13199 pProgress.queryInterfaceTo(aProgress.asOutParam());
13200
13201 return S_OK;
13202}
13203
13204/**
13205 * @note Locks this object for writing.
13206 */
13207HRESULT SessionMachine::endPoweringDown(LONG aResult,
13208 const com::Utf8Str &aErrMsg)
13209{
13210 HRESULT const hrcResult = (HRESULT)aResult;
13211 LogFlowThisFuncEnter();
13212
13213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13214
13215 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13216 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13217 && mConsoleTaskData.mLastState != MachineState_Null,
13218 E_FAIL);
13219
13220 /*
13221 * On failure, set the state to the state we had when BeginPoweringDown()
13222 * was called (this is expected by Console::PowerDown() and the associated
13223 * task). On success the VM process already changed the state to
13224 * MachineState_PoweredOff, so no need to do anything.
13225 */
13226 if (FAILED(hrcResult))
13227 i_setMachineState(mConsoleTaskData.mLastState);
13228
13229 /* notify the progress object about operation completion */
13230 Assert(mConsoleTaskData.mProgress);
13231 if (SUCCEEDED(hrcResult))
13232 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13233 else
13234 {
13235 if (aErrMsg.length())
13236 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13237 COM_IIDOF(ISession),
13238 getComponentName(),
13239 aErrMsg.c_str());
13240 else
13241 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13242 }
13243
13244 /* clear out the temporary saved state data */
13245 mConsoleTaskData.mLastState = MachineState_Null;
13246 mConsoleTaskData.mProgress.setNull();
13247
13248 LogFlowThisFuncLeave();
13249 return S_OK;
13250}
13251
13252
13253/**
13254 * Goes through the USB filters of the given machine to see if the given
13255 * device matches any filter or not.
13256 *
13257 * @note Locks the same as USBController::hasMatchingFilter() does.
13258 */
13259HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13260 BOOL *aMatched,
13261 ULONG *aMaskedInterfaces)
13262{
13263 LogFlowThisFunc(("\n"));
13264
13265#ifdef VBOX_WITH_USB
13266 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13267#else
13268 NOREF(aDevice);
13269 NOREF(aMaskedInterfaces);
13270 *aMatched = FALSE;
13271#endif
13272
13273 return S_OK;
13274}
13275
13276/**
13277 * @note Locks the same as Host::captureUSBDevice() does.
13278 */
13279HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13280{
13281 LogFlowThisFunc(("\n"));
13282
13283#ifdef VBOX_WITH_USB
13284 /* if captureDeviceForVM() fails, it must have set extended error info */
13285 clearError();
13286 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13287 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13288 return rc;
13289
13290 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13291 AssertReturn(service, E_FAIL);
13292 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13293#else
13294 RT_NOREF(aId, aCaptureFilename);
13295 return E_NOTIMPL;
13296#endif
13297}
13298
13299/**
13300 * @note Locks the same as Host::detachUSBDevice() does.
13301 */
13302HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13303 BOOL aDone)
13304{
13305 LogFlowThisFunc(("\n"));
13306
13307#ifdef VBOX_WITH_USB
13308 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13309 AssertReturn(service, E_FAIL);
13310 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13311#else
13312 NOREF(aId);
13313 NOREF(aDone);
13314 return E_NOTIMPL;
13315#endif
13316}
13317
13318/**
13319 * Inserts all machine filters to the USB proxy service and then calls
13320 * Host::autoCaptureUSBDevices().
13321 *
13322 * Called by Console from the VM process upon VM startup.
13323 *
13324 * @note Locks what called methods lock.
13325 */
13326HRESULT SessionMachine::autoCaptureUSBDevices()
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330#ifdef VBOX_WITH_USB
13331 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13332 AssertComRC(rc);
13333 NOREF(rc);
13334
13335 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13336 AssertReturn(service, E_FAIL);
13337 return service->autoCaptureDevicesForVM(this);
13338#else
13339 return S_OK;
13340#endif
13341}
13342
13343/**
13344 * Removes all machine filters from the USB proxy service and then calls
13345 * Host::detachAllUSBDevices().
13346 *
13347 * Called by Console from the VM process upon normal VM termination or by
13348 * SessionMachine::uninit() upon abnormal VM termination (from under the
13349 * Machine/SessionMachine lock).
13350 *
13351 * @note Locks what called methods lock.
13352 */
13353HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13354{
13355 LogFlowThisFunc(("\n"));
13356
13357#ifdef VBOX_WITH_USB
13358 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13359 AssertComRC(rc);
13360 NOREF(rc);
13361
13362 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13363 AssertReturn(service, E_FAIL);
13364 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13365#else
13366 NOREF(aDone);
13367 return S_OK;
13368#endif
13369}
13370
13371/**
13372 * @note Locks this object for writing.
13373 */
13374HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13375 ComPtr<IProgress> &aProgress)
13376{
13377 LogFlowThisFuncEnter();
13378
13379 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13380 /*
13381 * We don't assert below because it might happen that a non-direct session
13382 * informs us it is closed right after we've been uninitialized -- it's ok.
13383 */
13384
13385 /* get IInternalSessionControl interface */
13386 ComPtr<IInternalSessionControl> control(aSession);
13387
13388 ComAssertRet(!control.isNull(), E_INVALIDARG);
13389
13390 /* Creating a Progress object requires the VirtualBox lock, and
13391 * thus locking it here is required by the lock order rules. */
13392 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13393
13394 if (control == mData->mSession.mDirectControl)
13395 {
13396 /* The direct session is being normally closed by the client process
13397 * ----------------------------------------------------------------- */
13398
13399 /* go to the closing state (essential for all open*Session() calls and
13400 * for #i_checkForDeath()) */
13401 Assert(mData->mSession.mState == SessionState_Locked);
13402 mData->mSession.mState = SessionState_Unlocking;
13403
13404 /* set direct control to NULL to release the remote instance */
13405 mData->mSession.mDirectControl.setNull();
13406 LogFlowThisFunc(("Direct control is set to NULL\n"));
13407
13408 if (mData->mSession.mProgress)
13409 {
13410 /* finalize the progress, someone might wait if a frontend
13411 * closes the session before powering on the VM. */
13412 mData->mSession.mProgress->notifyComplete(E_FAIL,
13413 COM_IIDOF(ISession),
13414 getComponentName(),
13415 tr("The VM session was closed before any attempt to power it on"));
13416 mData->mSession.mProgress.setNull();
13417 }
13418
13419 /* Create the progress object the client will use to wait until
13420 * #i_checkForDeath() is called to uninitialize this session object after
13421 * it releases the IPC semaphore.
13422 * Note! Because we're "reusing" mProgress here, this must be a proxy
13423 * object just like for LaunchVMProcess. */
13424 Assert(mData->mSession.mProgress.isNull());
13425 ComObjPtr<ProgressProxy> progress;
13426 progress.createObject();
13427 ComPtr<IUnknown> pPeer(mPeer);
13428 progress->init(mParent, pPeer,
13429 Bstr(tr("Closing session")).raw(),
13430 FALSE /* aCancelable */);
13431 progress.queryInterfaceTo(aProgress.asOutParam());
13432 mData->mSession.mProgress = progress;
13433 }
13434 else
13435 {
13436 /* the remote session is being normally closed */
13437 bool found = false;
13438 for (Data::Session::RemoteControlList::iterator
13439 it = mData->mSession.mRemoteControls.begin();
13440 it != mData->mSession.mRemoteControls.end();
13441 ++it)
13442 {
13443 if (control == *it)
13444 {
13445 found = true;
13446 // This MUST be erase(it), not remove(*it) as the latter
13447 // triggers a very nasty use after free due to the place where
13448 // the value "lives".
13449 mData->mSession.mRemoteControls.erase(it);
13450 break;
13451 }
13452 }
13453 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13454 E_INVALIDARG);
13455 }
13456
13457 /* signal the client watcher thread, because the client is going away */
13458 mParent->i_updateClientWatcher();
13459
13460 LogFlowThisFuncLeave();
13461 return S_OK;
13462}
13463
13464HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13465 std::vector<com::Utf8Str> &aValues,
13466 std::vector<LONG64> &aTimestamps,
13467 std::vector<com::Utf8Str> &aFlags)
13468{
13469 LogFlowThisFunc(("\n"));
13470
13471#ifdef VBOX_WITH_GUEST_PROPS
13472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13473
13474 size_t cEntries = mHWData->mGuestProperties.size();
13475 aNames.resize(cEntries);
13476 aValues.resize(cEntries);
13477 aTimestamps.resize(cEntries);
13478 aFlags.resize(cEntries);
13479
13480 size_t i = 0;
13481 for (HWData::GuestPropertyMap::const_iterator
13482 it = mHWData->mGuestProperties.begin();
13483 it != mHWData->mGuestProperties.end();
13484 ++it, ++i)
13485 {
13486 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13487 aNames[i] = it->first;
13488 aValues[i] = it->second.strValue;
13489 aTimestamps[i] = it->second.mTimestamp;
13490
13491 /* If it is NULL, keep it NULL. */
13492 if (it->second.mFlags)
13493 {
13494 GuestPropWriteFlags(it->second.mFlags, szFlags);
13495 aFlags[i] = szFlags;
13496 }
13497 else
13498 aFlags[i] = "";
13499 }
13500 return S_OK;
13501#else
13502 ReturnComNotImplemented();
13503#endif
13504}
13505
13506HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13507 const com::Utf8Str &aValue,
13508 LONG64 aTimestamp,
13509 const com::Utf8Str &aFlags)
13510{
13511 LogFlowThisFunc(("\n"));
13512
13513#ifdef VBOX_WITH_GUEST_PROPS
13514 try
13515 {
13516 /*
13517 * Convert input up front.
13518 */
13519 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13520 if (aFlags.length())
13521 {
13522 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13523 AssertRCReturn(vrc, E_INVALIDARG);
13524 }
13525
13526 /*
13527 * Now grab the object lock, validate the state and do the update.
13528 */
13529
13530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13531
13532 if (!Global::IsOnline(mData->mMachineState))
13533 {
13534 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13535 VBOX_E_INVALID_VM_STATE);
13536 }
13537
13538 i_setModified(IsModified_MachineData);
13539 mHWData.backup();
13540
13541 bool fDelete = !aValue.length();
13542 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13543 if (it != mHWData->mGuestProperties.end())
13544 {
13545 if (!fDelete)
13546 {
13547 it->second.strValue = aValue;
13548 it->second.mTimestamp = aTimestamp;
13549 it->second.mFlags = fFlags;
13550 }
13551 else
13552 mHWData->mGuestProperties.erase(it);
13553
13554 mData->mGuestPropertiesModified = TRUE;
13555 }
13556 else if (!fDelete)
13557 {
13558 HWData::GuestProperty prop;
13559 prop.strValue = aValue;
13560 prop.mTimestamp = aTimestamp;
13561 prop.mFlags = fFlags;
13562
13563 mHWData->mGuestProperties[aName] = prop;
13564 mData->mGuestPropertiesModified = TRUE;
13565 }
13566
13567 alock.release();
13568
13569 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13570 }
13571 catch (...)
13572 {
13573 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13574 }
13575 return S_OK;
13576#else
13577 ReturnComNotImplemented();
13578#endif
13579}
13580
13581
13582HRESULT SessionMachine::lockMedia()
13583{
13584 AutoMultiWriteLock2 alock(this->lockHandle(),
13585 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13586
13587 AssertReturn( mData->mMachineState == MachineState_Starting
13588 || mData->mMachineState == MachineState_Restoring
13589 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13590
13591 clearError();
13592 alock.release();
13593 return i_lockMedia();
13594}
13595
13596HRESULT SessionMachine::unlockMedia()
13597{
13598 HRESULT hrc = i_unlockMedia();
13599 return hrc;
13600}
13601
13602HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13603 ComPtr<IMediumAttachment> &aNewAttachment)
13604{
13605 // request the host lock first, since might be calling Host methods for getting host drives;
13606 // next, protect the media tree all the while we're in here, as well as our member variables
13607 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13608 this->lockHandle(),
13609 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13610
13611 IMediumAttachment *iAttach = aAttachment;
13612 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13613
13614 Utf8Str ctrlName;
13615 LONG lPort;
13616 LONG lDevice;
13617 bool fTempEject;
13618 {
13619 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13620
13621 /* Need to query the details first, as the IMediumAttachment reference
13622 * might be to the original settings, which we are going to change. */
13623 ctrlName = pAttach->i_getControllerName();
13624 lPort = pAttach->i_getPort();
13625 lDevice = pAttach->i_getDevice();
13626 fTempEject = pAttach->i_getTempEject();
13627 }
13628
13629 if (!fTempEject)
13630 {
13631 /* Remember previously mounted medium. The medium before taking the
13632 * backup is not necessarily the same thing. */
13633 ComObjPtr<Medium> oldmedium;
13634 oldmedium = pAttach->i_getMedium();
13635
13636 i_setModified(IsModified_Storage);
13637 mMediumAttachments.backup();
13638
13639 // The backup operation makes the pAttach reference point to the
13640 // old settings. Re-get the correct reference.
13641 pAttach = i_findAttachment(*mMediumAttachments.data(),
13642 ctrlName,
13643 lPort,
13644 lDevice);
13645
13646 {
13647 AutoCaller autoAttachCaller(this);
13648 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13649
13650 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13651 if (!oldmedium.isNull())
13652 oldmedium->i_removeBackReference(mData->mUuid);
13653
13654 pAttach->i_updateMedium(NULL);
13655 pAttach->i_updateEjected();
13656 }
13657
13658 i_setModified(IsModified_Storage);
13659 }
13660 else
13661 {
13662 {
13663 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13664 pAttach->i_updateEjected();
13665 }
13666 }
13667
13668 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13669
13670 return S_OK;
13671}
13672
13673HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13674 com::Utf8Str &aResult)
13675{
13676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13677
13678 HRESULT hr = S_OK;
13679
13680 if (!mAuthLibCtx.hAuthLibrary)
13681 {
13682 /* Load the external authentication library. */
13683 Bstr authLibrary;
13684 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13685
13686 Utf8Str filename = authLibrary;
13687
13688 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13689 if (RT_FAILURE(vrc))
13690 hr = setErrorBoth(E_FAIL, vrc,
13691 tr("Could not load the external authentication library '%s' (%Rrc)"),
13692 filename.c_str(), vrc);
13693 }
13694
13695 /* The auth library might need the machine lock. */
13696 alock.release();
13697
13698 if (FAILED(hr))
13699 return hr;
13700
13701 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13702 {
13703 enum VRDEAuthParams
13704 {
13705 parmUuid = 1,
13706 parmGuestJudgement,
13707 parmUser,
13708 parmPassword,
13709 parmDomain,
13710 parmClientId
13711 };
13712
13713 AuthResult result = AuthResultAccessDenied;
13714
13715 Guid uuid(aAuthParams[parmUuid]);
13716 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13717 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13718
13719 result = AuthLibAuthenticate(&mAuthLibCtx,
13720 uuid.raw(), guestJudgement,
13721 aAuthParams[parmUser].c_str(),
13722 aAuthParams[parmPassword].c_str(),
13723 aAuthParams[parmDomain].c_str(),
13724 u32ClientId);
13725
13726 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13727 size_t cbPassword = aAuthParams[parmPassword].length();
13728 if (cbPassword)
13729 {
13730 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13731 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13732 }
13733
13734 if (result == AuthResultAccessGranted)
13735 aResult = "granted";
13736 else
13737 aResult = "denied";
13738
13739 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13740 aAuthParams[parmUser].c_str(), aResult.c_str()));
13741 }
13742 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13743 {
13744 enum VRDEAuthDisconnectParams
13745 {
13746 parmUuid = 1,
13747 parmClientId
13748 };
13749
13750 Guid uuid(aAuthParams[parmUuid]);
13751 uint32_t u32ClientId = 0;
13752 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13753 }
13754 else
13755 {
13756 hr = E_INVALIDARG;
13757 }
13758
13759 return hr;
13760}
13761
13762// public methods only for internal purposes
13763/////////////////////////////////////////////////////////////////////////////
13764
13765#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13766/**
13767 * Called from the client watcher thread to check for expected or unexpected
13768 * death of the client process that has a direct session to this machine.
13769 *
13770 * On Win32 and on OS/2, this method is called only when we've got the
13771 * mutex (i.e. the client has either died or terminated normally) so it always
13772 * returns @c true (the client is terminated, the session machine is
13773 * uninitialized).
13774 *
13775 * On other platforms, the method returns @c true if the client process has
13776 * terminated normally or abnormally and the session machine was uninitialized,
13777 * and @c false if the client process is still alive.
13778 *
13779 * @note Locks this object for writing.
13780 */
13781bool SessionMachine::i_checkForDeath()
13782{
13783 Uninit::Reason reason;
13784 bool terminated = false;
13785
13786 /* Enclose autoCaller with a block because calling uninit() from under it
13787 * will deadlock. */
13788 {
13789 AutoCaller autoCaller(this);
13790 if (!autoCaller.isOk())
13791 {
13792 /* return true if not ready, to cause the client watcher to exclude
13793 * the corresponding session from watching */
13794 LogFlowThisFunc(("Already uninitialized!\n"));
13795 return true;
13796 }
13797
13798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13799
13800 /* Determine the reason of death: if the session state is Closing here,
13801 * everything is fine. Otherwise it means that the client did not call
13802 * OnSessionEnd() before it released the IPC semaphore. This may happen
13803 * either because the client process has abnormally terminated, or
13804 * because it simply forgot to call ISession::Close() before exiting. We
13805 * threat the latter also as an abnormal termination (see
13806 * Session::uninit() for details). */
13807 reason = mData->mSession.mState == SessionState_Unlocking ?
13808 Uninit::Normal :
13809 Uninit::Abnormal;
13810
13811 if (mClientToken)
13812 terminated = mClientToken->release();
13813 } /* AutoCaller block */
13814
13815 if (terminated)
13816 uninit(reason);
13817
13818 return terminated;
13819}
13820
13821void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13822{
13823 LogFlowThisFunc(("\n"));
13824
13825 strTokenId.setNull();
13826
13827 AutoCaller autoCaller(this);
13828 AssertComRCReturnVoid(autoCaller.rc());
13829
13830 Assert(mClientToken);
13831 if (mClientToken)
13832 mClientToken->getId(strTokenId);
13833}
13834#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13835IToken *SessionMachine::i_getToken()
13836{
13837 LogFlowThisFunc(("\n"));
13838
13839 AutoCaller autoCaller(this);
13840 AssertComRCReturn(autoCaller.rc(), NULL);
13841
13842 Assert(mClientToken);
13843 if (mClientToken)
13844 return mClientToken->getToken();
13845 else
13846 return NULL;
13847}
13848#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13849
13850Machine::ClientToken *SessionMachine::i_getClientToken()
13851{
13852 LogFlowThisFunc(("\n"));
13853
13854 AutoCaller autoCaller(this);
13855 AssertComRCReturn(autoCaller.rc(), NULL);
13856
13857 return mClientToken;
13858}
13859
13860
13861/**
13862 * @note Locks this object for reading.
13863 */
13864HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13865{
13866 LogFlowThisFunc(("\n"));
13867
13868 AutoCaller autoCaller(this);
13869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13870
13871 ComPtr<IInternalSessionControl> directControl;
13872 {
13873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13874 if (mData->mSession.mLockType == LockType_VM)
13875 directControl = mData->mSession.mDirectControl;
13876 }
13877
13878 /* ignore notifications sent after #OnSessionEnd() is called */
13879 if (!directControl)
13880 return S_OK;
13881
13882 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13883}
13884
13885/**
13886 * @note Locks this object for reading.
13887 */
13888HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13889 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13890 const Utf8Str &aGuestIp, LONG aGuestPort)
13891{
13892 LogFlowThisFunc(("\n"));
13893
13894 AutoCaller autoCaller(this);
13895 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13896
13897 ComPtr<IInternalSessionControl> directControl;
13898 {
13899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13900 if (mData->mSession.mLockType == LockType_VM)
13901 directControl = mData->mSession.mDirectControl;
13902 }
13903
13904 /* ignore notifications sent after #OnSessionEnd() is called */
13905 if (!directControl)
13906 return S_OK;
13907 /*
13908 * instead acting like callback we ask IVirtualBox deliver corresponding event
13909 */
13910
13911 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13912 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13913 return S_OK;
13914}
13915
13916/**
13917 * @note Locks this object for reading.
13918 */
13919HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13920{
13921 LogFlowThisFunc(("\n"));
13922
13923 AutoCaller autoCaller(this);
13924 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13925
13926 ComPtr<IInternalSessionControl> directControl;
13927 {
13928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13929 if (mData->mSession.mLockType == LockType_VM)
13930 directControl = mData->mSession.mDirectControl;
13931 }
13932
13933 /* ignore notifications sent after #OnSessionEnd() is called */
13934 if (!directControl)
13935 return S_OK;
13936
13937 return directControl->OnAudioAdapterChange(audioAdapter);
13938}
13939
13940/**
13941 * @note Locks this object for reading.
13942 */
13943HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13944{
13945 LogFlowThisFunc(("\n"));
13946
13947 AutoCaller autoCaller(this);
13948 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13949
13950 ComPtr<IInternalSessionControl> directControl;
13951 {
13952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13953 if (mData->mSession.mLockType == LockType_VM)
13954 directControl = mData->mSession.mDirectControl;
13955 }
13956
13957 /* ignore notifications sent after #OnSessionEnd() is called */
13958 if (!directControl)
13959 return S_OK;
13960
13961 return directControl->OnSerialPortChange(serialPort);
13962}
13963
13964/**
13965 * @note Locks this object for reading.
13966 */
13967HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13968{
13969 LogFlowThisFunc(("\n"));
13970
13971 AutoCaller autoCaller(this);
13972 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13973
13974 ComPtr<IInternalSessionControl> directControl;
13975 {
13976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13977 if (mData->mSession.mLockType == LockType_VM)
13978 directControl = mData->mSession.mDirectControl;
13979 }
13980
13981 /* ignore notifications sent after #OnSessionEnd() is called */
13982 if (!directControl)
13983 return S_OK;
13984
13985 return directControl->OnParallelPortChange(parallelPort);
13986}
13987
13988/**
13989 * @note Locks this object for reading.
13990 */
13991HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13992{
13993 LogFlowThisFunc(("\n"));
13994
13995 AutoCaller autoCaller(this);
13996 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13997
13998 ComPtr<IInternalSessionControl> directControl;
13999 {
14000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14001 if (mData->mSession.mLockType == LockType_VM)
14002 directControl = mData->mSession.mDirectControl;
14003 }
14004
14005 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14006
14007 /* ignore notifications sent after #OnSessionEnd() is called */
14008 if (!directControl)
14009 return S_OK;
14010
14011 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14012}
14013
14014/**
14015 * @note Locks this object for reading.
14016 */
14017HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14018{
14019 LogFlowThisFunc(("\n"));
14020
14021 AutoCaller autoCaller(this);
14022 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14023
14024 ComPtr<IInternalSessionControl> directControl;
14025 {
14026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14027 if (mData->mSession.mLockType == LockType_VM)
14028 directControl = mData->mSession.mDirectControl;
14029 }
14030
14031 mParent->i_onMediumChanged(aAttachment);
14032
14033 /* ignore notifications sent after #OnSessionEnd() is called */
14034 if (!directControl)
14035 return S_OK;
14036
14037 return directControl->OnMediumChange(aAttachment, aForce);
14038}
14039
14040HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14041{
14042 LogFlowThisFunc(("\n"));
14043
14044 AutoCaller autoCaller(this);
14045 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14046
14047 ComPtr<IInternalSessionControl> directControl;
14048 {
14049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14050 if (mData->mSession.mLockType == LockType_VM)
14051 directControl = mData->mSession.mDirectControl;
14052 }
14053
14054 /* ignore notifications sent after #OnSessionEnd() is called */
14055 if (!directControl)
14056 return S_OK;
14057
14058 return directControl->OnVMProcessPriorityChange(aPriority);
14059}
14060
14061/**
14062 * @note Locks this object for reading.
14063 */
14064HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14065{
14066 LogFlowThisFunc(("\n"));
14067
14068 AutoCaller autoCaller(this);
14069 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14070
14071 ComPtr<IInternalSessionControl> directControl;
14072 {
14073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14074 if (mData->mSession.mLockType == LockType_VM)
14075 directControl = mData->mSession.mDirectControl;
14076 }
14077
14078 /* ignore notifications sent after #OnSessionEnd() is called */
14079 if (!directControl)
14080 return S_OK;
14081
14082 return directControl->OnCPUChange(aCPU, aRemove);
14083}
14084
14085HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14086{
14087 LogFlowThisFunc(("\n"));
14088
14089 AutoCaller autoCaller(this);
14090 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14091
14092 ComPtr<IInternalSessionControl> directControl;
14093 {
14094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14095 if (mData->mSession.mLockType == LockType_VM)
14096 directControl = mData->mSession.mDirectControl;
14097 }
14098
14099 /* ignore notifications sent after #OnSessionEnd() is called */
14100 if (!directControl)
14101 return S_OK;
14102
14103 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14104}
14105
14106/**
14107 * @note Locks this object for reading.
14108 */
14109HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14110{
14111 LogFlowThisFunc(("\n"));
14112
14113 AutoCaller autoCaller(this);
14114 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14115
14116 ComPtr<IInternalSessionControl> directControl;
14117 {
14118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14119 if (mData->mSession.mLockType == LockType_VM)
14120 directControl = mData->mSession.mDirectControl;
14121 }
14122
14123 /* ignore notifications sent after #OnSessionEnd() is called */
14124 if (!directControl)
14125 return S_OK;
14126
14127 return directControl->OnVRDEServerChange(aRestart);
14128}
14129
14130/**
14131 * @note Locks this object for reading.
14132 */
14133HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14134{
14135 LogFlowThisFunc(("\n"));
14136
14137 AutoCaller autoCaller(this);
14138 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14139
14140 ComPtr<IInternalSessionControl> directControl;
14141 {
14142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14143 if (mData->mSession.mLockType == LockType_VM)
14144 directControl = mData->mSession.mDirectControl;
14145 }
14146
14147 /* ignore notifications sent after #OnSessionEnd() is called */
14148 if (!directControl)
14149 return S_OK;
14150
14151 return directControl->OnRecordingChange(aEnable);
14152}
14153
14154/**
14155 * @note Locks this object for reading.
14156 */
14157HRESULT SessionMachine::i_onUSBControllerChange()
14158{
14159 LogFlowThisFunc(("\n"));
14160
14161 AutoCaller autoCaller(this);
14162 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14163
14164 ComPtr<IInternalSessionControl> directControl;
14165 {
14166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14167 if (mData->mSession.mLockType == LockType_VM)
14168 directControl = mData->mSession.mDirectControl;
14169 }
14170
14171 /* ignore notifications sent after #OnSessionEnd() is called */
14172 if (!directControl)
14173 return S_OK;
14174
14175 return directControl->OnUSBControllerChange();
14176}
14177
14178/**
14179 * @note Locks this object for reading.
14180 */
14181HRESULT SessionMachine::i_onSharedFolderChange()
14182{
14183 LogFlowThisFunc(("\n"));
14184
14185 AutoCaller autoCaller(this);
14186 AssertComRCReturnRC(autoCaller.rc());
14187
14188 ComPtr<IInternalSessionControl> directControl;
14189 {
14190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14191 if (mData->mSession.mLockType == LockType_VM)
14192 directControl = mData->mSession.mDirectControl;
14193 }
14194
14195 /* ignore notifications sent after #OnSessionEnd() is called */
14196 if (!directControl)
14197 return S_OK;
14198
14199 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14200}
14201
14202/**
14203 * @note Locks this object for reading.
14204 */
14205HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14206{
14207 LogFlowThisFunc(("\n"));
14208
14209 AutoCaller autoCaller(this);
14210 AssertComRCReturnRC(autoCaller.rc());
14211
14212 ComPtr<IInternalSessionControl> directControl;
14213 {
14214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14215 if (mData->mSession.mLockType == LockType_VM)
14216 directControl = mData->mSession.mDirectControl;
14217 }
14218
14219 /* ignore notifications sent after #OnSessionEnd() is called */
14220 if (!directControl)
14221 return S_OK;
14222
14223 return directControl->OnClipboardModeChange(aClipboardMode);
14224}
14225
14226/**
14227 * @note Locks this object for reading.
14228 */
14229HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14230{
14231 LogFlowThisFunc(("\n"));
14232
14233 AutoCaller autoCaller(this);
14234 AssertComRCReturnRC(autoCaller.rc());
14235
14236 ComPtr<IInternalSessionControl> directControl;
14237 {
14238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14239 if (mData->mSession.mLockType == LockType_VM)
14240 directControl = mData->mSession.mDirectControl;
14241 }
14242
14243 /* ignore notifications sent after #OnSessionEnd() is called */
14244 if (!directControl)
14245 return S_OK;
14246
14247 return directControl->OnClipboardFileTransferModeChange(aEnable);
14248}
14249
14250/**
14251 * @note Locks this object for reading.
14252 */
14253HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14254{
14255 LogFlowThisFunc(("\n"));
14256
14257 AutoCaller autoCaller(this);
14258 AssertComRCReturnRC(autoCaller.rc());
14259
14260 ComPtr<IInternalSessionControl> directControl;
14261 {
14262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14263 if (mData->mSession.mLockType == LockType_VM)
14264 directControl = mData->mSession.mDirectControl;
14265 }
14266
14267 /* ignore notifications sent after #OnSessionEnd() is called */
14268 if (!directControl)
14269 return S_OK;
14270
14271 return directControl->OnDnDModeChange(aDnDMode);
14272}
14273
14274/**
14275 * @note Locks this object for reading.
14276 */
14277HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14278{
14279 LogFlowThisFunc(("\n"));
14280
14281 AutoCaller autoCaller(this);
14282 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14283
14284 ComPtr<IInternalSessionControl> directControl;
14285 {
14286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14287 if (mData->mSession.mLockType == LockType_VM)
14288 directControl = mData->mSession.mDirectControl;
14289 }
14290
14291 /* ignore notifications sent after #OnSessionEnd() is called */
14292 if (!directControl)
14293 return S_OK;
14294
14295 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14296}
14297
14298/**
14299 * @note Locks this object for reading.
14300 */
14301HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14302{
14303 LogFlowThisFunc(("\n"));
14304
14305 AutoCaller autoCaller(this);
14306 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14307
14308 ComPtr<IInternalSessionControl> directControl;
14309 {
14310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14311 if (mData->mSession.mLockType == LockType_VM)
14312 directControl = mData->mSession.mDirectControl;
14313 }
14314
14315 /* ignore notifications sent after #OnSessionEnd() is called */
14316 if (!directControl)
14317 return S_OK;
14318
14319 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14320}
14321
14322/**
14323 * Returns @c true if this machine's USB controller reports it has a matching
14324 * filter for the given USB device and @c false otherwise.
14325 *
14326 * @note locks this object for reading.
14327 */
14328bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14329{
14330 AutoCaller autoCaller(this);
14331 /* silently return if not ready -- this method may be called after the
14332 * direct machine session has been called */
14333 if (!autoCaller.isOk())
14334 return false;
14335
14336#ifdef VBOX_WITH_USB
14337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14338
14339 switch (mData->mMachineState)
14340 {
14341 case MachineState_Starting:
14342 case MachineState_Restoring:
14343 case MachineState_TeleportingIn:
14344 case MachineState_Paused:
14345 case MachineState_Running:
14346 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14347 * elsewhere... */
14348 alock.release();
14349 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14350 default: break;
14351 }
14352#else
14353 NOREF(aDevice);
14354 NOREF(aMaskedIfs);
14355#endif
14356 return false;
14357}
14358
14359/**
14360 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14361 */
14362HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14363 IVirtualBoxErrorInfo *aError,
14364 ULONG aMaskedIfs,
14365 const com::Utf8Str &aCaptureFilename)
14366{
14367 LogFlowThisFunc(("\n"));
14368
14369 AutoCaller autoCaller(this);
14370
14371 /* This notification may happen after the machine object has been
14372 * uninitialized (the session was closed), so don't assert. */
14373 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14374
14375 ComPtr<IInternalSessionControl> directControl;
14376 {
14377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14378 if (mData->mSession.mLockType == LockType_VM)
14379 directControl = mData->mSession.mDirectControl;
14380 }
14381
14382 /* fail on notifications sent after #OnSessionEnd() is called, it is
14383 * expected by the caller */
14384 if (!directControl)
14385 return E_FAIL;
14386
14387 /* No locks should be held at this point. */
14388 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14389 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14390
14391 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14392}
14393
14394/**
14395 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14396 */
14397HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14398 IVirtualBoxErrorInfo *aError)
14399{
14400 LogFlowThisFunc(("\n"));
14401
14402 AutoCaller autoCaller(this);
14403
14404 /* This notification may happen after the machine object has been
14405 * uninitialized (the session was closed), so don't assert. */
14406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14407
14408 ComPtr<IInternalSessionControl> directControl;
14409 {
14410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14411 if (mData->mSession.mLockType == LockType_VM)
14412 directControl = mData->mSession.mDirectControl;
14413 }
14414
14415 /* fail on notifications sent after #OnSessionEnd() is called, it is
14416 * expected by the caller */
14417 if (!directControl)
14418 return E_FAIL;
14419
14420 /* No locks should be held at this point. */
14421 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14422 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14423
14424 return directControl->OnUSBDeviceDetach(aId, aError);
14425}
14426
14427// protected methods
14428/////////////////////////////////////////////////////////////////////////////
14429
14430/**
14431 * Deletes the given file if it is no longer in use by either the current machine state
14432 * (if the machine is "saved") or any of the machine's snapshots.
14433 *
14434 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14435 * but is different for each SnapshotMachine. When calling this, the order of calling this
14436 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14437 * is therefore critical. I know, it's all rather messy.
14438 *
14439 * @param strStateFile
14440 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14441 * the test for whether the saved state file is in use.
14442 */
14443void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14444 Snapshot *pSnapshotToIgnore)
14445{
14446 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14447 if ( (strStateFile.isNotEmpty())
14448 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14449 )
14450 // ... and it must also not be shared with other snapshots
14451 if ( !mData->mFirstSnapshot
14452 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14453 // this checks the SnapshotMachine's state file paths
14454 )
14455 RTFileDelete(strStateFile.c_str());
14456}
14457
14458/**
14459 * Locks the attached media.
14460 *
14461 * All attached hard disks are locked for writing and DVD/floppy are locked for
14462 * reading. Parents of attached hard disks (if any) are locked for reading.
14463 *
14464 * This method also performs accessibility check of all media it locks: if some
14465 * media is inaccessible, the method will return a failure and a bunch of
14466 * extended error info objects per each inaccessible medium.
14467 *
14468 * Note that this method is atomic: if it returns a success, all media are
14469 * locked as described above; on failure no media is locked at all (all
14470 * succeeded individual locks will be undone).
14471 *
14472 * The caller is responsible for doing the necessary state sanity checks.
14473 *
14474 * The locks made by this method must be undone by calling #unlockMedia() when
14475 * no more needed.
14476 */
14477HRESULT SessionMachine::i_lockMedia()
14478{
14479 AutoCaller autoCaller(this);
14480 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14481
14482 AutoMultiWriteLock2 alock(this->lockHandle(),
14483 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14484
14485 /* bail out if trying to lock things with already set up locking */
14486 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14487
14488 MultiResult mrc(S_OK);
14489
14490 /* Collect locking information for all medium objects attached to the VM. */
14491 for (MediumAttachmentList::const_iterator
14492 it = mMediumAttachments->begin();
14493 it != mMediumAttachments->end();
14494 ++it)
14495 {
14496 MediumAttachment *pAtt = *it;
14497 DeviceType_T devType = pAtt->i_getType();
14498 Medium *pMedium = pAtt->i_getMedium();
14499
14500 MediumLockList *pMediumLockList(new MediumLockList());
14501 // There can be attachments without a medium (floppy/dvd), and thus
14502 // it's impossible to create a medium lock list. It still makes sense
14503 // to have the empty medium lock list in the map in case a medium is
14504 // attached later.
14505 if (pMedium != NULL)
14506 {
14507 MediumType_T mediumType = pMedium->i_getType();
14508 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14509 || mediumType == MediumType_Shareable;
14510 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14511
14512 alock.release();
14513 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14514 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14515 false /* fMediumLockWriteAll */,
14516 NULL,
14517 *pMediumLockList);
14518 alock.acquire();
14519 if (FAILED(mrc))
14520 {
14521 delete pMediumLockList;
14522 mData->mSession.mLockedMedia.Clear();
14523 break;
14524 }
14525 }
14526
14527 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14528 if (FAILED(rc))
14529 {
14530 mData->mSession.mLockedMedia.Clear();
14531 mrc = setError(rc,
14532 tr("Collecting locking information for all attached media failed"));
14533 break;
14534 }
14535 }
14536
14537 if (SUCCEEDED(mrc))
14538 {
14539 /* Now lock all media. If this fails, nothing is locked. */
14540 alock.release();
14541 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14542 alock.acquire();
14543 if (FAILED(rc))
14544 {
14545 mrc = setError(rc,
14546 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14547 }
14548 }
14549
14550 return mrc;
14551}
14552
14553/**
14554 * Undoes the locks made by by #lockMedia().
14555 */
14556HRESULT SessionMachine::i_unlockMedia()
14557{
14558 AutoCaller autoCaller(this);
14559 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14560
14561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14562
14563 /* we may be holding important error info on the current thread;
14564 * preserve it */
14565 ErrorInfoKeeper eik;
14566
14567 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14568 AssertComRC(rc);
14569 return rc;
14570}
14571
14572/**
14573 * Helper to change the machine state (reimplementation).
14574 *
14575 * @note Locks this object for writing.
14576 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14577 * it can cause crashes in random places due to unexpectedly committing
14578 * the current settings. The caller is responsible for that. The call
14579 * to saveStateSettings is fine, because this method does not commit.
14580 */
14581HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14582{
14583 LogFlowThisFuncEnter();
14584 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14585
14586 AutoCaller autoCaller(this);
14587 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14588
14589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14590
14591 MachineState_T oldMachineState = mData->mMachineState;
14592
14593 AssertMsgReturn(oldMachineState != aMachineState,
14594 ("oldMachineState=%s, aMachineState=%s\n",
14595 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14596 E_FAIL);
14597
14598 HRESULT rc = S_OK;
14599
14600 int stsFlags = 0;
14601 bool deleteSavedState = false;
14602
14603 /* detect some state transitions */
14604
14605 if ( ( oldMachineState == MachineState_Saved
14606 && aMachineState == MachineState_Restoring)
14607 || ( ( oldMachineState == MachineState_PoweredOff
14608 || oldMachineState == MachineState_Teleported
14609 || oldMachineState == MachineState_Aborted
14610 )
14611 && ( aMachineState == MachineState_TeleportingIn
14612 || aMachineState == MachineState_Starting
14613 )
14614 )
14615 )
14616 {
14617 /* The EMT thread is about to start */
14618
14619 /* Nothing to do here for now... */
14620
14621 /// @todo NEWMEDIA don't let mDVDDrive and other children
14622 /// change anything when in the Starting/Restoring state
14623 }
14624 else if ( ( oldMachineState == MachineState_Running
14625 || oldMachineState == MachineState_Paused
14626 || oldMachineState == MachineState_Teleporting
14627 || oldMachineState == MachineState_OnlineSnapshotting
14628 || oldMachineState == MachineState_LiveSnapshotting
14629 || oldMachineState == MachineState_Stuck
14630 || oldMachineState == MachineState_Starting
14631 || oldMachineState == MachineState_Stopping
14632 || oldMachineState == MachineState_Saving
14633 || oldMachineState == MachineState_Restoring
14634 || oldMachineState == MachineState_TeleportingPausedVM
14635 || oldMachineState == MachineState_TeleportingIn
14636 )
14637 && ( aMachineState == MachineState_PoweredOff
14638 || aMachineState == MachineState_Saved
14639 || aMachineState == MachineState_Teleported
14640 || aMachineState == MachineState_Aborted
14641 )
14642 )
14643 {
14644 /* The EMT thread has just stopped, unlock attached media. Note that as
14645 * opposed to locking that is done from Console, we do unlocking here
14646 * because the VM process may have aborted before having a chance to
14647 * properly unlock all media it locked. */
14648
14649 unlockMedia();
14650 }
14651
14652 if (oldMachineState == MachineState_Restoring)
14653 {
14654 if (aMachineState != MachineState_Saved)
14655 {
14656 /*
14657 * delete the saved state file once the machine has finished
14658 * restoring from it (note that Console sets the state from
14659 * Restoring to Saved if the VM couldn't restore successfully,
14660 * to give the user an ability to fix an error and retry --
14661 * we keep the saved state file in this case)
14662 */
14663 deleteSavedState = true;
14664 }
14665 }
14666 else if ( oldMachineState == MachineState_Saved
14667 && ( aMachineState == MachineState_PoweredOff
14668 || aMachineState == MachineState_Aborted
14669 || aMachineState == MachineState_Teleported
14670 )
14671 )
14672 {
14673 /*
14674 * delete the saved state after SessionMachine::ForgetSavedState() is called
14675 * or if the VM process (owning a direct VM session) crashed while the
14676 * VM was Saved
14677 */
14678
14679 /// @todo (dmik)
14680 // Not sure that deleting the saved state file just because of the
14681 // client death before it attempted to restore the VM is a good
14682 // thing. But when it crashes we need to go to the Aborted state
14683 // which cannot have the saved state file associated... The only
14684 // way to fix this is to make the Aborted condition not a VM state
14685 // but a bool flag: i.e., when a crash occurs, set it to true and
14686 // change the state to PoweredOff or Saved depending on the
14687 // saved state presence.
14688
14689 deleteSavedState = true;
14690 mData->mCurrentStateModified = TRUE;
14691 stsFlags |= SaveSTS_CurStateModified;
14692 }
14693
14694 if ( aMachineState == MachineState_Starting
14695 || aMachineState == MachineState_Restoring
14696 || aMachineState == MachineState_TeleportingIn
14697 )
14698 {
14699 /* set the current state modified flag to indicate that the current
14700 * state is no more identical to the state in the
14701 * current snapshot */
14702 if (!mData->mCurrentSnapshot.isNull())
14703 {
14704 mData->mCurrentStateModified = TRUE;
14705 stsFlags |= SaveSTS_CurStateModified;
14706 }
14707 }
14708
14709 if (deleteSavedState)
14710 {
14711 if (mRemoveSavedState)
14712 {
14713 Assert(!mSSData->strStateFilePath.isEmpty());
14714
14715 // it is safe to delete the saved state file if ...
14716 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14717 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14718 // ... none of the snapshots share the saved state file
14719 )
14720 RTFileDelete(mSSData->strStateFilePath.c_str());
14721 }
14722
14723 mSSData->strStateFilePath.setNull();
14724 stsFlags |= SaveSTS_StateFilePath;
14725 }
14726
14727 /* redirect to the underlying peer machine */
14728 mPeer->i_setMachineState(aMachineState);
14729
14730 if ( oldMachineState != MachineState_RestoringSnapshot
14731 && ( aMachineState == MachineState_PoweredOff
14732 || aMachineState == MachineState_Teleported
14733 || aMachineState == MachineState_Aborted
14734 || aMachineState == MachineState_Saved))
14735 {
14736 /* the machine has stopped execution
14737 * (or the saved state file was adopted) */
14738 stsFlags |= SaveSTS_StateTimeStamp;
14739 }
14740
14741 if ( ( oldMachineState == MachineState_PoweredOff
14742 || oldMachineState == MachineState_Aborted
14743 || oldMachineState == MachineState_Teleported
14744 )
14745 && aMachineState == MachineState_Saved)
14746 {
14747 /* the saved state file was adopted */
14748 Assert(!mSSData->strStateFilePath.isEmpty());
14749 stsFlags |= SaveSTS_StateFilePath;
14750 }
14751
14752#ifdef VBOX_WITH_GUEST_PROPS
14753 if ( aMachineState == MachineState_PoweredOff
14754 || aMachineState == MachineState_Aborted
14755 || aMachineState == MachineState_Teleported)
14756 {
14757 /* Make sure any transient guest properties get removed from the
14758 * property store on shutdown. */
14759 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14760
14761 /* remove it from the settings representation */
14762 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14763 for (settings::GuestPropertiesList::iterator
14764 it = llGuestProperties.begin();
14765 it != llGuestProperties.end();
14766 /*nothing*/)
14767 {
14768 const settings::GuestProperty &prop = *it;
14769 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14770 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14771 {
14772 it = llGuestProperties.erase(it);
14773 fNeedsSaving = true;
14774 }
14775 else
14776 {
14777 ++it;
14778 }
14779 }
14780
14781 /* Additionally remove it from the HWData representation. Required to
14782 * keep everything in sync, as this is what the API keeps using. */
14783 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14784 for (HWData::GuestPropertyMap::iterator
14785 it = llHWGuestProperties.begin();
14786 it != llHWGuestProperties.end();
14787 /*nothing*/)
14788 {
14789 uint32_t fFlags = it->second.mFlags;
14790 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14791 {
14792 /* iterator where we need to continue after the erase call
14793 * (C++03 is a fact still, and it doesn't return the iterator
14794 * which would allow continuing) */
14795 HWData::GuestPropertyMap::iterator it2 = it;
14796 ++it2;
14797 llHWGuestProperties.erase(it);
14798 it = it2;
14799 fNeedsSaving = true;
14800 }
14801 else
14802 {
14803 ++it;
14804 }
14805 }
14806
14807 if (fNeedsSaving)
14808 {
14809 mData->mCurrentStateModified = TRUE;
14810 stsFlags |= SaveSTS_CurStateModified;
14811 }
14812 }
14813#endif /* VBOX_WITH_GUEST_PROPS */
14814
14815 rc = i_saveStateSettings(stsFlags);
14816
14817 if ( ( oldMachineState != MachineState_PoweredOff
14818 && oldMachineState != MachineState_Aborted
14819 && oldMachineState != MachineState_Teleported
14820 )
14821 && ( aMachineState == MachineState_PoweredOff
14822 || aMachineState == MachineState_Aborted
14823 || aMachineState == MachineState_Teleported
14824 )
14825 )
14826 {
14827 /* we've been shut down for any reason */
14828 /* no special action so far */
14829 }
14830
14831 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14832 LogFlowThisFuncLeave();
14833 return rc;
14834}
14835
14836/**
14837 * Sends the current machine state value to the VM process.
14838 *
14839 * @note Locks this object for reading, then calls a client process.
14840 */
14841HRESULT SessionMachine::i_updateMachineStateOnClient()
14842{
14843 AutoCaller autoCaller(this);
14844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14845
14846 ComPtr<IInternalSessionControl> directControl;
14847 {
14848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14849 AssertReturn(!!mData, E_FAIL);
14850 if (mData->mSession.mLockType == LockType_VM)
14851 directControl = mData->mSession.mDirectControl;
14852
14853 /* directControl may be already set to NULL here in #OnSessionEnd()
14854 * called too early by the direct session process while there is still
14855 * some operation (like deleting the snapshot) in progress. The client
14856 * process in this case is waiting inside Session::close() for the
14857 * "end session" process object to complete, while #uninit() called by
14858 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14859 * operation to complete. For now, we accept this inconsistent behavior
14860 * and simply do nothing here. */
14861
14862 if (mData->mSession.mState == SessionState_Unlocking)
14863 return S_OK;
14864 }
14865
14866 /* ignore notifications sent after #OnSessionEnd() is called */
14867 if (!directControl)
14868 return S_OK;
14869
14870 return directControl->UpdateMachineState(mData->mMachineState);
14871}
14872
14873
14874/*static*/
14875HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14876{
14877 va_list args;
14878 va_start(args, pcszMsg);
14879 HRESULT rc = setErrorInternalV(aResultCode,
14880 getStaticClassIID(),
14881 getStaticComponentName(),
14882 pcszMsg, args,
14883 false /* aWarning */,
14884 true /* aLogIt */);
14885 va_end(args);
14886 return rc;
14887}
14888
14889
14890HRESULT Machine::updateState(MachineState_T aState)
14891{
14892 NOREF(aState);
14893 ReturnComNotImplemented();
14894}
14895
14896HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14897{
14898 NOREF(aProgress);
14899 ReturnComNotImplemented();
14900}
14901
14902HRESULT Machine::endPowerUp(LONG aResult)
14903{
14904 NOREF(aResult);
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14909{
14910 NOREF(aProgress);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::endPoweringDown(LONG aResult,
14915 const com::Utf8Str &aErrMsg)
14916{
14917 NOREF(aResult);
14918 NOREF(aErrMsg);
14919 ReturnComNotImplemented();
14920}
14921
14922HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14923 BOOL *aMatched,
14924 ULONG *aMaskedInterfaces)
14925{
14926 NOREF(aDevice);
14927 NOREF(aMatched);
14928 NOREF(aMaskedInterfaces);
14929 ReturnComNotImplemented();
14930
14931}
14932
14933HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14934{
14935 NOREF(aId); NOREF(aCaptureFilename);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14940 BOOL aDone)
14941{
14942 NOREF(aId);
14943 NOREF(aDone);
14944 ReturnComNotImplemented();
14945}
14946
14947HRESULT Machine::autoCaptureUSBDevices()
14948{
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14953{
14954 NOREF(aDone);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14959 ComPtr<IProgress> &aProgress)
14960{
14961 NOREF(aSession);
14962 NOREF(aProgress);
14963 ReturnComNotImplemented();
14964}
14965
14966HRESULT Machine::finishOnlineMergeMedium()
14967{
14968 ReturnComNotImplemented();
14969}
14970
14971HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14972 std::vector<com::Utf8Str> &aValues,
14973 std::vector<LONG64> &aTimestamps,
14974 std::vector<com::Utf8Str> &aFlags)
14975{
14976 NOREF(aNames);
14977 NOREF(aValues);
14978 NOREF(aTimestamps);
14979 NOREF(aFlags);
14980 ReturnComNotImplemented();
14981}
14982
14983HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14984 const com::Utf8Str &aValue,
14985 LONG64 aTimestamp,
14986 const com::Utf8Str &aFlags)
14987{
14988 NOREF(aName);
14989 NOREF(aValue);
14990 NOREF(aTimestamp);
14991 NOREF(aFlags);
14992 ReturnComNotImplemented();
14993}
14994
14995HRESULT Machine::lockMedia()
14996{
14997 ReturnComNotImplemented();
14998}
14999
15000HRESULT Machine::unlockMedia()
15001{
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15006 ComPtr<IMediumAttachment> &aNewAttachment)
15007{
15008 NOREF(aAttachment);
15009 NOREF(aNewAttachment);
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15014 ULONG aCpuUser,
15015 ULONG aCpuKernel,
15016 ULONG aCpuIdle,
15017 ULONG aMemTotal,
15018 ULONG aMemFree,
15019 ULONG aMemBalloon,
15020 ULONG aMemShared,
15021 ULONG aMemCache,
15022 ULONG aPagedTotal,
15023 ULONG aMemAllocTotal,
15024 ULONG aMemFreeTotal,
15025 ULONG aMemBalloonTotal,
15026 ULONG aMemSharedTotal,
15027 ULONG aVmNetRx,
15028 ULONG aVmNetTx)
15029{
15030 NOREF(aValidStats);
15031 NOREF(aCpuUser);
15032 NOREF(aCpuKernel);
15033 NOREF(aCpuIdle);
15034 NOREF(aMemTotal);
15035 NOREF(aMemFree);
15036 NOREF(aMemBalloon);
15037 NOREF(aMemShared);
15038 NOREF(aMemCache);
15039 NOREF(aPagedTotal);
15040 NOREF(aMemAllocTotal);
15041 NOREF(aMemFreeTotal);
15042 NOREF(aMemBalloonTotal);
15043 NOREF(aMemSharedTotal);
15044 NOREF(aVmNetRx);
15045 NOREF(aVmNetTx);
15046 ReturnComNotImplemented();
15047}
15048
15049HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15050 com::Utf8Str &aResult)
15051{
15052 NOREF(aAuthParams);
15053 NOREF(aResult);
15054 ReturnComNotImplemented();
15055}
15056
15057com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15058{
15059 com::Utf8Str strControllerName = "Unknown";
15060 switch (aBusType)
15061 {
15062 case StorageBus_IDE:
15063 {
15064 strControllerName = "IDE";
15065 break;
15066 }
15067 case StorageBus_SATA:
15068 {
15069 strControllerName = "SATA";
15070 break;
15071 }
15072 case StorageBus_SCSI:
15073 {
15074 strControllerName = "SCSI";
15075 break;
15076 }
15077 case StorageBus_Floppy:
15078 {
15079 strControllerName = "Floppy";
15080 break;
15081 }
15082 case StorageBus_SAS:
15083 {
15084 strControllerName = "SAS";
15085 break;
15086 }
15087 case StorageBus_USB:
15088 {
15089 strControllerName = "USB";
15090 break;
15091 }
15092 default:
15093 break;
15094 }
15095 return strControllerName;
15096}
15097
15098HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15099{
15100 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15101
15102 AutoCaller autoCaller(this);
15103 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15104
15105 HRESULT rc = S_OK;
15106
15107 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15108 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15109 rc = getUSBDeviceFilters(usbDeviceFilters);
15110 if (FAILED(rc)) return rc;
15111
15112 NOREF(aFlags);
15113 com::Utf8Str osTypeId;
15114 ComObjPtr<GuestOSType> osType = NULL;
15115
15116 /* Get the guest os type as a string from the VB. */
15117 rc = getOSTypeId(osTypeId);
15118 if (FAILED(rc)) return rc;
15119
15120 /* Get the os type obj that coresponds, can be used to get
15121 * the defaults for this guest OS. */
15122 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15123 if (FAILED(rc)) return rc;
15124
15125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15126
15127 /* Let the OS type select 64-bit ness. */
15128 mHWData->mLongMode = osType->i_is64Bit()
15129 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15130
15131 /* Let the OS type enable the X2APIC */
15132 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15133
15134 /* This one covers IOAPICEnabled. */
15135 mBIOSSettings->i_applyDefaults(osType);
15136
15137 /* Initialize default record settings. */
15138 mRecordingSettings->i_applyDefaults();
15139
15140 /* Initialize default BIOS settings here */
15141 /* Hardware virtualization must be ON by default */
15142 mHWData->mAPIC = true;
15143 mHWData->mHWVirtExEnabled = true;
15144
15145 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15146 if (FAILED(rc)) return rc;
15147
15148 /* Graphics stuff. */
15149 GraphicsControllerType_T graphicsController;
15150 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15151 if (FAILED(rc)) return rc;
15152
15153 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15154 if (FAILED(rc)) return rc;
15155
15156 ULONG vramSize;
15157 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15158 if (FAILED(rc)) return rc;
15159
15160 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15161 if (FAILED(rc)) return rc;
15162
15163 BOOL fAccelerate2DVideoEnabled;
15164 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15165 if (FAILED(rc)) return rc;
15166
15167 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15168 if (FAILED(rc)) return rc;
15169
15170 BOOL fAccelerate3DEnabled;
15171 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15172 if (FAILED(rc)) return rc;
15173
15174 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15175 if (FAILED(rc)) return rc;
15176
15177 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15178 if (FAILED(rc)) return rc;
15179
15180 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15181 if (FAILED(rc)) return rc;
15182
15183 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15184 if (FAILED(rc)) return rc;
15185
15186 BOOL mRTCUseUTC;
15187 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15188 if (FAILED(rc)) return rc;
15189
15190 setRTCUseUTC(mRTCUseUTC);
15191 if (FAILED(rc)) return rc;
15192
15193 /* the setter does more than just the assignment, so use it */
15194 ChipsetType_T enmChipsetType;
15195 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15196 if (FAILED(rc)) return rc;
15197
15198 rc = COMSETTER(ChipsetType)(enmChipsetType);
15199 if (FAILED(rc)) return rc;
15200
15201 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15202 if (FAILED(rc)) return rc;
15203
15204 /* Apply IOMMU defaults. */
15205 IommuType_T enmIommuType;
15206 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15207 if (FAILED(rc)) return rc;
15208
15209 rc = COMSETTER(IommuType)(enmIommuType);
15210 if (FAILED(rc)) return rc;
15211
15212 /* Apply network adapters defaults */
15213 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15214 mNetworkAdapters[slot]->i_applyDefaults(osType);
15215
15216 /* Apply serial port defaults */
15217 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15218 mSerialPorts[slot]->i_applyDefaults(osType);
15219
15220 /* Apply parallel port defaults - not OS dependent*/
15221 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15222 mParallelPorts[slot]->i_applyDefaults();
15223
15224 /* Audio stuff. */
15225 AudioControllerType_T audioController;
15226 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15227 if (FAILED(rc)) return rc;
15228
15229 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15230 if (FAILED(rc)) return rc;
15231
15232 AudioCodecType_T audioCodec;
15233 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15237 if (FAILED(rc)) return rc;
15238
15239 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15240 if (FAILED(rc)) return rc;
15241
15242 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15243 if (FAILED(rc)) return rc;
15244
15245 /* Storage Controllers */
15246 StorageControllerType_T hdStorageControllerType;
15247 StorageBus_T hdStorageBusType;
15248 StorageControllerType_T dvdStorageControllerType;
15249 StorageBus_T dvdStorageBusType;
15250 BOOL recommendedFloppy;
15251 ComPtr<IStorageController> floppyController;
15252 ComPtr<IStorageController> hdController;
15253 ComPtr<IStorageController> dvdController;
15254 Utf8Str strFloppyName, strDVDName, strHDName;
15255
15256 /* GUI auto generates controller names using bus type. Do the same*/
15257 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15258
15259 /* Floppy recommended? add one. */
15260 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15261 if (FAILED(rc)) return rc;
15262 if (recommendedFloppy)
15263 {
15264 rc = addStorageController(strFloppyName,
15265 StorageBus_Floppy,
15266 floppyController);
15267 if (FAILED(rc)) return rc;
15268 }
15269
15270 /* Setup one DVD storage controller. */
15271 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15275 if (FAILED(rc)) return rc;
15276
15277 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15278
15279 rc = addStorageController(strDVDName,
15280 dvdStorageBusType,
15281 dvdController);
15282 if (FAILED(rc)) return rc;
15283
15284 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15285 if (FAILED(rc)) return rc;
15286
15287 /* Setup one HDD storage controller. */
15288 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15289 if (FAILED(rc)) return rc;
15290
15291 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15292 if (FAILED(rc)) return rc;
15293
15294 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15295
15296 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15297 {
15298 rc = addStorageController(strHDName,
15299 hdStorageBusType,
15300 hdController);
15301 if (FAILED(rc)) return rc;
15302
15303 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15304 if (FAILED(rc)) return rc;
15305 }
15306 else
15307 {
15308 /* The HD controller is the same as DVD: */
15309 hdController = dvdController;
15310 }
15311
15312 /* Limit the AHCI port count if it's used because windows has trouble with
15313 * too many ports and other guest (OS X in particular) may take extra long
15314 * boot: */
15315
15316 // pParent = static_cast<Medium*>(aP)
15317 IStorageController *temp = hdController;
15318 ComObjPtr<StorageController> storageController;
15319 storageController = static_cast<StorageController *>(temp);
15320
15321 // tempHDController = aHDController;
15322 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15323 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15324 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15325 storageController->COMSETTER(PortCount)(1);
15326
15327 /* USB stuff */
15328
15329 bool ohciEnabled = false;
15330
15331 ComPtr<IUSBController> usbController;
15332 BOOL recommendedUSB3;
15333 BOOL recommendedUSB;
15334 BOOL usbProxyAvailable;
15335
15336 getUSBProxyAvailable(&usbProxyAvailable);
15337 if (FAILED(rc)) return rc;
15338
15339 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15340 if (FAILED(rc)) return rc;
15341 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15342 if (FAILED(rc)) return rc;
15343
15344 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15345 {
15346#ifdef VBOX_WITH_EXTPACK
15347 /* USB 3.0 is only available if the proper ExtPack is installed. */
15348 ExtPackManager *aManager = mParent->i_getExtPackManager();
15349 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15350 {
15351 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15352 if (FAILED(rc)) return rc;
15353
15354 /* xHci includes OHCI */
15355 ohciEnabled = true;
15356 }
15357#endif
15358 }
15359 if ( !ohciEnabled
15360 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15361 {
15362 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15363 if (FAILED(rc)) return rc;
15364 ohciEnabled = true;
15365
15366#ifdef VBOX_WITH_EXTPACK
15367 /* USB 2.0 is only available if the proper ExtPack is installed.
15368 * Note. Configuring EHCI here and providing messages about
15369 * the missing extpack isn't exactly clean, but it is a
15370 * necessary evil to patch over legacy compatability issues
15371 * introduced by the new distribution model. */
15372 ExtPackManager *manager = mParent->i_getExtPackManager();
15373 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15374 {
15375 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15376 if (FAILED(rc)) return rc;
15377 }
15378#endif
15379 }
15380
15381 /* Set recommended human interface device types: */
15382 BOOL recommendedUSBHID;
15383 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15384 if (FAILED(rc)) return rc;
15385
15386 if (recommendedUSBHID)
15387 {
15388 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15389 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15390 if (!ohciEnabled && !usbDeviceFilters.isNull())
15391 {
15392 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15393 if (FAILED(rc)) return rc;
15394 }
15395 }
15396
15397 BOOL recommendedUSBTablet;
15398 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15399 if (FAILED(rc)) return rc;
15400
15401 if (recommendedUSBTablet)
15402 {
15403 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15404 if (!ohciEnabled && !usbDeviceFilters.isNull())
15405 {
15406 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15407 if (FAILED(rc)) return rc;
15408 }
15409 }
15410 return S_OK;
15411}
15412
15413/* This isn't handled entirely by the wrapper generator yet. */
15414#ifdef VBOX_WITH_XPCOM
15415NS_DECL_CLASSINFO(SessionMachine)
15416NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15417
15418NS_DECL_CLASSINFO(SnapshotMachine)
15419NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15420#endif
Note: See TracBrowser for help on using the repository browser.

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