VirtualBox

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

Last change on this file since 90441 was 89868, checked in by vboxsync, 3 years ago

Main/Console+Machine: Eliminate CMPXCHG16B hack (see bugref:6300) since it is no longer needed. Also make the ParavirtProvider check (Windows Server versions) for the Hyper-V case somewhat future proof. Add a reminder to Global.cpp when adding new guest OS types.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.9 KB
Line 
1/* $Id: MachineImpl.cpp 89868 2021-06-23 18:02:11Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
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::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1851{
1852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853
1854 aRecordingSettings = mRecordingSettings;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862
1863 aGraphicsAdapter = mGraphicsAdapter;
1864
1865 return S_OK;
1866}
1867
1868HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1869{
1870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1871
1872 switch (aProperty)
1873 {
1874 case CPUPropertyType_PAE:
1875 *aValue = mHWData->mPAEEnabled;
1876 break;
1877
1878 case CPUPropertyType_LongMode:
1879 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1880 *aValue = TRUE;
1881 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1882 *aValue = FALSE;
1883#if HC_ARCH_BITS == 64
1884 else
1885 *aValue = TRUE;
1886#else
1887 else
1888 {
1889 *aValue = FALSE;
1890
1891 ComObjPtr<GuestOSType> pGuestOSType;
1892 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1893 pGuestOSType);
1894 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1895 {
1896 if (pGuestOSType->i_is64Bit())
1897 {
1898 ComObjPtr<Host> pHost = mParent->i_host();
1899 alock.release();
1900
1901 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1902 if (FAILED(hrc2))
1903 *aValue = FALSE;
1904 }
1905 }
1906 }
1907#endif
1908 break;
1909
1910 case CPUPropertyType_TripleFaultReset:
1911 *aValue = mHWData->mTripleFaultReset;
1912 break;
1913
1914 case CPUPropertyType_APIC:
1915 *aValue = mHWData->mAPIC;
1916 break;
1917
1918 case CPUPropertyType_X2APIC:
1919 *aValue = mHWData->mX2APIC;
1920 break;
1921
1922 case CPUPropertyType_IBPBOnVMExit:
1923 *aValue = mHWData->mIBPBOnVMExit;
1924 break;
1925
1926 case CPUPropertyType_IBPBOnVMEntry:
1927 *aValue = mHWData->mIBPBOnVMEntry;
1928 break;
1929
1930 case CPUPropertyType_SpecCtrl:
1931 *aValue = mHWData->mSpecCtrl;
1932 break;
1933
1934 case CPUPropertyType_SpecCtrlByHost:
1935 *aValue = mHWData->mSpecCtrlByHost;
1936 break;
1937
1938 case CPUPropertyType_HWVirt:
1939 *aValue = mHWData->mNestedHWVirt;
1940 break;
1941
1942 case CPUPropertyType_L1DFlushOnEMTScheduling:
1943 *aValue = mHWData->mL1DFlushOnSched;
1944 break;
1945
1946 case CPUPropertyType_L1DFlushOnVMEntry:
1947 *aValue = mHWData->mL1DFlushOnVMEntry;
1948 break;
1949
1950 case CPUPropertyType_MDSClearOnEMTScheduling:
1951 *aValue = mHWData->mMDSClearOnSched;
1952 break;
1953
1954 case CPUPropertyType_MDSClearOnVMEntry:
1955 *aValue = mHWData->mMDSClearOnVMEntry;
1956 break;
1957
1958 default:
1959 return E_INVALIDARG;
1960 }
1961 return S_OK;
1962}
1963
1964HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1965{
1966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 HRESULT rc = i_checkStateDependency(MutableStateDep);
1969 if (FAILED(rc)) return rc;
1970
1971 switch (aProperty)
1972 {
1973 case CPUPropertyType_PAE:
1974 i_setModified(IsModified_MachineData);
1975 mHWData.backup();
1976 mHWData->mPAEEnabled = !!aValue;
1977 break;
1978
1979 case CPUPropertyType_LongMode:
1980 i_setModified(IsModified_MachineData);
1981 mHWData.backup();
1982 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1983 break;
1984
1985 case CPUPropertyType_TripleFaultReset:
1986 i_setModified(IsModified_MachineData);
1987 mHWData.backup();
1988 mHWData->mTripleFaultReset = !!aValue;
1989 break;
1990
1991 case CPUPropertyType_APIC:
1992 if (mHWData->mX2APIC)
1993 aValue = TRUE;
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mAPIC = !!aValue;
1997 break;
1998
1999 case CPUPropertyType_X2APIC:
2000 i_setModified(IsModified_MachineData);
2001 mHWData.backup();
2002 mHWData->mX2APIC = !!aValue;
2003 if (aValue)
2004 mHWData->mAPIC = !!aValue;
2005 break;
2006
2007 case CPUPropertyType_IBPBOnVMExit:
2008 i_setModified(IsModified_MachineData);
2009 mHWData.backup();
2010 mHWData->mIBPBOnVMExit = !!aValue;
2011 break;
2012
2013 case CPUPropertyType_IBPBOnVMEntry:
2014 i_setModified(IsModified_MachineData);
2015 mHWData.backup();
2016 mHWData->mIBPBOnVMEntry = !!aValue;
2017 break;
2018
2019 case CPUPropertyType_SpecCtrl:
2020 i_setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mSpecCtrl = !!aValue;
2023 break;
2024
2025 case CPUPropertyType_SpecCtrlByHost:
2026 i_setModified(IsModified_MachineData);
2027 mHWData.backup();
2028 mHWData->mSpecCtrlByHost = !!aValue;
2029 break;
2030
2031 case CPUPropertyType_HWVirt:
2032 i_setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mNestedHWVirt = !!aValue;
2035 break;
2036
2037 case CPUPropertyType_L1DFlushOnEMTScheduling:
2038 i_setModified(IsModified_MachineData);
2039 mHWData.backup();
2040 mHWData->mL1DFlushOnSched = !!aValue;
2041 break;
2042
2043 case CPUPropertyType_L1DFlushOnVMEntry:
2044 i_setModified(IsModified_MachineData);
2045 mHWData.backup();
2046 mHWData->mL1DFlushOnVMEntry = !!aValue;
2047 break;
2048
2049 case CPUPropertyType_MDSClearOnEMTScheduling:
2050 i_setModified(IsModified_MachineData);
2051 mHWData.backup();
2052 mHWData->mMDSClearOnSched = !!aValue;
2053 break;
2054
2055 case CPUPropertyType_MDSClearOnVMEntry:
2056 i_setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 mHWData->mMDSClearOnVMEntry = !!aValue;
2059 break;
2060
2061 default:
2062 return E_INVALIDARG;
2063 }
2064 return S_OK;
2065}
2066
2067HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2068 ULONG *aValEcx, ULONG *aValEdx)
2069{
2070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2071 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2072 {
2073 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2074 it != mHWData->mCpuIdLeafList.end();
2075 ++it)
2076 {
2077 if (aOrdinal == 0)
2078 {
2079 const settings::CpuIdLeaf &rLeaf= *it;
2080 *aIdx = rLeaf.idx;
2081 *aSubIdx = rLeaf.idxSub;
2082 *aValEax = rLeaf.uEax;
2083 *aValEbx = rLeaf.uEbx;
2084 *aValEcx = rLeaf.uEcx;
2085 *aValEdx = rLeaf.uEdx;
2086 return S_OK;
2087 }
2088 aOrdinal--;
2089 }
2090 }
2091 return E_INVALIDARG;
2092}
2093
2094HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2095{
2096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 /*
2099 * Search the list.
2100 */
2101 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2102 {
2103 const settings::CpuIdLeaf &rLeaf= *it;
2104 if ( rLeaf.idx == aIdx
2105 && ( aSubIdx == UINT32_MAX
2106 || rLeaf.idxSub == aSubIdx) )
2107 {
2108 *aValEax = rLeaf.uEax;
2109 *aValEbx = rLeaf.uEbx;
2110 *aValEcx = rLeaf.uEcx;
2111 *aValEdx = rLeaf.uEdx;
2112 return S_OK;
2113 }
2114 }
2115
2116 return E_INVALIDARG;
2117}
2118
2119
2120HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2121{
2122 /*
2123 * Validate input before taking locks and checking state.
2124 */
2125 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2126 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2127 if ( aIdx >= UINT32_C(0x20)
2128 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2129 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2130 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2131
2132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2133 HRESULT rc = i_checkStateDependency(MutableStateDep);
2134 if (FAILED(rc)) return rc;
2135
2136 /*
2137 * Impose a maximum number of leaves.
2138 */
2139 if (mHWData->mCpuIdLeafList.size() > 256)
2140 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2141
2142 /*
2143 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2144 */
2145 i_setModified(IsModified_MachineData);
2146 mHWData.backup();
2147
2148 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2149 {
2150 settings::CpuIdLeaf &rLeaf= *it;
2151 if ( rLeaf.idx == aIdx
2152 && ( aSubIdx == UINT32_MAX
2153 || rLeaf.idxSub == aSubIdx) )
2154 it = mHWData->mCpuIdLeafList.erase(it);
2155 else
2156 ++it;
2157 }
2158
2159 settings::CpuIdLeaf NewLeaf;
2160 NewLeaf.idx = aIdx;
2161 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2162 NewLeaf.uEax = aValEax;
2163 NewLeaf.uEbx = aValEbx;
2164 NewLeaf.uEcx = aValEcx;
2165 NewLeaf.uEdx = aValEdx;
2166 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2167 return S_OK;
2168}
2169
2170HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2171{
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = i_checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 /*
2178 * Do the removal.
2179 */
2180 bool fModified = mHWData.isBackedUp();
2181 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2182 {
2183 settings::CpuIdLeaf &rLeaf= *it;
2184 if ( rLeaf.idx == aIdx
2185 && ( aSubIdx == UINT32_MAX
2186 || rLeaf.idxSub == aSubIdx) )
2187 {
2188 if (!fModified)
2189 {
2190 fModified = true;
2191 i_setModified(IsModified_MachineData);
2192 mHWData.backup();
2193 // Start from the beginning, since mHWData.backup() creates
2194 // a new list, causing iterator mixup. This makes sure that
2195 // the settings are not unnecessarily marked as modified,
2196 // at the price of extra list walking.
2197 it = mHWData->mCpuIdLeafList.begin();
2198 }
2199 else
2200 it = mHWData->mCpuIdLeafList.erase(it);
2201 }
2202 else
2203 ++it;
2204 }
2205
2206 return S_OK;
2207}
2208
2209HRESULT Machine::removeAllCPUIDLeaves()
2210{
2211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2212
2213 HRESULT rc = i_checkStateDependency(MutableStateDep);
2214 if (FAILED(rc)) return rc;
2215
2216 if (mHWData->mCpuIdLeafList.size() > 0)
2217 {
2218 i_setModified(IsModified_MachineData);
2219 mHWData.backup();
2220
2221 mHWData->mCpuIdLeafList.clear();
2222 }
2223
2224 return S_OK;
2225}
2226HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2227{
2228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2229
2230 switch(aProperty)
2231 {
2232 case HWVirtExPropertyType_Enabled:
2233 *aValue = mHWData->mHWVirtExEnabled;
2234 break;
2235
2236 case HWVirtExPropertyType_VPID:
2237 *aValue = mHWData->mHWVirtExVPIDEnabled;
2238 break;
2239
2240 case HWVirtExPropertyType_NestedPaging:
2241 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2242 break;
2243
2244 case HWVirtExPropertyType_UnrestrictedExecution:
2245 *aValue = mHWData->mHWVirtExUXEnabled;
2246 break;
2247
2248 case HWVirtExPropertyType_LargePages:
2249 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2250#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2251 *aValue = FALSE;
2252#endif
2253 break;
2254
2255 case HWVirtExPropertyType_Force:
2256 *aValue = mHWData->mHWVirtExForceEnabled;
2257 break;
2258
2259 case HWVirtExPropertyType_UseNativeApi:
2260 *aValue = mHWData->mHWVirtExUseNativeApi;
2261 break;
2262
2263 case HWVirtExPropertyType_VirtVmsaveVmload:
2264 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2265 break;
2266
2267 default:
2268 return E_INVALIDARG;
2269 }
2270 return S_OK;
2271}
2272
2273HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2274{
2275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2276
2277 HRESULT rc = i_checkStateDependency(MutableStateDep);
2278 if (FAILED(rc)) return rc;
2279
2280 switch (aProperty)
2281 {
2282 case HWVirtExPropertyType_Enabled:
2283 i_setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mHWVirtExEnabled = !!aValue;
2286 break;
2287
2288 case HWVirtExPropertyType_VPID:
2289 i_setModified(IsModified_MachineData);
2290 mHWData.backup();
2291 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2292 break;
2293
2294 case HWVirtExPropertyType_NestedPaging:
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2298 break;
2299
2300 case HWVirtExPropertyType_UnrestrictedExecution:
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mHWVirtExUXEnabled = !!aValue;
2304 break;
2305
2306 case HWVirtExPropertyType_LargePages:
2307 i_setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2310 break;
2311
2312 case HWVirtExPropertyType_Force:
2313 i_setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mHWVirtExForceEnabled = !!aValue;
2316 break;
2317
2318 case HWVirtExPropertyType_UseNativeApi:
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mHWVirtExUseNativeApi = !!aValue;
2322 break;
2323
2324 case HWVirtExPropertyType_VirtVmsaveVmload:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2328 break;
2329
2330 default:
2331 return E_INVALIDARG;
2332 }
2333
2334 return S_OK;
2335}
2336
2337HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2338{
2339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2340
2341 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2342
2343 return S_OK;
2344}
2345
2346HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2347{
2348 /** @todo (r=dmik):
2349 * 1. Allow to change the name of the snapshot folder containing snapshots
2350 * 2. Rename the folder on disk instead of just changing the property
2351 * value (to be smart and not to leave garbage). Note that it cannot be
2352 * done here because the change may be rolled back. Thus, the right
2353 * place is #saveSettings().
2354 */
2355
2356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2357
2358 HRESULT rc = i_checkStateDependency(MutableStateDep);
2359 if (FAILED(rc)) return rc;
2360
2361 if (!mData->mCurrentSnapshot.isNull())
2362 return setError(E_FAIL,
2363 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2364
2365 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2366
2367 if (strSnapshotFolder.isEmpty())
2368 strSnapshotFolder = "Snapshots";
2369 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2370 if (RT_FAILURE(vrc))
2371 return setErrorBoth(E_FAIL, vrc,
2372 tr("Invalid snapshot folder '%s' (%Rrc)"),
2373 strSnapshotFolder.c_str(), vrc);
2374
2375 i_setModified(IsModified_MachineData);
2376 mUserData.backup();
2377
2378 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2379
2380 return S_OK;
2381}
2382
2383HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2384{
2385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2386
2387 aMediumAttachments.resize(mMediumAttachments->size());
2388 size_t i = 0;
2389 for (MediumAttachmentList::const_iterator
2390 it = mMediumAttachments->begin();
2391 it != mMediumAttachments->end();
2392 ++it, ++i)
2393 aMediumAttachments[i] = *it;
2394
2395 return S_OK;
2396}
2397
2398HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2399{
2400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2401
2402 Assert(!!mVRDEServer);
2403
2404 aVRDEServer = mVRDEServer;
2405
2406 return S_OK;
2407}
2408
2409HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2410{
2411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2412
2413 aAudioAdapter = mAudioAdapter;
2414
2415 return S_OK;
2416}
2417
2418HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2419{
2420#ifdef VBOX_WITH_VUSB
2421 clearError();
2422 MultiResult rc(S_OK);
2423
2424# ifdef VBOX_WITH_USB
2425 rc = mParent->i_host()->i_checkUSBProxyService();
2426 if (FAILED(rc)) return rc;
2427# endif
2428
2429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2430
2431 aUSBControllers.resize(mUSBControllers->size());
2432 size_t i = 0;
2433 for (USBControllerList::const_iterator
2434 it = mUSBControllers->begin();
2435 it != mUSBControllers->end();
2436 ++it, ++i)
2437 aUSBControllers[i] = *it;
2438
2439 return S_OK;
2440#else
2441 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2442 * extended error info to indicate that USB is simply not available
2443 * (w/o treating it as a failure), for example, as in OSE */
2444 NOREF(aUSBControllers);
2445 ReturnComNotImplemented();
2446#endif /* VBOX_WITH_VUSB */
2447}
2448
2449HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2450{
2451#ifdef VBOX_WITH_VUSB
2452 clearError();
2453 MultiResult rc(S_OK);
2454
2455# ifdef VBOX_WITH_USB
2456 rc = mParent->i_host()->i_checkUSBProxyService();
2457 if (FAILED(rc)) return rc;
2458# endif
2459
2460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2461
2462 aUSBDeviceFilters = mUSBDeviceFilters;
2463 return rc;
2464#else
2465 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2466 * extended error info to indicate that USB is simply not available
2467 * (w/o treating it as a failure), for example, as in OSE */
2468 NOREF(aUSBDeviceFilters);
2469 ReturnComNotImplemented();
2470#endif /* VBOX_WITH_VUSB */
2471}
2472
2473HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2474{
2475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2476
2477 aSettingsFilePath = mData->m_strConfigFileFull;
2478
2479 return S_OK;
2480}
2481
2482HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2483{
2484 RT_NOREF(aSettingsFilePath);
2485 ReturnComNotImplemented();
2486}
2487
2488HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2489{
2490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2493 if (FAILED(rc)) return rc;
2494
2495 if (!mData->pMachineConfigFile->fileExists())
2496 // this is a new machine, and no config file exists yet:
2497 *aSettingsModified = TRUE;
2498 else
2499 *aSettingsModified = (mData->flModifications != 0);
2500
2501 return S_OK;
2502}
2503
2504HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2505{
2506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 *aSessionState = mData->mSession.mState;
2509
2510 return S_OK;
2511}
2512
2513HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2514{
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 aSessionName = mData->mSession.mName;
2518
2519 return S_OK;
2520}
2521
2522HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2523{
2524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 *aSessionPID = mData->mSession.mPID;
2527
2528 return S_OK;
2529}
2530
2531HRESULT Machine::getState(MachineState_T *aState)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 *aState = mData->mMachineState;
2536 Assert(mData->mMachineState != MachineState_Null);
2537
2538 return S_OK;
2539}
2540
2541HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2542{
2543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2544
2545 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2546
2547 return S_OK;
2548}
2549
2550HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2551{
2552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2553
2554 aStateFilePath = mSSData->strStateFilePath;
2555
2556 return S_OK;
2557}
2558
2559HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2560{
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 i_getLogFolder(aLogFolder);
2564
2565 return S_OK;
2566}
2567
2568HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2569{
2570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2571
2572 aCurrentSnapshot = mData->mCurrentSnapshot;
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2582 ? 0
2583 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 /* Note: for machines with no snapshots, we always return FALSE
2593 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2594 * reasons :) */
2595
2596 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2597 ? FALSE
2598 : mData->mCurrentStateModified;
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 aSharedFolders.resize(mHWData->mSharedFolders.size());
2608 size_t i = 0;
2609 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2610 it = mHWData->mSharedFolders.begin();
2611 it != mHWData->mSharedFolders.end();
2612 ++it, ++i)
2613 aSharedFolders[i] = *it;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 *aClipboardMode = mHWData->mClipboardMode;
2623
2624 return S_OK;
2625}
2626
2627HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2628{
2629 HRESULT rc = S_OK;
2630
2631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 alock.release();
2634 rc = i_onClipboardModeChange(aClipboardMode);
2635 alock.acquire();
2636 if (FAILED(rc)) return rc;
2637
2638 i_setModified(IsModified_MachineData);
2639 mHWData.backup();
2640 mHWData->mClipboardMode = aClipboardMode;
2641
2642 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2643 if (Global::IsOnline(mData->mMachineState))
2644 i_saveSettings(NULL);
2645
2646 return S_OK;
2647}
2648
2649HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2654
2655 return S_OK;
2656}
2657
2658HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2659{
2660 HRESULT rc = S_OK;
2661
2662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2663
2664 alock.release();
2665 rc = i_onClipboardFileTransferModeChange(aEnabled);
2666 alock.acquire();
2667 if (FAILED(rc)) return rc;
2668
2669 i_setModified(IsModified_MachineData);
2670 mHWData.backup();
2671 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2672
2673 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2674 if (Global::IsOnline(mData->mMachineState))
2675 i_saveSettings(NULL);
2676
2677 return S_OK;
2678}
2679
2680HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2681{
2682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 *aDnDMode = mHWData->mDnDMode;
2685
2686 return S_OK;
2687}
2688
2689HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2690{
2691 HRESULT rc = S_OK;
2692
2693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 alock.release();
2696 rc = i_onDnDModeChange(aDnDMode);
2697
2698 alock.acquire();
2699 if (FAILED(rc)) return rc;
2700
2701 i_setModified(IsModified_MachineData);
2702 mHWData.backup();
2703 mHWData->mDnDMode = aDnDMode;
2704
2705 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2706 if (Global::IsOnline(mData->mMachineState))
2707 i_saveSettings(NULL);
2708
2709 return S_OK;
2710}
2711
2712HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2713{
2714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 aStorageControllers.resize(mStorageControllers->size());
2717 size_t i = 0;
2718 for (StorageControllerList::const_iterator
2719 it = mStorageControllers->begin();
2720 it != mStorageControllers->end();
2721 ++it, ++i)
2722 aStorageControllers[i] = *it;
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 *aEnabled = mUserData->s.fTeleporterEnabled;
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2737{
2738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 /* Only allow it to be set to true when PoweredOff or Aborted.
2741 (Clearing it is always permitted.) */
2742 if ( aTeleporterEnabled
2743 && mData->mRegistered
2744 && ( !i_isSessionMachine()
2745 || ( mData->mMachineState != MachineState_PoweredOff
2746 && mData->mMachineState != MachineState_Teleported
2747 && mData->mMachineState != MachineState_Aborted
2748 )
2749 )
2750 )
2751 return setError(VBOX_E_INVALID_VM_STATE,
2752 tr("The machine is not powered off (state is %s)"),
2753 Global::stringifyMachineState(mData->mMachineState));
2754
2755 i_setModified(IsModified_MachineData);
2756 mUserData.backup();
2757 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2767
2768 return S_OK;
2769}
2770
2771HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2772{
2773 if (aTeleporterPort >= _64K)
2774 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2775
2776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2779 if (FAILED(rc)) return rc;
2780
2781 i_setModified(IsModified_MachineData);
2782 mUserData.backup();
2783 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2798{
2799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2802 if (FAILED(rc)) return rc;
2803
2804 i_setModified(IsModified_MachineData);
2805 mUserData.backup();
2806 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2807
2808 return S_OK;
2809}
2810
2811HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2812{
2813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2814 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2820{
2821 /*
2822 * Hash the password first.
2823 */
2824 com::Utf8Str aT = aTeleporterPassword;
2825
2826 if (!aT.isEmpty())
2827 {
2828 if (VBoxIsPasswordHashed(&aT))
2829 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2830 VBoxHashPassword(&aT);
2831 }
2832
2833 /*
2834 * Do the update.
2835 */
2836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2837 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2838 if (SUCCEEDED(hrc))
2839 {
2840 i_setModified(IsModified_MachineData);
2841 mUserData.backup();
2842 mUserData->s.strTeleporterPassword = aT;
2843 }
2844
2845 return hrc;
2846}
2847
2848HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2849{
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2858{
2859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 /* Only allow it to be set to true when PoweredOff or Aborted.
2862 (Clearing it is always permitted.) */
2863 if ( aRTCUseUTC
2864 && mData->mRegistered
2865 && ( !i_isSessionMachine()
2866 || ( mData->mMachineState != MachineState_PoweredOff
2867 && mData->mMachineState != MachineState_Teleported
2868 && mData->mMachineState != MachineState_Aborted
2869 )
2870 )
2871 )
2872 return setError(VBOX_E_INVALID_VM_STATE,
2873 tr("The machine is not powered off (state is %s)"),
2874 Global::stringifyMachineState(mData->mMachineState));
2875
2876 i_setModified(IsModified_MachineData);
2877 mUserData.backup();
2878 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2879
2880 return S_OK;
2881}
2882
2883HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2884{
2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2888
2889 return S_OK;
2890}
2891
2892HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2893{
2894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 HRESULT rc = i_checkStateDependency(MutableStateDep);
2897 if (FAILED(rc)) return rc;
2898
2899 i_setModified(IsModified_MachineData);
2900 mHWData.backup();
2901 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2902
2903 return S_OK;
2904}
2905
2906HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2907{
2908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 *aIOCacheSize = mHWData->mIOCacheSize;
2911
2912 return S_OK;
2913}
2914
2915HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2916{
2917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 HRESULT rc = i_checkStateDependency(MutableStateDep);
2920 if (FAILED(rc)) return rc;
2921
2922 i_setModified(IsModified_MachineData);
2923 mHWData.backup();
2924 mHWData->mIOCacheSize = aIOCacheSize;
2925
2926 return S_OK;
2927}
2928
2929
2930/**
2931 * @note Locks objects!
2932 */
2933HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2934 LockType_T aLockType)
2935{
2936 /* check the session state */
2937 SessionState_T state;
2938 HRESULT rc = aSession->COMGETTER(State)(&state);
2939 if (FAILED(rc)) return rc;
2940
2941 if (state != SessionState_Unlocked)
2942 return setError(VBOX_E_INVALID_OBJECT_STATE,
2943 tr("The given session is busy"));
2944
2945 // get the client's IInternalSessionControl interface
2946 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2947 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2948 E_INVALIDARG);
2949
2950 // session name (only used in some code paths)
2951 Utf8Str strSessionName;
2952
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 if (!mData->mRegistered)
2956 return setError(E_UNEXPECTED,
2957 tr("The machine '%s' is not registered"),
2958 mUserData->s.strName.c_str());
2959
2960 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2961
2962 SessionState_T oldState = mData->mSession.mState;
2963 /* Hack: in case the session is closing and there is a progress object
2964 * which allows waiting for the session to be closed, take the opportunity
2965 * and do a limited wait (max. 1 second). This helps a lot when the system
2966 * is busy and thus session closing can take a little while. */
2967 if ( mData->mSession.mState == SessionState_Unlocking
2968 && mData->mSession.mProgress)
2969 {
2970 alock.release();
2971 mData->mSession.mProgress->WaitForCompletion(1000);
2972 alock.acquire();
2973 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2974 }
2975
2976 // try again now
2977 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2978 // (i.e. session machine exists)
2979 && (aLockType == LockType_Shared) // caller wants a shared link to the
2980 // existing session that holds the write lock:
2981 )
2982 {
2983 // OK, share the session... we are now dealing with three processes:
2984 // 1) VBoxSVC (where this code runs);
2985 // 2) process C: the caller's client process (who wants a shared session);
2986 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2987
2988 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2989 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2990 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2991 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2992 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2993
2994 /*
2995 * Release the lock before calling the client process. It's safe here
2996 * since the only thing to do after we get the lock again is to add
2997 * the remote control to the list (which doesn't directly influence
2998 * anything).
2999 */
3000 alock.release();
3001
3002 // get the console of the session holding the write lock (this is a remote call)
3003 ComPtr<IConsole> pConsoleW;
3004 if (mData->mSession.mLockType == LockType_VM)
3005 {
3006 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3007 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3008 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3009 if (FAILED(rc))
3010 // the failure may occur w/o any error info (from RPC), so provide one
3011 return setError(VBOX_E_VM_ERROR,
3012 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3013 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3014 }
3015
3016 // share the session machine and W's console with the caller's session
3017 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3018 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3019 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3020
3021 if (FAILED(rc))
3022 // the failure may occur w/o any error info (from RPC), so provide one
3023 return setError(VBOX_E_VM_ERROR,
3024 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3025 alock.acquire();
3026
3027 // need to revalidate the state after acquiring the lock again
3028 if (mData->mSession.mState != SessionState_Locked)
3029 {
3030 pSessionControl->Uninitialize();
3031 return setError(VBOX_E_INVALID_SESSION_STATE,
3032 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3033 mUserData->s.strName.c_str());
3034 }
3035
3036 // add the caller's session to the list
3037 mData->mSession.mRemoteControls.push_back(pSessionControl);
3038 }
3039 else if ( mData->mSession.mState == SessionState_Locked
3040 || mData->mSession.mState == SessionState_Unlocking
3041 )
3042 {
3043 // sharing not permitted, or machine still unlocking:
3044 return setError(VBOX_E_INVALID_OBJECT_STATE,
3045 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3046 mUserData->s.strName.c_str());
3047 }
3048 else
3049 {
3050 // machine is not locked: then write-lock the machine (create the session machine)
3051
3052 // must not be busy
3053 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3054
3055 // get the caller's session PID
3056 RTPROCESS pid = NIL_RTPROCESS;
3057 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3058 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3059 Assert(pid != NIL_RTPROCESS);
3060
3061 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3062
3063 if (fLaunchingVMProcess)
3064 {
3065 if (mData->mSession.mPID == NIL_RTPROCESS)
3066 {
3067 // two or more clients racing for a lock, the one which set the
3068 // session state to Spawning will win, the others will get an
3069 // error as we can't decide here if waiting a little would help
3070 // (only for shared locks this would avoid an error)
3071 return setError(VBOX_E_INVALID_OBJECT_STATE,
3072 tr("The machine '%s' already has a lock request pending"),
3073 mUserData->s.strName.c_str());
3074 }
3075
3076 // this machine is awaiting for a spawning session to be opened:
3077 // then the calling process must be the one that got started by
3078 // LaunchVMProcess()
3079
3080 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3081 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3082
3083#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3084 /* Hardened windows builds spawns three processes when a VM is
3085 launched, the 3rd one is the one that will end up here. */
3086 RTPROCESS pidParent;
3087 int vrc = RTProcQueryParent(pid, &pidParent);
3088 if (RT_SUCCESS(vrc))
3089 vrc = RTProcQueryParent(pidParent, &pidParent);
3090 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3091 || vrc == VERR_ACCESS_DENIED)
3092 {
3093 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3094 mData->mSession.mPID = pid;
3095 }
3096#endif
3097
3098 if (mData->mSession.mPID != pid)
3099 return setError(E_ACCESSDENIED,
3100 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3101 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3102 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3103 }
3104
3105 // create the mutable SessionMachine from the current machine
3106 ComObjPtr<SessionMachine> sessionMachine;
3107 sessionMachine.createObject();
3108 rc = sessionMachine->init(this);
3109 AssertComRC(rc);
3110
3111 /* NOTE: doing return from this function after this point but
3112 * before the end is forbidden since it may call SessionMachine::uninit()
3113 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3114 * lock while still holding the Machine lock in alock so that a deadlock
3115 * is possible due to the wrong lock order. */
3116
3117 if (SUCCEEDED(rc))
3118 {
3119 /*
3120 * Set the session state to Spawning to protect against subsequent
3121 * attempts to open a session and to unregister the machine after
3122 * we release the lock.
3123 */
3124 SessionState_T origState = mData->mSession.mState;
3125 mData->mSession.mState = SessionState_Spawning;
3126
3127#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3128 /* Get the client token ID to be passed to the client process */
3129 Utf8Str strTokenId;
3130 sessionMachine->i_getTokenId(strTokenId);
3131 Assert(!strTokenId.isEmpty());
3132#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3133 /* Get the client token to be passed to the client process */
3134 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3135 /* The token is now "owned" by pToken, fix refcount */
3136 if (!pToken.isNull())
3137 pToken->Release();
3138#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3139
3140 /*
3141 * Release the lock before calling the client process -- it will call
3142 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3143 * because the state is Spawning, so that LaunchVMProcess() and
3144 * LockMachine() calls will fail. This method, called before we
3145 * acquire the lock again, will fail because of the wrong PID.
3146 *
3147 * Note that mData->mSession.mRemoteControls accessed outside
3148 * the lock may not be modified when state is Spawning, so it's safe.
3149 */
3150 alock.release();
3151
3152 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3153#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3154 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3155#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3156 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3157 /* Now the token is owned by the client process. */
3158 pToken.setNull();
3159#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3160 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3161
3162 /* The failure may occur w/o any error info (from RPC), so provide one */
3163 if (FAILED(rc))
3164 setError(VBOX_E_VM_ERROR,
3165 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3166
3167 // get session name, either to remember or to compare against
3168 // the already known session name.
3169 {
3170 Bstr bstrSessionName;
3171 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3172 if (SUCCEEDED(rc2))
3173 strSessionName = bstrSessionName;
3174 }
3175
3176 if ( SUCCEEDED(rc)
3177 && fLaunchingVMProcess
3178 )
3179 {
3180 /* complete the remote session initialization */
3181
3182 /* get the console from the direct session */
3183 ComPtr<IConsole> console;
3184 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3185 ComAssertComRC(rc);
3186
3187 if (SUCCEEDED(rc) && !console)
3188 {
3189 ComAssert(!!console);
3190 rc = E_FAIL;
3191 }
3192
3193 /* assign machine & console to the remote session */
3194 if (SUCCEEDED(rc))
3195 {
3196 /*
3197 * after LaunchVMProcess(), the first and the only
3198 * entry in remoteControls is that remote session
3199 */
3200 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3201 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3202 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3203
3204 /* The failure may occur w/o any error info (from RPC), so provide one */
3205 if (FAILED(rc))
3206 setError(VBOX_E_VM_ERROR,
3207 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3208 }
3209
3210 if (FAILED(rc))
3211 pSessionControl->Uninitialize();
3212 }
3213
3214 /* acquire the lock again */
3215 alock.acquire();
3216
3217 /* Restore the session state */
3218 mData->mSession.mState = origState;
3219 }
3220
3221 // finalize spawning anyway (this is why we don't return on errors above)
3222 if (fLaunchingVMProcess)
3223 {
3224 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3225 /* Note that the progress object is finalized later */
3226 /** @todo Consider checking mData->mSession.mProgress for cancellation
3227 * around here. */
3228
3229 /* We don't reset mSession.mPID here because it is necessary for
3230 * SessionMachine::uninit() to reap the child process later. */
3231
3232 if (FAILED(rc))
3233 {
3234 /* Close the remote session, remove the remote control from the list
3235 * and reset session state to Closed (@note keep the code in sync
3236 * with the relevant part in checkForSpawnFailure()). */
3237
3238 Assert(mData->mSession.mRemoteControls.size() == 1);
3239 if (mData->mSession.mRemoteControls.size() == 1)
3240 {
3241 ErrorInfoKeeper eik;
3242 mData->mSession.mRemoteControls.front()->Uninitialize();
3243 }
3244
3245 mData->mSession.mRemoteControls.clear();
3246 mData->mSession.mState = SessionState_Unlocked;
3247 }
3248 }
3249 else
3250 {
3251 /* memorize PID of the directly opened session */
3252 if (SUCCEEDED(rc))
3253 mData->mSession.mPID = pid;
3254 }
3255
3256 if (SUCCEEDED(rc))
3257 {
3258 mData->mSession.mLockType = aLockType;
3259 /* memorize the direct session control and cache IUnknown for it */
3260 mData->mSession.mDirectControl = pSessionControl;
3261 mData->mSession.mState = SessionState_Locked;
3262 if (!fLaunchingVMProcess)
3263 mData->mSession.mName = strSessionName;
3264 /* associate the SessionMachine with this Machine */
3265 mData->mSession.mMachine = sessionMachine;
3266
3267 /* request an IUnknown pointer early from the remote party for later
3268 * identity checks (it will be internally cached within mDirectControl
3269 * at least on XPCOM) */
3270 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3271 NOREF(unk);
3272 }
3273
3274 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3275 * would break the lock order */
3276 alock.release();
3277
3278 /* uninitialize the created session machine on failure */
3279 if (FAILED(rc))
3280 sessionMachine->uninit();
3281 }
3282
3283 if (SUCCEEDED(rc))
3284 {
3285 /*
3286 * tell the client watcher thread to update the set of
3287 * machines that have open sessions
3288 */
3289 mParent->i_updateClientWatcher();
3290
3291 if (oldState != SessionState_Locked)
3292 /* fire an event */
3293 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3294 }
3295
3296 return rc;
3297}
3298
3299/**
3300 * @note Locks objects!
3301 */
3302HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3303 const com::Utf8Str &aName,
3304 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3305 ComPtr<IProgress> &aProgress)
3306{
3307 Utf8Str strFrontend(aName);
3308 /* "emergencystop" doesn't need the session, so skip the checks/interface
3309 * retrieval. This code doesn't quite fit in here, but introducing a
3310 * special API method would be even more effort, and would require explicit
3311 * support by every API client. It's better to hide the feature a bit. */
3312 if (strFrontend != "emergencystop")
3313 CheckComArgNotNull(aSession);
3314
3315 HRESULT rc = S_OK;
3316 if (strFrontend.isEmpty())
3317 {
3318 Bstr bstrFrontend;
3319 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3320 if (FAILED(rc))
3321 return rc;
3322 strFrontend = bstrFrontend;
3323 if (strFrontend.isEmpty())
3324 {
3325 ComPtr<ISystemProperties> systemProperties;
3326 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3327 if (FAILED(rc))
3328 return rc;
3329 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3330 if (FAILED(rc))
3331 return rc;
3332 strFrontend = bstrFrontend;
3333 }
3334 /* paranoia - emergencystop is not a valid default */
3335 if (strFrontend == "emergencystop")
3336 strFrontend = Utf8Str::Empty;
3337 }
3338 /* default frontend: Qt GUI */
3339 if (strFrontend.isEmpty())
3340 strFrontend = "GUI/Qt";
3341
3342 if (strFrontend != "emergencystop")
3343 {
3344 /* check the session state */
3345 SessionState_T state;
3346 rc = aSession->COMGETTER(State)(&state);
3347 if (FAILED(rc))
3348 return rc;
3349
3350 if (state != SessionState_Unlocked)
3351 return setError(VBOX_E_INVALID_OBJECT_STATE,
3352 tr("The given session is busy"));
3353
3354 /* get the IInternalSessionControl interface */
3355 ComPtr<IInternalSessionControl> control(aSession);
3356 ComAssertMsgRet(!control.isNull(),
3357 ("No IInternalSessionControl interface"),
3358 E_INVALIDARG);
3359
3360 /* get the teleporter enable state for the progress object init. */
3361 BOOL fTeleporterEnabled;
3362 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3363 if (FAILED(rc))
3364 return rc;
3365
3366 /* create a progress object */
3367 ComObjPtr<ProgressProxy> progress;
3368 progress.createObject();
3369 rc = progress->init(mParent,
3370 static_cast<IMachine*>(this),
3371 Bstr(tr("Starting VM")).raw(),
3372 TRUE /* aCancelable */,
3373 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3374 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3375 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3376 2 /* uFirstOperationWeight */,
3377 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3378
3379 if (SUCCEEDED(rc))
3380 {
3381 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3382 if (SUCCEEDED(rc))
3383 {
3384 aProgress = progress;
3385
3386 /* signal the client watcher thread */
3387 mParent->i_updateClientWatcher();
3388
3389 /* fire an event */
3390 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3391 }
3392 }
3393 }
3394 else
3395 {
3396 /* no progress object - either instant success or failure */
3397 aProgress = NULL;
3398
3399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3400
3401 if (mData->mSession.mState != SessionState_Locked)
3402 return setError(VBOX_E_INVALID_OBJECT_STATE,
3403 tr("The machine '%s' is not locked by a session"),
3404 mUserData->s.strName.c_str());
3405
3406 /* must have a VM process associated - do not kill normal API clients
3407 * with an open session */
3408 if (!Global::IsOnline(mData->mMachineState))
3409 return setError(VBOX_E_INVALID_OBJECT_STATE,
3410 tr("The machine '%s' does not have a VM process"),
3411 mUserData->s.strName.c_str());
3412
3413 /* forcibly terminate the VM process */
3414 if (mData->mSession.mPID != NIL_RTPROCESS)
3415 RTProcTerminate(mData->mSession.mPID);
3416
3417 /* signal the client watcher thread, as most likely the client has
3418 * been terminated */
3419 mParent->i_updateClientWatcher();
3420 }
3421
3422 return rc;
3423}
3424
3425HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3426{
3427 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3428 return setError(E_INVALIDARG,
3429 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3430 aPosition, SchemaDefs::MaxBootPosition);
3431
3432 if (aDevice == DeviceType_USB)
3433 return setError(E_NOTIMPL,
3434 tr("Booting from USB device is currently not supported"));
3435
3436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3437
3438 HRESULT rc = i_checkStateDependency(MutableStateDep);
3439 if (FAILED(rc)) return rc;
3440
3441 i_setModified(IsModified_MachineData);
3442 mHWData.backup();
3443 mHWData->mBootOrder[aPosition - 1] = aDevice;
3444
3445 return S_OK;
3446}
3447
3448HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3449{
3450 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3451 return setError(E_INVALIDARG,
3452 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3453 aPosition, SchemaDefs::MaxBootPosition);
3454
3455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3456
3457 *aDevice = mHWData->mBootOrder[aPosition - 1];
3458
3459 return S_OK;
3460}
3461
3462HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3463 LONG aControllerPort,
3464 LONG aDevice,
3465 DeviceType_T aType,
3466 const ComPtr<IMedium> &aMedium)
3467{
3468 IMedium *aM = aMedium;
3469 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3470 aName.c_str(), aControllerPort, aDevice, aType, aM));
3471
3472 // request the host lock first, since might be calling Host methods for getting host drives;
3473 // next, protect the media tree all the while we're in here, as well as our member variables
3474 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3475 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3476
3477 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3478 if (FAILED(rc)) return rc;
3479
3480 /// @todo NEWMEDIA implicit machine registration
3481 if (!mData->mRegistered)
3482 return setError(VBOX_E_INVALID_OBJECT_STATE,
3483 tr("Cannot attach storage devices to an unregistered machine"));
3484
3485 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3486
3487 /* Check for an existing controller. */
3488 ComObjPtr<StorageController> ctl;
3489 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3490 if (FAILED(rc)) return rc;
3491
3492 StorageControllerType_T ctrlType;
3493 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3494 if (FAILED(rc))
3495 return setError(E_FAIL,
3496 tr("Could not get type of controller '%s'"),
3497 aName.c_str());
3498
3499 bool fSilent = false;
3500 Utf8Str strReconfig;
3501
3502 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3503 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3504 if ( mData->mMachineState == MachineState_Paused
3505 && strReconfig == "1")
3506 fSilent = true;
3507
3508 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3509 bool fHotplug = false;
3510 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3511 fHotplug = true;
3512
3513 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3514 return setError(VBOX_E_INVALID_VM_STATE,
3515 tr("Controller '%s' does not support hotplugging"),
3516 aName.c_str());
3517
3518 // check that the port and device are not out of range
3519 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3520 if (FAILED(rc)) return rc;
3521
3522 /* check if the device slot is already busy */
3523 MediumAttachment *pAttachTemp;
3524 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3525 aName,
3526 aControllerPort,
3527 aDevice)))
3528 {
3529 Medium *pMedium = pAttachTemp->i_getMedium();
3530 if (pMedium)
3531 {
3532 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3533 return setError(VBOX_E_OBJECT_IN_USE,
3534 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3535 pMedium->i_getLocationFull().c_str(),
3536 aControllerPort,
3537 aDevice,
3538 aName.c_str());
3539 }
3540 else
3541 return setError(VBOX_E_OBJECT_IN_USE,
3542 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3543 aControllerPort, aDevice, aName.c_str());
3544 }
3545
3546 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3547 if (aMedium && medium.isNull())
3548 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3549
3550 AutoCaller mediumCaller(medium);
3551 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3552
3553 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3554
3555 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3556 && !medium.isNull()
3557 && ( medium->i_getType() != MediumType_Readonly
3558 || medium->i_getDeviceType() != DeviceType_DVD)
3559 )
3560 return setError(VBOX_E_OBJECT_IN_USE,
3561 tr("Medium '%s' is already attached to this virtual machine"),
3562 medium->i_getLocationFull().c_str());
3563
3564 if (!medium.isNull())
3565 {
3566 MediumType_T mtype = medium->i_getType();
3567 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3568 // For DVDs it's not written to the config file, so needs no global config
3569 // version bump. For floppies it's a new attribute "type", which is ignored
3570 // by older VirtualBox version, so needs no global config version bump either.
3571 // For hard disks this type is not accepted.
3572 if (mtype == MediumType_MultiAttach)
3573 {
3574 // This type is new with VirtualBox 4.0 and therefore requires settings
3575 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3576 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3577 // two reasons: The medium type is a property of the media registry tree, which
3578 // can reside in the global config file (for pre-4.0 media); we would therefore
3579 // possibly need to bump the global config version. We don't want to do that though
3580 // because that might make downgrading to pre-4.0 impossible.
3581 // As a result, we can only use these two new types if the medium is NOT in the
3582 // global registry:
3583 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3584 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3585 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3586 )
3587 return setError(VBOX_E_INVALID_OBJECT_STATE,
3588 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3589 "to machines that were created with VirtualBox 4.0 or later"),
3590 medium->i_getLocationFull().c_str());
3591 }
3592 }
3593
3594 bool fIndirect = false;
3595 if (!medium.isNull())
3596 fIndirect = medium->i_isReadOnly();
3597 bool associate = true;
3598
3599 do
3600 {
3601 if ( aType == DeviceType_HardDisk
3602 && mMediumAttachments.isBackedUp())
3603 {
3604 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3605
3606 /* check if the medium was attached to the VM before we started
3607 * changing attachments in which case the attachment just needs to
3608 * be restored */
3609 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3610 {
3611 AssertReturn(!fIndirect, E_FAIL);
3612
3613 /* see if it's the same bus/channel/device */
3614 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3615 {
3616 /* the simplest case: restore the whole attachment
3617 * and return, nothing else to do */
3618 mMediumAttachments->push_back(pAttachTemp);
3619
3620 /* Reattach the medium to the VM. */
3621 if (fHotplug || fSilent)
3622 {
3623 mediumLock.release();
3624 treeLock.release();
3625 alock.release();
3626
3627 MediumLockList *pMediumLockList(new MediumLockList());
3628
3629 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3630 medium /* pToLockWrite */,
3631 false /* fMediumLockWriteAll */,
3632 NULL,
3633 *pMediumLockList);
3634 alock.acquire();
3635 if (FAILED(rc))
3636 delete pMediumLockList;
3637 else
3638 {
3639 mData->mSession.mLockedMedia.Unlock();
3640 alock.release();
3641 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3642 mData->mSession.mLockedMedia.Lock();
3643 alock.acquire();
3644 }
3645 alock.release();
3646
3647 if (SUCCEEDED(rc))
3648 {
3649 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3650 /* Remove lock list in case of error. */
3651 if (FAILED(rc))
3652 {
3653 mData->mSession.mLockedMedia.Unlock();
3654 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3655 mData->mSession.mLockedMedia.Lock();
3656 }
3657 }
3658 }
3659
3660 return S_OK;
3661 }
3662
3663 /* bus/channel/device differ; we need a new attachment object,
3664 * but don't try to associate it again */
3665 associate = false;
3666 break;
3667 }
3668 }
3669
3670 /* go further only if the attachment is to be indirect */
3671 if (!fIndirect)
3672 break;
3673
3674 /* perform the so called smart attachment logic for indirect
3675 * attachments. Note that smart attachment is only applicable to base
3676 * hard disks. */
3677
3678 if (medium->i_getParent().isNull())
3679 {
3680 /* first, investigate the backup copy of the current hard disk
3681 * attachments to make it possible to re-attach existing diffs to
3682 * another device slot w/o losing their contents */
3683 if (mMediumAttachments.isBackedUp())
3684 {
3685 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3686
3687 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3688 uint32_t foundLevel = 0;
3689
3690 for (MediumAttachmentList::const_iterator
3691 it = oldAtts.begin();
3692 it != oldAtts.end();
3693 ++it)
3694 {
3695 uint32_t level = 0;
3696 MediumAttachment *pAttach = *it;
3697 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3698 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3699 if (pMedium.isNull())
3700 continue;
3701
3702 if (pMedium->i_getBase(&level) == medium)
3703 {
3704 /* skip the hard disk if its currently attached (we
3705 * cannot attach the same hard disk twice) */
3706 if (i_findAttachment(*mMediumAttachments.data(),
3707 pMedium))
3708 continue;
3709
3710 /* matched device, channel and bus (i.e. attached to the
3711 * same place) will win and immediately stop the search;
3712 * otherwise the attachment that has the youngest
3713 * descendant of medium will be used
3714 */
3715 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3716 {
3717 /* the simplest case: restore the whole attachment
3718 * and return, nothing else to do */
3719 mMediumAttachments->push_back(*it);
3720
3721 /* Reattach the medium to the VM. */
3722 if (fHotplug || fSilent)
3723 {
3724 mediumLock.release();
3725 treeLock.release();
3726 alock.release();
3727
3728 MediumLockList *pMediumLockList(new MediumLockList());
3729
3730 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3731 medium /* pToLockWrite */,
3732 false /* fMediumLockWriteAll */,
3733 NULL,
3734 *pMediumLockList);
3735 alock.acquire();
3736 if (FAILED(rc))
3737 delete pMediumLockList;
3738 else
3739 {
3740 mData->mSession.mLockedMedia.Unlock();
3741 alock.release();
3742 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3743 mData->mSession.mLockedMedia.Lock();
3744 alock.acquire();
3745 }
3746 alock.release();
3747
3748 if (SUCCEEDED(rc))
3749 {
3750 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3751 /* Remove lock list in case of error. */
3752 if (FAILED(rc))
3753 {
3754 mData->mSession.mLockedMedia.Unlock();
3755 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3756 mData->mSession.mLockedMedia.Lock();
3757 }
3758 }
3759 }
3760
3761 return S_OK;
3762 }
3763 else if ( foundIt == oldAtts.end()
3764 || level > foundLevel /* prefer younger */
3765 )
3766 {
3767 foundIt = it;
3768 foundLevel = level;
3769 }
3770 }
3771 }
3772
3773 if (foundIt != oldAtts.end())
3774 {
3775 /* use the previously attached hard disk */
3776 medium = (*foundIt)->i_getMedium();
3777 mediumCaller.attach(medium);
3778 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3779 mediumLock.attach(medium);
3780 /* not implicit, doesn't require association with this VM */
3781 fIndirect = false;
3782 associate = false;
3783 /* go right to the MediumAttachment creation */
3784 break;
3785 }
3786 }
3787
3788 /* must give up the medium lock and medium tree lock as below we
3789 * go over snapshots, which needs a lock with higher lock order. */
3790 mediumLock.release();
3791 treeLock.release();
3792
3793 /* then, search through snapshots for the best diff in the given
3794 * hard disk's chain to base the new diff on */
3795
3796 ComObjPtr<Medium> base;
3797 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3798 while (snap)
3799 {
3800 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3801
3802 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3803
3804 MediumAttachment *pAttachFound = NULL;
3805 uint32_t foundLevel = 0;
3806
3807 for (MediumAttachmentList::const_iterator
3808 it = snapAtts.begin();
3809 it != snapAtts.end();
3810 ++it)
3811 {
3812 MediumAttachment *pAttach = *it;
3813 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3814 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3815 if (pMedium.isNull())
3816 continue;
3817
3818 uint32_t level = 0;
3819 if (pMedium->i_getBase(&level) == medium)
3820 {
3821 /* matched device, channel and bus (i.e. attached to the
3822 * same place) will win and immediately stop the search;
3823 * otherwise the attachment that has the youngest
3824 * descendant of medium will be used
3825 */
3826 if ( pAttach->i_getDevice() == aDevice
3827 && pAttach->i_getPort() == aControllerPort
3828 && pAttach->i_getControllerName() == aName
3829 )
3830 {
3831 pAttachFound = pAttach;
3832 break;
3833 }
3834 else if ( !pAttachFound
3835 || level > foundLevel /* prefer younger */
3836 )
3837 {
3838 pAttachFound = pAttach;
3839 foundLevel = level;
3840 }
3841 }
3842 }
3843
3844 if (pAttachFound)
3845 {
3846 base = pAttachFound->i_getMedium();
3847 break;
3848 }
3849
3850 snap = snap->i_getParent();
3851 }
3852
3853 /* re-lock medium tree and the medium, as we need it below */
3854 treeLock.acquire();
3855 mediumLock.acquire();
3856
3857 /* found a suitable diff, use it as a base */
3858 if (!base.isNull())
3859 {
3860 medium = base;
3861 mediumCaller.attach(medium);
3862 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3863 mediumLock.attach(medium);
3864 }
3865 }
3866
3867 Utf8Str strFullSnapshotFolder;
3868 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3869
3870 ComObjPtr<Medium> diff;
3871 diff.createObject();
3872 // store this diff in the same registry as the parent
3873 Guid uuidRegistryParent;
3874 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3875 {
3876 // parent image has no registry: this can happen if we're attaching a new immutable
3877 // image that has not yet been attached (medium then points to the base and we're
3878 // creating the diff image for the immutable, and the parent is not yet registered);
3879 // put the parent in the machine registry then
3880 mediumLock.release();
3881 treeLock.release();
3882 alock.release();
3883 i_addMediumToRegistry(medium);
3884 alock.acquire();
3885 treeLock.acquire();
3886 mediumLock.acquire();
3887 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3888 }
3889 rc = diff->init(mParent,
3890 medium->i_getPreferredDiffFormat(),
3891 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3892 uuidRegistryParent,
3893 DeviceType_HardDisk);
3894 if (FAILED(rc)) return rc;
3895
3896 /* Apply the normal locking logic to the entire chain. */
3897 MediumLockList *pMediumLockList(new MediumLockList());
3898 mediumLock.release();
3899 treeLock.release();
3900 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3901 diff /* pToLockWrite */,
3902 false /* fMediumLockWriteAll */,
3903 medium,
3904 *pMediumLockList);
3905 treeLock.acquire();
3906 mediumLock.acquire();
3907 if (SUCCEEDED(rc))
3908 {
3909 mediumLock.release();
3910 treeLock.release();
3911 rc = pMediumLockList->Lock();
3912 treeLock.acquire();
3913 mediumLock.acquire();
3914 if (FAILED(rc))
3915 setError(rc,
3916 tr("Could not lock medium when creating diff '%s'"),
3917 diff->i_getLocationFull().c_str());
3918 else
3919 {
3920 /* will release the lock before the potentially lengthy
3921 * operation, so protect with the special state */
3922 MachineState_T oldState = mData->mMachineState;
3923 i_setMachineState(MachineState_SettingUp);
3924
3925 mediumLock.release();
3926 treeLock.release();
3927 alock.release();
3928
3929 rc = medium->i_createDiffStorage(diff,
3930 medium->i_getPreferredDiffVariant(),
3931 pMediumLockList,
3932 NULL /* aProgress */,
3933 true /* aWait */,
3934 false /* aNotify */);
3935
3936 alock.acquire();
3937 treeLock.acquire();
3938 mediumLock.acquire();
3939
3940 i_setMachineState(oldState);
3941 }
3942 }
3943
3944 /* Unlock the media and free the associated memory. */
3945 delete pMediumLockList;
3946
3947 if (FAILED(rc)) return rc;
3948
3949 /* use the created diff for the actual attachment */
3950 medium = diff;
3951 mediumCaller.attach(medium);
3952 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3953 mediumLock.attach(medium);
3954 }
3955 while (0);
3956
3957 ComObjPtr<MediumAttachment> attachment;
3958 attachment.createObject();
3959 rc = attachment->init(this,
3960 medium,
3961 aName,
3962 aControllerPort,
3963 aDevice,
3964 aType,
3965 fIndirect,
3966 false /* fPassthrough */,
3967 false /* fTempEject */,
3968 false /* fNonRotational */,
3969 false /* fDiscard */,
3970 fHotplug /* fHotPluggable */,
3971 Utf8Str::Empty);
3972 if (FAILED(rc)) return rc;
3973
3974 if (associate && !medium.isNull())
3975 {
3976 // as the last step, associate the medium to the VM
3977 rc = medium->i_addBackReference(mData->mUuid);
3978 // here we can fail because of Deleting, or being in process of creating a Diff
3979 if (FAILED(rc)) return rc;
3980
3981 mediumLock.release();
3982 treeLock.release();
3983 alock.release();
3984 i_addMediumToRegistry(medium);
3985 alock.acquire();
3986 treeLock.acquire();
3987 mediumLock.acquire();
3988 }
3989
3990 /* success: finally remember the attachment */
3991 i_setModified(IsModified_Storage);
3992 mMediumAttachments.backup();
3993 mMediumAttachments->push_back(attachment);
3994
3995 mediumLock.release();
3996 treeLock.release();
3997 alock.release();
3998
3999 if (fHotplug || fSilent)
4000 {
4001 if (!medium.isNull())
4002 {
4003 MediumLockList *pMediumLockList(new MediumLockList());
4004
4005 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4006 medium /* pToLockWrite */,
4007 false /* fMediumLockWriteAll */,
4008 NULL,
4009 *pMediumLockList);
4010 alock.acquire();
4011 if (FAILED(rc))
4012 delete pMediumLockList;
4013 else
4014 {
4015 mData->mSession.mLockedMedia.Unlock();
4016 alock.release();
4017 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4018 mData->mSession.mLockedMedia.Lock();
4019 alock.acquire();
4020 }
4021 alock.release();
4022 }
4023
4024 if (SUCCEEDED(rc))
4025 {
4026 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4027 /* Remove lock list in case of error. */
4028 if (FAILED(rc))
4029 {
4030 mData->mSession.mLockedMedia.Unlock();
4031 mData->mSession.mLockedMedia.Remove(attachment);
4032 mData->mSession.mLockedMedia.Lock();
4033 }
4034 }
4035 }
4036
4037 /* Save modified registries, but skip this machine as it's the caller's
4038 * job to save its settings like all other settings changes. */
4039 mParent->i_unmarkRegistryModified(i_getId());
4040 mParent->i_saveModifiedRegistries();
4041
4042 if (SUCCEEDED(rc))
4043 {
4044 if (fIndirect && medium != aM)
4045 mParent->i_onMediumConfigChanged(medium);
4046 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4047 }
4048
4049 return rc;
4050}
4051
4052HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4053 LONG aDevice)
4054{
4055 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4056 aName.c_str(), aControllerPort, aDevice));
4057
4058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4059
4060 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4061 if (FAILED(rc)) return rc;
4062
4063 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4064
4065 /* Check for an existing controller. */
4066 ComObjPtr<StorageController> ctl;
4067 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4068 if (FAILED(rc)) return rc;
4069
4070 StorageControllerType_T ctrlType;
4071 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4072 if (FAILED(rc))
4073 return setError(E_FAIL,
4074 tr("Could not get type of controller '%s'"),
4075 aName.c_str());
4076
4077 bool fSilent = false;
4078 Utf8Str strReconfig;
4079
4080 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4081 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4082 if ( mData->mMachineState == MachineState_Paused
4083 && strReconfig == "1")
4084 fSilent = true;
4085
4086 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4087 bool fHotplug = false;
4088 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4089 fHotplug = true;
4090
4091 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4092 return setError(VBOX_E_INVALID_VM_STATE,
4093 tr("Controller '%s' does not support hotplugging"),
4094 aName.c_str());
4095
4096 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4097 aName,
4098 aControllerPort,
4099 aDevice);
4100 if (!pAttach)
4101 return setError(VBOX_E_OBJECT_NOT_FOUND,
4102 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4103 aDevice, aControllerPort, aName.c_str());
4104
4105 if (fHotplug && !pAttach->i_getHotPluggable())
4106 return setError(VBOX_E_NOT_SUPPORTED,
4107 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4108 aDevice, aControllerPort, aName.c_str());
4109
4110 /*
4111 * The VM has to detach the device before we delete any implicit diffs.
4112 * If this fails we can roll back without loosing data.
4113 */
4114 if (fHotplug || fSilent)
4115 {
4116 alock.release();
4117 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4118 alock.acquire();
4119 }
4120 if (FAILED(rc)) return rc;
4121
4122 /* If we are here everything went well and we can delete the implicit now. */
4123 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4124
4125 alock.release();
4126
4127 /* Save modified registries, but skip this machine as it's the caller's
4128 * job to save its settings like all other settings changes. */
4129 mParent->i_unmarkRegistryModified(i_getId());
4130 mParent->i_saveModifiedRegistries();
4131
4132 if (SUCCEEDED(rc))
4133 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4134
4135 return rc;
4136}
4137
4138HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4139 LONG aDevice, BOOL aPassthrough)
4140{
4141 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4142 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4143
4144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4145
4146 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4147 if (FAILED(rc)) return rc;
4148
4149 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4150
4151 /* Check for an existing controller. */
4152 ComObjPtr<StorageController> ctl;
4153 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4154 if (FAILED(rc)) return rc;
4155
4156 StorageControllerType_T ctrlType;
4157 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4158 if (FAILED(rc))
4159 return setError(E_FAIL,
4160 tr("Could not get type of controller '%s'"),
4161 aName.c_str());
4162
4163 bool fSilent = false;
4164 Utf8Str strReconfig;
4165
4166 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4167 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4168 if ( mData->mMachineState == MachineState_Paused
4169 && strReconfig == "1")
4170 fSilent = true;
4171
4172 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4173 bool fHotplug = false;
4174 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4175 fHotplug = true;
4176
4177 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4178 return setError(VBOX_E_INVALID_VM_STATE,
4179 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4180 aName.c_str());
4181
4182 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4183 aName,
4184 aControllerPort,
4185 aDevice);
4186 if (!pAttach)
4187 return setError(VBOX_E_OBJECT_NOT_FOUND,
4188 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4189 aDevice, aControllerPort, aName.c_str());
4190
4191
4192 i_setModified(IsModified_Storage);
4193 mMediumAttachments.backup();
4194
4195 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4196
4197 if (pAttach->i_getType() != DeviceType_DVD)
4198 return setError(E_INVALIDARG,
4199 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4200 aDevice, aControllerPort, aName.c_str());
4201
4202 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4203
4204 pAttach->i_updatePassthrough(!!aPassthrough);
4205
4206 attLock.release();
4207 alock.release();
4208 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4209 if (SUCCEEDED(rc) && fValueChanged)
4210 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4211
4212 return rc;
4213}
4214
4215HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4216 LONG aDevice, BOOL aTemporaryEject)
4217{
4218
4219 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4220 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4221
4222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4223
4224 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4225 if (FAILED(rc)) return rc;
4226
4227 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4228 aName,
4229 aControllerPort,
4230 aDevice);
4231 if (!pAttach)
4232 return setError(VBOX_E_OBJECT_NOT_FOUND,
4233 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4234 aDevice, aControllerPort, aName.c_str());
4235
4236
4237 i_setModified(IsModified_Storage);
4238 mMediumAttachments.backup();
4239
4240 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4241
4242 if (pAttach->i_getType() != DeviceType_DVD)
4243 return setError(E_INVALIDARG,
4244 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4245 aDevice, aControllerPort, aName.c_str());
4246 pAttach->i_updateTempEject(!!aTemporaryEject);
4247
4248 return S_OK;
4249}
4250
4251HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4252 LONG aDevice, BOOL aNonRotational)
4253{
4254
4255 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4256 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4257
4258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4259
4260 HRESULT rc = i_checkStateDependency(MutableStateDep);
4261 if (FAILED(rc)) return rc;
4262
4263 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4264
4265 if (Global::IsOnlineOrTransient(mData->mMachineState))
4266 return setError(VBOX_E_INVALID_VM_STATE,
4267 tr("Invalid machine state: %s"),
4268 Global::stringifyMachineState(mData->mMachineState));
4269
4270 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4271 aName,
4272 aControllerPort,
4273 aDevice);
4274 if (!pAttach)
4275 return setError(VBOX_E_OBJECT_NOT_FOUND,
4276 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4277 aDevice, aControllerPort, aName.c_str());
4278
4279
4280 i_setModified(IsModified_Storage);
4281 mMediumAttachments.backup();
4282
4283 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4284
4285 if (pAttach->i_getType() != DeviceType_HardDisk)
4286 return setError(E_INVALIDARG,
4287 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"),
4288 aDevice, aControllerPort, aName.c_str());
4289 pAttach->i_updateNonRotational(!!aNonRotational);
4290
4291 return S_OK;
4292}
4293
4294HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4295 LONG aDevice, BOOL aDiscard)
4296{
4297
4298 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4299 aName.c_str(), aControllerPort, aDevice, aDiscard));
4300
4301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4302
4303 HRESULT rc = i_checkStateDependency(MutableStateDep);
4304 if (FAILED(rc)) return rc;
4305
4306 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4307
4308 if (Global::IsOnlineOrTransient(mData->mMachineState))
4309 return setError(VBOX_E_INVALID_VM_STATE,
4310 tr("Invalid machine state: %s"),
4311 Global::stringifyMachineState(mData->mMachineState));
4312
4313 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4314 aName,
4315 aControllerPort,
4316 aDevice);
4317 if (!pAttach)
4318 return setError(VBOX_E_OBJECT_NOT_FOUND,
4319 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4320 aDevice, aControllerPort, aName.c_str());
4321
4322
4323 i_setModified(IsModified_Storage);
4324 mMediumAttachments.backup();
4325
4326 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4327
4328 if (pAttach->i_getType() != DeviceType_HardDisk)
4329 return setError(E_INVALIDARG,
4330 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"),
4331 aDevice, aControllerPort, aName.c_str());
4332 pAttach->i_updateDiscard(!!aDiscard);
4333
4334 return S_OK;
4335}
4336
4337HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4338 LONG aDevice, BOOL aHotPluggable)
4339{
4340 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4341 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4342
4343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4344
4345 HRESULT rc = i_checkStateDependency(MutableStateDep);
4346 if (FAILED(rc)) return rc;
4347
4348 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4349
4350 if (Global::IsOnlineOrTransient(mData->mMachineState))
4351 return setError(VBOX_E_INVALID_VM_STATE,
4352 tr("Invalid machine state: %s"),
4353 Global::stringifyMachineState(mData->mMachineState));
4354
4355 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4356 aName,
4357 aControllerPort,
4358 aDevice);
4359 if (!pAttach)
4360 return setError(VBOX_E_OBJECT_NOT_FOUND,
4361 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4362 aDevice, aControllerPort, aName.c_str());
4363
4364 /* Check for an existing controller. */
4365 ComObjPtr<StorageController> ctl;
4366 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4367 if (FAILED(rc)) return rc;
4368
4369 StorageControllerType_T ctrlType;
4370 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4371 if (FAILED(rc))
4372 return setError(E_FAIL,
4373 tr("Could not get type of controller '%s'"),
4374 aName.c_str());
4375
4376 if (!i_isControllerHotplugCapable(ctrlType))
4377 return setError(VBOX_E_NOT_SUPPORTED,
4378 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4379 aName.c_str());
4380
4381 i_setModified(IsModified_Storage);
4382 mMediumAttachments.backup();
4383
4384 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4385
4386 if (pAttach->i_getType() == DeviceType_Floppy)
4387 return setError(E_INVALIDARG,
4388 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"),
4389 aDevice, aControllerPort, aName.c_str());
4390 pAttach->i_updateHotPluggable(!!aHotPluggable);
4391
4392 return S_OK;
4393}
4394
4395HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4396 LONG aDevice)
4397{
4398 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4399 aName.c_str(), aControllerPort, aDevice));
4400
4401 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4402}
4403
4404HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4405 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4406{
4407 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4408 aName.c_str(), aControllerPort, aDevice));
4409
4410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4411
4412 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4413 if (FAILED(rc)) return rc;
4414
4415 if (Global::IsOnlineOrTransient(mData->mMachineState))
4416 return setError(VBOX_E_INVALID_VM_STATE,
4417 tr("Invalid machine state: %s"),
4418 Global::stringifyMachineState(mData->mMachineState));
4419
4420 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4421 aName,
4422 aControllerPort,
4423 aDevice);
4424 if (!pAttach)
4425 return setError(VBOX_E_OBJECT_NOT_FOUND,
4426 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4427 aDevice, aControllerPort, aName.c_str());
4428
4429
4430 i_setModified(IsModified_Storage);
4431 mMediumAttachments.backup();
4432
4433 IBandwidthGroup *iB = aBandwidthGroup;
4434 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4435 if (aBandwidthGroup && group.isNull())
4436 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4437
4438 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4439
4440 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4441 if (strBandwidthGroupOld.isNotEmpty())
4442 {
4443 /* Get the bandwidth group object and release it - this must not fail. */
4444 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4445 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4446 Assert(SUCCEEDED(rc));
4447
4448 pBandwidthGroupOld->i_release();
4449 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4450 }
4451
4452 if (!group.isNull())
4453 {
4454 group->i_reference();
4455 pAttach->i_updateBandwidthGroup(group->i_getName());
4456 }
4457
4458 return S_OK;
4459}
4460
4461HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4462 LONG aControllerPort,
4463 LONG aDevice,
4464 DeviceType_T aType)
4465{
4466 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4467 aName.c_str(), aControllerPort, aDevice, aType));
4468
4469 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4470}
4471
4472
4473HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4474 LONG aControllerPort,
4475 LONG aDevice,
4476 BOOL aForce)
4477{
4478 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4479 aName.c_str(), aControllerPort, aForce));
4480
4481 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4482}
4483
4484HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4485 LONG aControllerPort,
4486 LONG aDevice,
4487 const ComPtr<IMedium> &aMedium,
4488 BOOL aForce)
4489{
4490 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4491 aName.c_str(), aControllerPort, aDevice, aForce));
4492
4493 // request the host lock first, since might be calling Host methods for getting host drives;
4494 // next, protect the media tree all the while we're in here, as well as our member variables
4495 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4496 this->lockHandle(),
4497 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4498
4499 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4500 aName,
4501 aControllerPort,
4502 aDevice);
4503 if (pAttach.isNull())
4504 return setError(VBOX_E_OBJECT_NOT_FOUND,
4505 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4506 aDevice, aControllerPort, aName.c_str());
4507
4508 /* Remember previously mounted medium. The medium before taking the
4509 * backup is not necessarily the same thing. */
4510 ComObjPtr<Medium> oldmedium;
4511 oldmedium = pAttach->i_getMedium();
4512
4513 IMedium *iM = aMedium;
4514 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4515 if (aMedium && pMedium.isNull())
4516 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4517
4518 AutoCaller mediumCaller(pMedium);
4519 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4520
4521 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4522 if (pMedium)
4523 {
4524 DeviceType_T mediumType = pAttach->i_getType();
4525 switch (mediumType)
4526 {
4527 case DeviceType_DVD:
4528 case DeviceType_Floppy:
4529 break;
4530
4531 default:
4532 return setError(VBOX_E_INVALID_OBJECT_STATE,
4533 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4534 aControllerPort,
4535 aDevice,
4536 aName.c_str());
4537 }
4538 }
4539
4540 i_setModified(IsModified_Storage);
4541 mMediumAttachments.backup();
4542
4543 {
4544 // The backup operation makes the pAttach reference point to the
4545 // old settings. Re-get the correct reference.
4546 pAttach = i_findAttachment(*mMediumAttachments.data(),
4547 aName,
4548 aControllerPort,
4549 aDevice);
4550 if (!oldmedium.isNull())
4551 oldmedium->i_removeBackReference(mData->mUuid);
4552 if (!pMedium.isNull())
4553 {
4554 pMedium->i_addBackReference(mData->mUuid);
4555
4556 mediumLock.release();
4557 multiLock.release();
4558 i_addMediumToRegistry(pMedium);
4559 multiLock.acquire();
4560 mediumLock.acquire();
4561 }
4562
4563 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4564 pAttach->i_updateMedium(pMedium);
4565 }
4566
4567 i_setModified(IsModified_Storage);
4568
4569 mediumLock.release();
4570 multiLock.release();
4571 HRESULT rc = i_onMediumChange(pAttach, aForce);
4572 multiLock.acquire();
4573 mediumLock.acquire();
4574
4575 /* On error roll back this change only. */
4576 if (FAILED(rc))
4577 {
4578 if (!pMedium.isNull())
4579 pMedium->i_removeBackReference(mData->mUuid);
4580 pAttach = i_findAttachment(*mMediumAttachments.data(),
4581 aName,
4582 aControllerPort,
4583 aDevice);
4584 /* If the attachment is gone in the meantime, bail out. */
4585 if (pAttach.isNull())
4586 return rc;
4587 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4588 if (!oldmedium.isNull())
4589 oldmedium->i_addBackReference(mData->mUuid);
4590 pAttach->i_updateMedium(oldmedium);
4591 }
4592
4593 mediumLock.release();
4594 multiLock.release();
4595
4596 /* Save modified registries, but skip this machine as it's the caller's
4597 * job to save its settings like all other settings changes. */
4598 mParent->i_unmarkRegistryModified(i_getId());
4599 mParent->i_saveModifiedRegistries();
4600
4601 return rc;
4602}
4603HRESULT Machine::getMedium(const com::Utf8Str &aName,
4604 LONG aControllerPort,
4605 LONG aDevice,
4606 ComPtr<IMedium> &aMedium)
4607{
4608 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4609 aName.c_str(), aControllerPort, aDevice));
4610
4611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 aMedium = NULL;
4614
4615 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4616 aName,
4617 aControllerPort,
4618 aDevice);
4619 if (pAttach.isNull())
4620 return setError(VBOX_E_OBJECT_NOT_FOUND,
4621 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4622 aDevice, aControllerPort, aName.c_str());
4623
4624 aMedium = pAttach->i_getMedium();
4625
4626 return S_OK;
4627}
4628
4629HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4630{
4631 if (aSlot < RT_ELEMENTS(mSerialPorts))
4632 {
4633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4634 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4635 return S_OK;
4636 }
4637 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4638}
4639
4640HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4641{
4642 if (aSlot < RT_ELEMENTS(mParallelPorts))
4643 {
4644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4645 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4646 return S_OK;
4647 }
4648 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4649}
4650
4651
4652HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4653{
4654 /* Do not assert if slot is out of range, just return the advertised
4655 status. testdriver/vbox.py triggers this in logVmInfo. */
4656 if (aSlot >= mNetworkAdapters.size())
4657 return setError(E_INVALIDARG,
4658 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4659 aSlot, mNetworkAdapters.size());
4660
4661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4662
4663 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4664
4665 return S_OK;
4666}
4667
4668HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4669{
4670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4671
4672 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4673 size_t i = 0;
4674 for (settings::StringsMap::const_iterator
4675 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4676 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4677 ++it, ++i)
4678 aKeys[i] = it->first;
4679
4680 return S_OK;
4681}
4682
4683 /**
4684 * @note Locks this object for reading.
4685 */
4686HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4687 com::Utf8Str &aValue)
4688{
4689 /* start with nothing found */
4690 aValue = "";
4691
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4695 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4696 // found:
4697 aValue = it->second; // source is a Utf8Str
4698
4699 /* return the result to caller (may be empty) */
4700 return S_OK;
4701}
4702
4703 /**
4704 * @note Locks mParent for writing + this object for writing.
4705 */
4706HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4707{
4708 /* Because control characters in aKey have caused problems in the settings
4709 * they are rejected unless the key should be deleted. */
4710 if (!aValue.isEmpty())
4711 {
4712 for (size_t i = 0; i < aKey.length(); ++i)
4713 {
4714 char ch = aKey[i];
4715 if (RTLocCIsCntrl(ch))
4716 return E_INVALIDARG;
4717 }
4718 }
4719
4720 Utf8Str strOldValue; // empty
4721
4722 // locking note: we only hold the read lock briefly to look up the old value,
4723 // then release it and call the onExtraCanChange callbacks. There is a small
4724 // chance of a race insofar as the callback might be called twice if two callers
4725 // change the same key at the same time, but that's a much better solution
4726 // than the deadlock we had here before. The actual changing of the extradata
4727 // is then performed under the write lock and race-free.
4728
4729 // look up the old value first; if nothing has changed then we need not do anything
4730 {
4731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4732
4733 // For snapshots don't even think about allowing changes, extradata
4734 // is global for a machine, so there is nothing snapshot specific.
4735 if (i_isSnapshotMachine())
4736 return setError(VBOX_E_INVALID_VM_STATE,
4737 tr("Cannot set extradata for a snapshot"));
4738
4739 // check if the right IMachine instance is used
4740 if (mData->mRegistered && !i_isSessionMachine())
4741 return setError(VBOX_E_INVALID_VM_STATE,
4742 tr("Cannot set extradata for an immutable machine"));
4743
4744 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4745 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4746 strOldValue = it->second;
4747 }
4748
4749 bool fChanged;
4750 if ((fChanged = (strOldValue != aValue)))
4751 {
4752 // ask for permission from all listeners outside the locks;
4753 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4754 // lock to copy the list of callbacks to invoke
4755 Bstr bstrError;
4756 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4757 {
4758 const char *sep = bstrError.isEmpty() ? "" : ": ";
4759 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4760 return setError(E_ACCESSDENIED,
4761 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4762 aKey.c_str(),
4763 aValue.c_str(),
4764 sep,
4765 bstrError.raw());
4766 }
4767
4768 // data is changing and change not vetoed: then write it out under the lock
4769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4770
4771 if (aValue.isEmpty())
4772 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4773 else
4774 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4775 // creates a new key if needed
4776
4777 bool fNeedsGlobalSaveSettings = false;
4778 // This saving of settings is tricky: there is no "old state" for the
4779 // extradata items at all (unlike all other settings), so the old/new
4780 // settings comparison would give a wrong result!
4781 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4782
4783 if (fNeedsGlobalSaveSettings)
4784 {
4785 // save the global settings; for that we should hold only the VirtualBox lock
4786 alock.release();
4787 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4788 mParent->i_saveSettings();
4789 }
4790 }
4791
4792 // fire notification outside the lock
4793 if (fChanged)
4794 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4795
4796 return S_OK;
4797}
4798
4799HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4800{
4801 aProgress = NULL;
4802 NOREF(aSettingsFilePath);
4803 ReturnComNotImplemented();
4804}
4805
4806HRESULT Machine::saveSettings()
4807{
4808 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4809
4810 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4811 if (FAILED(rc)) return rc;
4812
4813 /* the settings file path may never be null */
4814 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4815
4816 /* save all VM data excluding snapshots */
4817 bool fNeedsGlobalSaveSettings = false;
4818 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4819 mlock.release();
4820
4821 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4822 {
4823 // save the global settings; for that we should hold only the VirtualBox lock
4824 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4825 rc = mParent->i_saveSettings();
4826 }
4827
4828 return rc;
4829}
4830
4831
4832HRESULT Machine::discardSettings()
4833{
4834 /*
4835 * We need to take the machine list lock here as well as the machine one
4836 * or we'll get into trouble should any media stuff require rolling back.
4837 *
4838 * Details:
4839 *
4840 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4841 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4842 * 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]
4843 * 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
4844 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4845 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4846 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4847 * 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
4848 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4849 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4850 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4851 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4852 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4853 * 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]
4854 * 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] (*)
4855 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4856 * 0:005> k
4857 * # Child-SP RetAddr Call Site
4858 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4859 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4860 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4861 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4862 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4863 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4864 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4865 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4866 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4867 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4868 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4869 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4870 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4871 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4872 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4873 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4874 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4875 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4876 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4877 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4878 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4879 *
4880 */
4881 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4883
4884 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4885 if (FAILED(rc)) return rc;
4886
4887 /*
4888 * during this rollback, the session will be notified if data has
4889 * been actually changed
4890 */
4891 i_rollback(true /* aNotify */);
4892
4893 return S_OK;
4894}
4895
4896/** @note Locks objects! */
4897HRESULT Machine::unregister(AutoCaller &autoCaller,
4898 CleanupMode_T aCleanupMode,
4899 std::vector<ComPtr<IMedium> > &aMedia)
4900{
4901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4902
4903 Guid id(i_getId());
4904
4905 if (mData->mSession.mState != SessionState_Unlocked)
4906 return setError(VBOX_E_INVALID_OBJECT_STATE,
4907 tr("Cannot unregister the machine '%s' while it is locked"),
4908 mUserData->s.strName.c_str());
4909
4910 // wait for state dependents to drop to zero
4911 i_ensureNoStateDependencies();
4912
4913 if (!mData->mAccessible)
4914 {
4915 // inaccessible machines can only be unregistered; uninitialize ourselves
4916 // here because currently there may be no unregistered that are inaccessible
4917 // (this state combination is not supported). Note releasing the caller and
4918 // leaving the lock before calling uninit()
4919 alock.release();
4920 autoCaller.release();
4921
4922 uninit();
4923
4924 mParent->i_unregisterMachine(this, id);
4925 // calls VirtualBox::i_saveSettings()
4926
4927 return S_OK;
4928 }
4929
4930 HRESULT rc = S_OK;
4931 mData->llFilesToDelete.clear();
4932
4933 if (!mSSData->strStateFilePath.isEmpty())
4934 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4935
4936 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4937 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4938 mData->llFilesToDelete.push_back(strNVRAMFile);
4939
4940 // This list collects the medium objects from all medium attachments
4941 // which we will detach from the machine and its snapshots, in a specific
4942 // order which allows for closing all media without getting "media in use"
4943 // errors, simply by going through the list from the front to the back:
4944 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4945 // and must be closed before the parent media from the snapshots, or closing the parents
4946 // will fail because they still have children);
4947 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4948 // the root ("first") snapshot of the machine.
4949 MediaList llMedia;
4950
4951 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4952 && mMediumAttachments->size()
4953 )
4954 {
4955 // we have media attachments: detach them all and add the Medium objects to our list
4956 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4957 }
4958
4959 if (mData->mFirstSnapshot)
4960 {
4961 // add the media from the medium attachments of the snapshots to llMedia
4962 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4963 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4964 // into the children first
4965
4966 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4967 MachineState_T oldState = mData->mMachineState;
4968 mData->mMachineState = MachineState_DeletingSnapshot;
4969
4970 // make a copy of the first snapshot reference so the refcount does not
4971 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4972 // (would hang due to the AutoCaller voodoo)
4973 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4974
4975 // GO!
4976 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4977
4978 mData->mMachineState = oldState;
4979 }
4980
4981 if (FAILED(rc))
4982 {
4983 i_rollbackMedia();
4984 return rc;
4985 }
4986
4987 // commit all the media changes made above
4988 i_commitMedia();
4989
4990 mData->mRegistered = false;
4991
4992 // machine lock no longer needed
4993 alock.release();
4994
4995 /* Make sure that the settings of the current VM are not saved, because
4996 * they are rather crippled at this point to meet the cleanup expectations
4997 * and there's no point destroying the VM config on disk just because. */
4998 mParent->i_unmarkRegistryModified(id);
4999
5000 // return media to caller
5001 aMedia.resize(llMedia.size());
5002 size_t i = 0;
5003 for (MediaList::const_iterator
5004 it = llMedia.begin();
5005 it != llMedia.end();
5006 ++it, ++i)
5007 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5008
5009 mParent->i_unregisterMachine(this, id);
5010 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5011
5012 return S_OK;
5013}
5014
5015/**
5016 * Task record for deleting a machine config.
5017 */
5018class Machine::DeleteConfigTask
5019 : public Machine::Task
5020{
5021public:
5022 DeleteConfigTask(Machine *m,
5023 Progress *p,
5024 const Utf8Str &t,
5025 const RTCList<ComPtr<IMedium> > &llMediums,
5026 const StringsList &llFilesToDelete)
5027 : Task(m, p, t),
5028 m_llMediums(llMediums),
5029 m_llFilesToDelete(llFilesToDelete)
5030 {}
5031
5032private:
5033 void handler()
5034 {
5035 try
5036 {
5037 m_pMachine->i_deleteConfigHandler(*this);
5038 }
5039 catch (...)
5040 {
5041 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5042 }
5043 }
5044
5045 RTCList<ComPtr<IMedium> > m_llMediums;
5046 StringsList m_llFilesToDelete;
5047
5048 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5049};
5050
5051/**
5052 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5053 * SessionMachine::taskHandler().
5054 *
5055 * @note Locks this object for writing.
5056 *
5057 * @param task
5058 * @return
5059 */
5060void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5061{
5062 LogFlowThisFuncEnter();
5063
5064 AutoCaller autoCaller(this);
5065 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5066 if (FAILED(autoCaller.rc()))
5067 {
5068 /* we might have been uninitialized because the session was accidentally
5069 * closed by the client, so don't assert */
5070 HRESULT rc = setError(E_FAIL,
5071 tr("The session has been accidentally closed"));
5072 task.m_pProgress->i_notifyComplete(rc);
5073 LogFlowThisFuncLeave();
5074 return;
5075 }
5076
5077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5078
5079 HRESULT rc = S_OK;
5080
5081 try
5082 {
5083 ULONG uLogHistoryCount = 3;
5084 ComPtr<ISystemProperties> systemProperties;
5085 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5086 if (FAILED(rc)) throw rc;
5087
5088 if (!systemProperties.isNull())
5089 {
5090 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5091 if (FAILED(rc)) throw rc;
5092 }
5093
5094 MachineState_T oldState = mData->mMachineState;
5095 i_setMachineState(MachineState_SettingUp);
5096 alock.release();
5097 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5098 {
5099 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5100 {
5101 AutoCaller mac(pMedium);
5102 if (FAILED(mac.rc())) throw mac.rc();
5103 Utf8Str strLocation = pMedium->i_getLocationFull();
5104 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5105 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5106 if (FAILED(rc)) throw rc;
5107 }
5108 if (pMedium->i_isMediumFormatFile())
5109 {
5110 ComPtr<IProgress> pProgress2;
5111 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5112 if (FAILED(rc)) throw rc;
5113 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5114 if (FAILED(rc)) throw rc;
5115 }
5116
5117 /* Close the medium, deliberately without checking the return
5118 * code, and without leaving any trace in the error info, as
5119 * a failure here is a very minor issue, which shouldn't happen
5120 * as above we even managed to delete the medium. */
5121 {
5122 ErrorInfoKeeper eik;
5123 pMedium->Close();
5124 }
5125 }
5126 i_setMachineState(oldState);
5127 alock.acquire();
5128
5129 // delete the files pushed on the task list by Machine::Delete()
5130 // (this includes saved states of the machine and snapshots and
5131 // medium storage files from the IMedium list passed in, and the
5132 // machine XML file)
5133 for (StringsList::const_iterator
5134 it = task.m_llFilesToDelete.begin();
5135 it != task.m_llFilesToDelete.end();
5136 ++it)
5137 {
5138 const Utf8Str &strFile = *it;
5139 LogFunc(("Deleting file %s\n", strFile.c_str()));
5140 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5141 if (FAILED(rc)) throw rc;
5142
5143 int vrc = RTFileDelete(strFile.c_str());
5144 if (RT_FAILURE(vrc))
5145 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5146 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5147 }
5148
5149 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5150 if (FAILED(rc)) throw rc;
5151
5152 /* delete the settings only when the file actually exists */
5153 if (mData->pMachineConfigFile->fileExists())
5154 {
5155 /* Delete any backup or uncommitted XML files. Ignore failures.
5156 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5157 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5158 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5159 RTFileDelete(otherXml.c_str());
5160 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5161 RTFileDelete(otherXml.c_str());
5162
5163 /* delete the Logs folder, nothing important should be left
5164 * there (we don't check for errors because the user might have
5165 * some private files there that we don't want to delete) */
5166 Utf8Str logFolder;
5167 getLogFolder(logFolder);
5168 Assert(logFolder.length());
5169 if (RTDirExists(logFolder.c_str()))
5170 {
5171 /* Delete all VBox.log[.N] files from the Logs folder
5172 * (this must be in sync with the rotation logic in
5173 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5174 * files that may have been created by the GUI. */
5175 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5176 RTFileDelete(log.c_str());
5177 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5178 RTFileDelete(log.c_str());
5179 for (ULONG i = uLogHistoryCount; i > 0; i--)
5180 {
5181 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5182 RTFileDelete(log.c_str());
5183 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5184 RTFileDelete(log.c_str());
5185 }
5186 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5187 RTFileDelete(log.c_str());
5188#if defined(RT_OS_WINDOWS)
5189 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5190 RTFileDelete(log.c_str());
5191 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5192 RTFileDelete(log.c_str());
5193#endif
5194
5195 RTDirRemove(logFolder.c_str());
5196 }
5197
5198 /* delete the Snapshots folder, nothing important should be left
5199 * there (we don't check for errors because the user might have
5200 * some private files there that we don't want to delete) */
5201 Utf8Str strFullSnapshotFolder;
5202 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5203 Assert(!strFullSnapshotFolder.isEmpty());
5204 if (RTDirExists(strFullSnapshotFolder.c_str()))
5205 RTDirRemove(strFullSnapshotFolder.c_str());
5206
5207 // delete the directory that contains the settings file, but only
5208 // if it matches the VM name
5209 Utf8Str settingsDir;
5210 if (i_isInOwnDir(&settingsDir))
5211 RTDirRemove(settingsDir.c_str());
5212 }
5213
5214 alock.release();
5215
5216 mParent->i_saveModifiedRegistries();
5217 }
5218 catch (HRESULT aRC) { rc = aRC; }
5219
5220 task.m_pProgress->i_notifyComplete(rc);
5221
5222 LogFlowThisFuncLeave();
5223}
5224
5225HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5226{
5227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5228
5229 HRESULT rc = i_checkStateDependency(MutableStateDep);
5230 if (FAILED(rc)) return rc;
5231
5232 if (mData->mRegistered)
5233 return setError(VBOX_E_INVALID_VM_STATE,
5234 tr("Cannot delete settings of a registered machine"));
5235
5236 // collect files to delete
5237 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5238 // machine config file
5239 if (mData->pMachineConfigFile->fileExists())
5240 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5241 // backup of machine config file
5242 Utf8Str strTmp(mData->m_strConfigFileFull);
5243 strTmp.append("-prev");
5244 if (RTFileExists(strTmp.c_str()))
5245 llFilesToDelete.push_back(strTmp);
5246
5247 RTCList<ComPtr<IMedium> > llMediums;
5248 for (size_t i = 0; i < aMedia.size(); ++i)
5249 {
5250 IMedium *pIMedium(aMedia[i]);
5251 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5252 if (pMedium.isNull())
5253 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5254 SafeArray<BSTR> ids;
5255 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5256 if (FAILED(rc)) return rc;
5257 /* At this point the medium should not have any back references
5258 * anymore. If it has it is attached to another VM and *must* not
5259 * deleted. */
5260 if (ids.size() < 1)
5261 llMediums.append(pMedium);
5262 }
5263
5264 ComObjPtr<Progress> pProgress;
5265 pProgress.createObject();
5266 rc = pProgress->init(i_getVirtualBox(),
5267 static_cast<IMachine*>(this) /* aInitiator */,
5268 tr("Deleting files"),
5269 true /* fCancellable */,
5270 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5271 tr("Collecting file inventory"));
5272 if (FAILED(rc))
5273 return rc;
5274
5275 /* create and start the task on a separate thread (note that it will not
5276 * start working until we release alock) */
5277 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5278 rc = pTask->createThread();
5279 pTask = NULL;
5280 if (FAILED(rc))
5281 return rc;
5282
5283 pProgress.queryInterfaceTo(aProgress.asOutParam());
5284
5285 LogFlowFuncLeave();
5286
5287 return S_OK;
5288}
5289
5290HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5291{
5292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5293
5294 ComObjPtr<Snapshot> pSnapshot;
5295 HRESULT rc;
5296
5297 if (aNameOrId.isEmpty())
5298 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5299 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5300 else
5301 {
5302 Guid uuid(aNameOrId);
5303 if (uuid.isValid())
5304 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5305 else
5306 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5307 }
5308 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5309
5310 return rc;
5311}
5312
5313HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5314 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5315{
5316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5317
5318 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5319 if (FAILED(rc)) return rc;
5320
5321 ComObjPtr<SharedFolder> sharedFolder;
5322 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5323 if (SUCCEEDED(rc))
5324 return setError(VBOX_E_OBJECT_IN_USE,
5325 tr("Shared folder named '%s' already exists"),
5326 aName.c_str());
5327
5328 sharedFolder.createObject();
5329 rc = sharedFolder->init(i_getMachine(),
5330 aName,
5331 aHostPath,
5332 !!aWritable,
5333 !!aAutomount,
5334 aAutoMountPoint,
5335 true /* fFailOnError */);
5336 if (FAILED(rc)) return rc;
5337
5338 i_setModified(IsModified_SharedFolders);
5339 mHWData.backup();
5340 mHWData->mSharedFolders.push_back(sharedFolder);
5341
5342 /* inform the direct session if any */
5343 alock.release();
5344 i_onSharedFolderChange();
5345
5346 return S_OK;
5347}
5348
5349HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5350{
5351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5352
5353 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5354 if (FAILED(rc)) return rc;
5355
5356 ComObjPtr<SharedFolder> sharedFolder;
5357 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5358 if (FAILED(rc)) return rc;
5359
5360 i_setModified(IsModified_SharedFolders);
5361 mHWData.backup();
5362 mHWData->mSharedFolders.remove(sharedFolder);
5363
5364 /* inform the direct session if any */
5365 alock.release();
5366 i_onSharedFolderChange();
5367
5368 return S_OK;
5369}
5370
5371HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5372{
5373 /* start with No */
5374 *aCanShow = FALSE;
5375
5376 ComPtr<IInternalSessionControl> directControl;
5377 {
5378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 if (mData->mSession.mState != SessionState_Locked)
5381 return setError(VBOX_E_INVALID_VM_STATE,
5382 tr("Machine is not locked for session (session state: %s)"),
5383 Global::stringifySessionState(mData->mSession.mState));
5384
5385 if (mData->mSession.mLockType == LockType_VM)
5386 directControl = mData->mSession.mDirectControl;
5387 }
5388
5389 /* ignore calls made after #OnSessionEnd() is called */
5390 if (!directControl)
5391 return S_OK;
5392
5393 LONG64 dummy;
5394 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5395}
5396
5397HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5398{
5399 ComPtr<IInternalSessionControl> directControl;
5400 {
5401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5402
5403 if (mData->mSession.mState != SessionState_Locked)
5404 return setError(E_FAIL,
5405 tr("Machine is not locked for session (session state: %s)"),
5406 Global::stringifySessionState(mData->mSession.mState));
5407
5408 if (mData->mSession.mLockType == LockType_VM)
5409 directControl = mData->mSession.mDirectControl;
5410 }
5411
5412 /* ignore calls made after #OnSessionEnd() is called */
5413 if (!directControl)
5414 return S_OK;
5415
5416 BOOL dummy;
5417 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5418}
5419
5420#ifdef VBOX_WITH_GUEST_PROPS
5421/**
5422 * Look up a guest property in VBoxSVC's internal structures.
5423 */
5424HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5425 com::Utf8Str &aValue,
5426 LONG64 *aTimestamp,
5427 com::Utf8Str &aFlags) const
5428{
5429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5430
5431 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5432 if (it != mHWData->mGuestProperties.end())
5433 {
5434 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5435 aValue = it->second.strValue;
5436 *aTimestamp = it->second.mTimestamp;
5437 GuestPropWriteFlags(it->second.mFlags, szFlags);
5438 aFlags = Utf8Str(szFlags);
5439 }
5440
5441 return S_OK;
5442}
5443
5444/**
5445 * Query the VM that a guest property belongs to for the property.
5446 * @returns E_ACCESSDENIED if the VM process is not available or not
5447 * currently handling queries and the lookup should then be done in
5448 * VBoxSVC.
5449 */
5450HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5451 com::Utf8Str &aValue,
5452 LONG64 *aTimestamp,
5453 com::Utf8Str &aFlags) const
5454{
5455 HRESULT rc = S_OK;
5456 Bstr bstrValue;
5457 Bstr bstrFlags;
5458
5459 ComPtr<IInternalSessionControl> directControl;
5460 {
5461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5462 if (mData->mSession.mLockType == LockType_VM)
5463 directControl = mData->mSession.mDirectControl;
5464 }
5465
5466 /* ignore calls made after #OnSessionEnd() is called */
5467 if (!directControl)
5468 rc = E_ACCESSDENIED;
5469 else
5470 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5471 0 /* accessMode */,
5472 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5473
5474 aValue = bstrValue;
5475 aFlags = bstrFlags;
5476
5477 return rc;
5478}
5479#endif // VBOX_WITH_GUEST_PROPS
5480
5481HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5482 com::Utf8Str &aValue,
5483 LONG64 *aTimestamp,
5484 com::Utf8Str &aFlags)
5485{
5486#ifndef VBOX_WITH_GUEST_PROPS
5487 ReturnComNotImplemented();
5488#else // VBOX_WITH_GUEST_PROPS
5489
5490 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5491
5492 if (rc == E_ACCESSDENIED)
5493 /* The VM is not running or the service is not (yet) accessible */
5494 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5495 return rc;
5496#endif // VBOX_WITH_GUEST_PROPS
5497}
5498
5499HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5500{
5501 LONG64 dummyTimestamp;
5502 com::Utf8Str dummyFlags;
5503 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5504 return rc;
5505
5506}
5507HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5508{
5509 com::Utf8Str dummyFlags;
5510 com::Utf8Str dummyValue;
5511 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5512 return rc;
5513}
5514
5515#ifdef VBOX_WITH_GUEST_PROPS
5516/**
5517 * Set a guest property in VBoxSVC's internal structures.
5518 */
5519HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5520 const com::Utf8Str &aFlags, bool fDelete)
5521{
5522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5523 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5524 if (FAILED(rc)) return rc;
5525
5526 try
5527 {
5528 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5529 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5530 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5531
5532 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5533 if (it == mHWData->mGuestProperties.end())
5534 {
5535 if (!fDelete)
5536 {
5537 i_setModified(IsModified_MachineData);
5538 mHWData.backupEx();
5539
5540 RTTIMESPEC time;
5541 HWData::GuestProperty prop;
5542 prop.strValue = Bstr(aValue).raw();
5543 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5544 prop.mFlags = fFlags;
5545 mHWData->mGuestProperties[aName] = prop;
5546 }
5547 }
5548 else
5549 {
5550 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5551 {
5552 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5553 }
5554 else
5555 {
5556 i_setModified(IsModified_MachineData);
5557 mHWData.backupEx();
5558
5559 /* The backupEx() operation invalidates our iterator,
5560 * so get a new one. */
5561 it = mHWData->mGuestProperties.find(aName);
5562 Assert(it != mHWData->mGuestProperties.end());
5563
5564 if (!fDelete)
5565 {
5566 RTTIMESPEC time;
5567 it->second.strValue = aValue;
5568 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5569 it->second.mFlags = fFlags;
5570 }
5571 else
5572 mHWData->mGuestProperties.erase(it);
5573 }
5574 }
5575
5576 if (SUCCEEDED(rc))
5577 {
5578 alock.release();
5579
5580 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5581 }
5582 }
5583 catch (std::bad_alloc &)
5584 {
5585 rc = E_OUTOFMEMORY;
5586 }
5587
5588 return rc;
5589}
5590
5591/**
5592 * Set a property on the VM that that property belongs to.
5593 * @returns E_ACCESSDENIED if the VM process is not available or not
5594 * currently handling queries and the setting should then be done in
5595 * VBoxSVC.
5596 */
5597HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5598 const com::Utf8Str &aFlags, bool fDelete)
5599{
5600 HRESULT rc;
5601
5602 try
5603 {
5604 ComPtr<IInternalSessionControl> directControl;
5605 {
5606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5607 if (mData->mSession.mLockType == LockType_VM)
5608 directControl = mData->mSession.mDirectControl;
5609 }
5610
5611 Bstr dummy1; /* will not be changed (setter) */
5612 Bstr dummy2; /* will not be changed (setter) */
5613 LONG64 dummy64;
5614 if (!directControl)
5615 rc = E_ACCESSDENIED;
5616 else
5617 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5618 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5619 fDelete ? 2 : 1 /* accessMode */,
5620 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5621 }
5622 catch (std::bad_alloc &)
5623 {
5624 rc = E_OUTOFMEMORY;
5625 }
5626
5627 return rc;
5628}
5629#endif // VBOX_WITH_GUEST_PROPS
5630
5631HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5632 const com::Utf8Str &aFlags)
5633{
5634#ifndef VBOX_WITH_GUEST_PROPS
5635 ReturnComNotImplemented();
5636#else // VBOX_WITH_GUEST_PROPS
5637 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5638 if (rc == E_ACCESSDENIED)
5639 /* The VM is not running or the service is not (yet) accessible */
5640 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5641 return rc;
5642#endif // VBOX_WITH_GUEST_PROPS
5643}
5644
5645HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5646{
5647 return setGuestProperty(aProperty, aValue, "");
5648}
5649
5650HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5651{
5652#ifndef VBOX_WITH_GUEST_PROPS
5653 ReturnComNotImplemented();
5654#else // VBOX_WITH_GUEST_PROPS
5655 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5656 if (rc == E_ACCESSDENIED)
5657 /* The VM is not running or the service is not (yet) accessible */
5658 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5659 return rc;
5660#endif // VBOX_WITH_GUEST_PROPS
5661}
5662
5663#ifdef VBOX_WITH_GUEST_PROPS
5664/**
5665 * Enumerate the guest properties in VBoxSVC's internal structures.
5666 */
5667HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5668 std::vector<com::Utf8Str> &aNames,
5669 std::vector<com::Utf8Str> &aValues,
5670 std::vector<LONG64> &aTimestamps,
5671 std::vector<com::Utf8Str> &aFlags)
5672{
5673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5674 Utf8Str strPatterns(aPatterns);
5675
5676 /*
5677 * Look for matching patterns and build up a list.
5678 */
5679 HWData::GuestPropertyMap propMap;
5680 for (HWData::GuestPropertyMap::const_iterator
5681 it = mHWData->mGuestProperties.begin();
5682 it != mHWData->mGuestProperties.end();
5683 ++it)
5684 {
5685 if ( strPatterns.isEmpty()
5686 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5687 RTSTR_MAX,
5688 it->first.c_str(),
5689 RTSTR_MAX,
5690 NULL)
5691 )
5692 propMap.insert(*it);
5693 }
5694
5695 alock.release();
5696
5697 /*
5698 * And build up the arrays for returning the property information.
5699 */
5700 size_t cEntries = propMap.size();
5701
5702 aNames.resize(cEntries);
5703 aValues.resize(cEntries);
5704 aTimestamps.resize(cEntries);
5705 aFlags.resize(cEntries);
5706
5707 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5708 size_t i = 0;
5709 for (HWData::GuestPropertyMap::const_iterator
5710 it = propMap.begin();
5711 it != propMap.end();
5712 ++it, ++i)
5713 {
5714 aNames[i] = it->first;
5715 aValues[i] = it->second.strValue;
5716 aTimestamps[i] = it->second.mTimestamp;
5717 GuestPropWriteFlags(it->second.mFlags, szFlags);
5718 aFlags[i] = Utf8Str(szFlags);
5719 }
5720
5721 return S_OK;
5722}
5723
5724/**
5725 * Enumerate the properties managed by a VM.
5726 * @returns E_ACCESSDENIED if the VM process is not available or not
5727 * currently handling queries and the setting should then be done in
5728 * VBoxSVC.
5729 */
5730HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5731 std::vector<com::Utf8Str> &aNames,
5732 std::vector<com::Utf8Str> &aValues,
5733 std::vector<LONG64> &aTimestamps,
5734 std::vector<com::Utf8Str> &aFlags)
5735{
5736 HRESULT rc;
5737 ComPtr<IInternalSessionControl> directControl;
5738 {
5739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5740 if (mData->mSession.mLockType == LockType_VM)
5741 directControl = mData->mSession.mDirectControl;
5742 }
5743
5744 com::SafeArray<BSTR> bNames;
5745 com::SafeArray<BSTR> bValues;
5746 com::SafeArray<LONG64> bTimestamps;
5747 com::SafeArray<BSTR> bFlags;
5748
5749 if (!directControl)
5750 rc = E_ACCESSDENIED;
5751 else
5752 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5753 ComSafeArrayAsOutParam(bNames),
5754 ComSafeArrayAsOutParam(bValues),
5755 ComSafeArrayAsOutParam(bTimestamps),
5756 ComSafeArrayAsOutParam(bFlags));
5757 size_t i;
5758 aNames.resize(bNames.size());
5759 for (i = 0; i < bNames.size(); ++i)
5760 aNames[i] = Utf8Str(bNames[i]);
5761 aValues.resize(bValues.size());
5762 for (i = 0; i < bValues.size(); ++i)
5763 aValues[i] = Utf8Str(bValues[i]);
5764 aTimestamps.resize(bTimestamps.size());
5765 for (i = 0; i < bTimestamps.size(); ++i)
5766 aTimestamps[i] = bTimestamps[i];
5767 aFlags.resize(bFlags.size());
5768 for (i = 0; i < bFlags.size(); ++i)
5769 aFlags[i] = Utf8Str(bFlags[i]);
5770
5771 return rc;
5772}
5773#endif // VBOX_WITH_GUEST_PROPS
5774HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5775 std::vector<com::Utf8Str> &aNames,
5776 std::vector<com::Utf8Str> &aValues,
5777 std::vector<LONG64> &aTimestamps,
5778 std::vector<com::Utf8Str> &aFlags)
5779{
5780#ifndef VBOX_WITH_GUEST_PROPS
5781 ReturnComNotImplemented();
5782#else // VBOX_WITH_GUEST_PROPS
5783
5784 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5785
5786 if (rc == E_ACCESSDENIED)
5787 /* The VM is not running or the service is not (yet) accessible */
5788 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5789 return rc;
5790#endif // VBOX_WITH_GUEST_PROPS
5791}
5792
5793HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5794 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5795{
5796 MediumAttachmentList atts;
5797
5798 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5799 if (FAILED(rc)) return rc;
5800
5801 aMediumAttachments.resize(atts.size());
5802 size_t i = 0;
5803 for (MediumAttachmentList::const_iterator
5804 it = atts.begin();
5805 it != atts.end();
5806 ++it, ++i)
5807 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5808
5809 return S_OK;
5810}
5811
5812HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5813 LONG aControllerPort,
5814 LONG aDevice,
5815 ComPtr<IMediumAttachment> &aAttachment)
5816{
5817 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5818 aName.c_str(), aControllerPort, aDevice));
5819
5820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5821
5822 aAttachment = NULL;
5823
5824 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5825 aName,
5826 aControllerPort,
5827 aDevice);
5828 if (pAttach.isNull())
5829 return setError(VBOX_E_OBJECT_NOT_FOUND,
5830 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5831 aDevice, aControllerPort, aName.c_str());
5832
5833 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5834
5835 return S_OK;
5836}
5837
5838
5839HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5840 StorageBus_T aConnectionType,
5841 ComPtr<IStorageController> &aController)
5842{
5843 if ( (aConnectionType <= StorageBus_Null)
5844 || (aConnectionType > StorageBus_VirtioSCSI))
5845 return setError(E_INVALIDARG,
5846 tr("Invalid connection type: %d"),
5847 aConnectionType);
5848
5849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5850
5851 HRESULT rc = i_checkStateDependency(MutableStateDep);
5852 if (FAILED(rc)) return rc;
5853
5854 /* try to find one with the name first. */
5855 ComObjPtr<StorageController> ctrl;
5856
5857 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5858 if (SUCCEEDED(rc))
5859 return setError(VBOX_E_OBJECT_IN_USE,
5860 tr("Storage controller named '%s' already exists"),
5861 aName.c_str());
5862
5863 ctrl.createObject();
5864
5865 /* get a new instance number for the storage controller */
5866 ULONG ulInstance = 0;
5867 bool fBootable = true;
5868 for (StorageControllerList::const_iterator
5869 it = mStorageControllers->begin();
5870 it != mStorageControllers->end();
5871 ++it)
5872 {
5873 if ((*it)->i_getStorageBus() == aConnectionType)
5874 {
5875 ULONG ulCurInst = (*it)->i_getInstance();
5876
5877 if (ulCurInst >= ulInstance)
5878 ulInstance = ulCurInst + 1;
5879
5880 /* Only one controller of each type can be marked as bootable. */
5881 if ((*it)->i_getBootable())
5882 fBootable = false;
5883 }
5884 }
5885
5886 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5887 if (FAILED(rc)) return rc;
5888
5889 i_setModified(IsModified_Storage);
5890 mStorageControllers.backup();
5891 mStorageControllers->push_back(ctrl);
5892
5893 ctrl.queryInterfaceTo(aController.asOutParam());
5894
5895 /* inform the direct session if any */
5896 alock.release();
5897 i_onStorageControllerChange(i_getId(), aName);
5898
5899 return S_OK;
5900}
5901
5902HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5903 ComPtr<IStorageController> &aStorageController)
5904{
5905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5906
5907 ComObjPtr<StorageController> ctrl;
5908
5909 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5910 if (SUCCEEDED(rc))
5911 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5912
5913 return rc;
5914}
5915
5916HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5917 ULONG aInstance,
5918 ComPtr<IStorageController> &aStorageController)
5919{
5920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5921
5922 for (StorageControllerList::const_iterator
5923 it = mStorageControllers->begin();
5924 it != mStorageControllers->end();
5925 ++it)
5926 {
5927 if ( (*it)->i_getStorageBus() == aConnectionType
5928 && (*it)->i_getInstance() == aInstance)
5929 {
5930 (*it).queryInterfaceTo(aStorageController.asOutParam());
5931 return S_OK;
5932 }
5933 }
5934
5935 return setError(VBOX_E_OBJECT_NOT_FOUND,
5936 tr("Could not find a storage controller with instance number '%lu'"),
5937 aInstance);
5938}
5939
5940HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5941{
5942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5943
5944 HRESULT rc = i_checkStateDependency(MutableStateDep);
5945 if (FAILED(rc)) return rc;
5946
5947 ComObjPtr<StorageController> ctrl;
5948
5949 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5950 if (SUCCEEDED(rc))
5951 {
5952 /* Ensure that only one controller of each type is marked as bootable. */
5953 if (aBootable == TRUE)
5954 {
5955 for (StorageControllerList::const_iterator
5956 it = mStorageControllers->begin();
5957 it != mStorageControllers->end();
5958 ++it)
5959 {
5960 ComObjPtr<StorageController> aCtrl = (*it);
5961
5962 if ( (aCtrl->i_getName() != aName)
5963 && aCtrl->i_getBootable() == TRUE
5964 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5965 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5966 {
5967 aCtrl->i_setBootable(FALSE);
5968 break;
5969 }
5970 }
5971 }
5972
5973 if (SUCCEEDED(rc))
5974 {
5975 ctrl->i_setBootable(aBootable);
5976 i_setModified(IsModified_Storage);
5977 }
5978 }
5979
5980 if (SUCCEEDED(rc))
5981 {
5982 /* inform the direct session if any */
5983 alock.release();
5984 i_onStorageControllerChange(i_getId(), aName);
5985 }
5986
5987 return rc;
5988}
5989
5990HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5991{
5992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5993
5994 HRESULT rc = i_checkStateDependency(MutableStateDep);
5995 if (FAILED(rc)) return rc;
5996
5997 ComObjPtr<StorageController> ctrl;
5998 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5999 if (FAILED(rc)) return rc;
6000
6001 MediumAttachmentList llDetachedAttachments;
6002 {
6003 /* find all attached devices to the appropriate storage controller and detach them all */
6004 // make a temporary list because detachDevice invalidates iterators into
6005 // mMediumAttachments
6006 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6007
6008 for (MediumAttachmentList::const_iterator
6009 it = llAttachments2.begin();
6010 it != llAttachments2.end();
6011 ++it)
6012 {
6013 MediumAttachment *pAttachTemp = *it;
6014
6015 AutoCaller localAutoCaller(pAttachTemp);
6016 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6017
6018 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6019
6020 if (pAttachTemp->i_getControllerName() == aName)
6021 {
6022 llDetachedAttachments.push_back(pAttachTemp);
6023 rc = i_detachDevice(pAttachTemp, alock, NULL);
6024 if (FAILED(rc)) return rc;
6025 }
6026 }
6027 }
6028
6029 /* send event about detached devices before removing parent controller */
6030 for (MediumAttachmentList::const_iterator
6031 it = llDetachedAttachments.begin();
6032 it != llDetachedAttachments.end();
6033 ++it)
6034 {
6035 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6036 }
6037
6038 /* We can remove it now. */
6039 i_setModified(IsModified_Storage);
6040 mStorageControllers.backup();
6041
6042 ctrl->i_unshare();
6043
6044 mStorageControllers->remove(ctrl);
6045
6046 /* inform the direct session if any */
6047 alock.release();
6048 i_onStorageControllerChange(i_getId(), aName);
6049
6050 return S_OK;
6051}
6052
6053HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6054 ComPtr<IUSBController> &aController)
6055{
6056 if ( (aType <= USBControllerType_Null)
6057 || (aType >= USBControllerType_Last))
6058 return setError(E_INVALIDARG,
6059 tr("Invalid USB controller type: %d"),
6060 aType);
6061
6062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6063
6064 HRESULT rc = i_checkStateDependency(MutableStateDep);
6065 if (FAILED(rc)) return rc;
6066
6067 /* try to find one with the same type first. */
6068 ComObjPtr<USBController> ctrl;
6069
6070 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6071 if (SUCCEEDED(rc))
6072 return setError(VBOX_E_OBJECT_IN_USE,
6073 tr("USB controller named '%s' already exists"),
6074 aName.c_str());
6075
6076 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6077 ULONG maxInstances;
6078 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6079 if (FAILED(rc))
6080 return rc;
6081
6082 ULONG cInstances = i_getUSBControllerCountByType(aType);
6083 if (cInstances >= maxInstances)
6084 return setError(E_INVALIDARG,
6085 tr("Too many USB controllers of this type"));
6086
6087 ctrl.createObject();
6088
6089 rc = ctrl->init(this, aName, aType);
6090 if (FAILED(rc)) return rc;
6091
6092 i_setModified(IsModified_USB);
6093 mUSBControllers.backup();
6094 mUSBControllers->push_back(ctrl);
6095
6096 ctrl.queryInterfaceTo(aController.asOutParam());
6097
6098 /* inform the direct session if any */
6099 alock.release();
6100 i_onUSBControllerChange();
6101
6102 return S_OK;
6103}
6104
6105HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6106{
6107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6108
6109 ComObjPtr<USBController> ctrl;
6110
6111 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6112 if (SUCCEEDED(rc))
6113 ctrl.queryInterfaceTo(aController.asOutParam());
6114
6115 return rc;
6116}
6117
6118HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6119 ULONG *aControllers)
6120{
6121 if ( (aType <= USBControllerType_Null)
6122 || (aType >= USBControllerType_Last))
6123 return setError(E_INVALIDARG,
6124 tr("Invalid USB controller type: %d"),
6125 aType);
6126
6127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6128
6129 ComObjPtr<USBController> ctrl;
6130
6131 *aControllers = i_getUSBControllerCountByType(aType);
6132
6133 return S_OK;
6134}
6135
6136HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6137{
6138
6139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6140
6141 HRESULT rc = i_checkStateDependency(MutableStateDep);
6142 if (FAILED(rc)) return rc;
6143
6144 ComObjPtr<USBController> ctrl;
6145 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6146 if (FAILED(rc)) return rc;
6147
6148 i_setModified(IsModified_USB);
6149 mUSBControllers.backup();
6150
6151 ctrl->i_unshare();
6152
6153 mUSBControllers->remove(ctrl);
6154
6155 /* inform the direct session if any */
6156 alock.release();
6157 i_onUSBControllerChange();
6158
6159 return S_OK;
6160}
6161
6162HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6163 ULONG *aOriginX,
6164 ULONG *aOriginY,
6165 ULONG *aWidth,
6166 ULONG *aHeight,
6167 BOOL *aEnabled)
6168{
6169 uint32_t u32OriginX= 0;
6170 uint32_t u32OriginY= 0;
6171 uint32_t u32Width = 0;
6172 uint32_t u32Height = 0;
6173 uint16_t u16Flags = 0;
6174
6175 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6176 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6177 if (RT_FAILURE(vrc))
6178 {
6179#ifdef RT_OS_WINDOWS
6180 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6181 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6182 * So just assign fEnable to TRUE again.
6183 * The right fix would be to change GUI API wrappers to make sure that parameters
6184 * are changed only if API succeeds.
6185 */
6186 *aEnabled = TRUE;
6187#endif
6188 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6189 tr("Saved guest size is not available (%Rrc)"),
6190 vrc);
6191 }
6192
6193 *aOriginX = u32OriginX;
6194 *aOriginY = u32OriginY;
6195 *aWidth = u32Width;
6196 *aHeight = u32Height;
6197 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6198
6199 return S_OK;
6200}
6201
6202HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6203 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6204{
6205 if (aScreenId != 0)
6206 return E_NOTIMPL;
6207
6208 if ( aBitmapFormat != BitmapFormat_BGR0
6209 && aBitmapFormat != BitmapFormat_BGRA
6210 && aBitmapFormat != BitmapFormat_RGBA
6211 && aBitmapFormat != BitmapFormat_PNG)
6212 return setError(E_NOTIMPL,
6213 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6214
6215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6216
6217 uint8_t *pu8Data = NULL;
6218 uint32_t cbData = 0;
6219 uint32_t u32Width = 0;
6220 uint32_t u32Height = 0;
6221
6222 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6223
6224 if (RT_FAILURE(vrc))
6225 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6226 tr("Saved thumbnail data is not available (%Rrc)"),
6227 vrc);
6228
6229 HRESULT hr = S_OK;
6230
6231 *aWidth = u32Width;
6232 *aHeight = u32Height;
6233
6234 if (cbData > 0)
6235 {
6236 /* Convert pixels to the format expected by the API caller. */
6237 if (aBitmapFormat == BitmapFormat_BGR0)
6238 {
6239 /* [0] B, [1] G, [2] R, [3] 0. */
6240 aData.resize(cbData);
6241 memcpy(&aData.front(), pu8Data, cbData);
6242 }
6243 else if (aBitmapFormat == BitmapFormat_BGRA)
6244 {
6245 /* [0] B, [1] G, [2] R, [3] A. */
6246 aData.resize(cbData);
6247 for (uint32_t i = 0; i < cbData; i += 4)
6248 {
6249 aData[i] = pu8Data[i];
6250 aData[i + 1] = pu8Data[i + 1];
6251 aData[i + 2] = pu8Data[i + 2];
6252 aData[i + 3] = 0xff;
6253 }
6254 }
6255 else if (aBitmapFormat == BitmapFormat_RGBA)
6256 {
6257 /* [0] R, [1] G, [2] B, [3] A. */
6258 aData.resize(cbData);
6259 for (uint32_t i = 0; i < cbData; i += 4)
6260 {
6261 aData[i] = pu8Data[i + 2];
6262 aData[i + 1] = pu8Data[i + 1];
6263 aData[i + 2] = pu8Data[i];
6264 aData[i + 3] = 0xff;
6265 }
6266 }
6267 else if (aBitmapFormat == BitmapFormat_PNG)
6268 {
6269 uint8_t *pu8PNG = NULL;
6270 uint32_t cbPNG = 0;
6271 uint32_t cxPNG = 0;
6272 uint32_t cyPNG = 0;
6273
6274 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6275
6276 if (RT_SUCCESS(vrc))
6277 {
6278 aData.resize(cbPNG);
6279 if (cbPNG)
6280 memcpy(&aData.front(), pu8PNG, cbPNG);
6281 }
6282 else
6283 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6284 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6285 vrc);
6286
6287 RTMemFree(pu8PNG);
6288 }
6289 }
6290
6291 freeSavedDisplayScreenshot(pu8Data);
6292
6293 return hr;
6294}
6295
6296HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6297 ULONG *aWidth,
6298 ULONG *aHeight,
6299 std::vector<BitmapFormat_T> &aBitmapFormats)
6300{
6301 if (aScreenId != 0)
6302 return E_NOTIMPL;
6303
6304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6305
6306 uint8_t *pu8Data = NULL;
6307 uint32_t cbData = 0;
6308 uint32_t u32Width = 0;
6309 uint32_t u32Height = 0;
6310
6311 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6312
6313 if (RT_FAILURE(vrc))
6314 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6315 tr("Saved screenshot data is not available (%Rrc)"),
6316 vrc);
6317
6318 *aWidth = u32Width;
6319 *aHeight = u32Height;
6320 aBitmapFormats.resize(1);
6321 aBitmapFormats[0] = BitmapFormat_PNG;
6322
6323 freeSavedDisplayScreenshot(pu8Data);
6324
6325 return S_OK;
6326}
6327
6328HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6329 BitmapFormat_T aBitmapFormat,
6330 ULONG *aWidth,
6331 ULONG *aHeight,
6332 std::vector<BYTE> &aData)
6333{
6334 if (aScreenId != 0)
6335 return E_NOTIMPL;
6336
6337 if (aBitmapFormat != BitmapFormat_PNG)
6338 return E_NOTIMPL;
6339
6340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6341
6342 uint8_t *pu8Data = NULL;
6343 uint32_t cbData = 0;
6344 uint32_t u32Width = 0;
6345 uint32_t u32Height = 0;
6346
6347 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6348
6349 if (RT_FAILURE(vrc))
6350 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6351 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6352 vrc);
6353
6354 *aWidth = u32Width;
6355 *aHeight = u32Height;
6356
6357 aData.resize(cbData);
6358 if (cbData)
6359 memcpy(&aData.front(), pu8Data, cbData);
6360
6361 freeSavedDisplayScreenshot(pu8Data);
6362
6363 return S_OK;
6364}
6365
6366HRESULT Machine::hotPlugCPU(ULONG aCpu)
6367{
6368 HRESULT rc = S_OK;
6369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6370
6371 if (!mHWData->mCPUHotPlugEnabled)
6372 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6373
6374 if (aCpu >= mHWData->mCPUCount)
6375 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6376
6377 if (mHWData->mCPUAttached[aCpu])
6378 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6379
6380 alock.release();
6381 rc = i_onCPUChange(aCpu, false);
6382 alock.acquire();
6383 if (FAILED(rc)) return rc;
6384
6385 i_setModified(IsModified_MachineData);
6386 mHWData.backup();
6387 mHWData->mCPUAttached[aCpu] = true;
6388
6389 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6390 if (Global::IsOnline(mData->mMachineState))
6391 i_saveSettings(NULL);
6392
6393 return S_OK;
6394}
6395
6396HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6397{
6398 HRESULT rc = S_OK;
6399
6400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6401
6402 if (!mHWData->mCPUHotPlugEnabled)
6403 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6404
6405 if (aCpu >= SchemaDefs::MaxCPUCount)
6406 return setError(E_INVALIDARG,
6407 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6408 SchemaDefs::MaxCPUCount);
6409
6410 if (!mHWData->mCPUAttached[aCpu])
6411 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6412
6413 /* CPU 0 can't be detached */
6414 if (aCpu == 0)
6415 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6416
6417 alock.release();
6418 rc = i_onCPUChange(aCpu, true);
6419 alock.acquire();
6420 if (FAILED(rc)) return rc;
6421
6422 i_setModified(IsModified_MachineData);
6423 mHWData.backup();
6424 mHWData->mCPUAttached[aCpu] = false;
6425
6426 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6427 if (Global::IsOnline(mData->mMachineState))
6428 i_saveSettings(NULL);
6429
6430 return S_OK;
6431}
6432
6433HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6434{
6435 *aAttached = false;
6436
6437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6438
6439 /* If hotplug is enabled the CPU is always enabled. */
6440 if (!mHWData->mCPUHotPlugEnabled)
6441 {
6442 if (aCpu < mHWData->mCPUCount)
6443 *aAttached = true;
6444 }
6445 else
6446 {
6447 if (aCpu < SchemaDefs::MaxCPUCount)
6448 *aAttached = mHWData->mCPUAttached[aCpu];
6449 }
6450
6451 return S_OK;
6452}
6453
6454HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6455{
6456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6457
6458 Utf8Str log = i_getLogFilename(aIdx);
6459 if (!RTFileExists(log.c_str()))
6460 log.setNull();
6461 aFilename = log;
6462
6463 return S_OK;
6464}
6465
6466HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6467{
6468 if (aSize < 0)
6469 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6470
6471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6472
6473 HRESULT rc = S_OK;
6474 Utf8Str log = i_getLogFilename(aIdx);
6475
6476 /* do not unnecessarily hold the lock while doing something which does
6477 * not need the lock and potentially takes a long time. */
6478 alock.release();
6479
6480 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6481 * keeps the SOAP reply size under 1M for the webservice (we're using
6482 * base64 encoded strings for binary data for years now, avoiding the
6483 * expansion of each byte array element to approx. 25 bytes of XML. */
6484 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6485 aData.resize(cbData);
6486
6487 RTFILE LogFile;
6488 int vrc = RTFileOpen(&LogFile, log.c_str(),
6489 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6490 if (RT_SUCCESS(vrc))
6491 {
6492 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6493 if (RT_SUCCESS(vrc))
6494 aData.resize(cbData);
6495 else
6496 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6497 tr("Could not read log file '%s' (%Rrc)"),
6498 log.c_str(), vrc);
6499 RTFileClose(LogFile);
6500 }
6501 else
6502 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6503 tr("Could not open log file '%s' (%Rrc)"),
6504 log.c_str(), vrc);
6505
6506 if (FAILED(rc))
6507 aData.resize(0);
6508
6509 return rc;
6510}
6511
6512
6513/**
6514 * Currently this method doesn't attach device to the running VM,
6515 * just makes sure it's plugged on next VM start.
6516 */
6517HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6518{
6519 // lock scope
6520 {
6521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6522
6523 HRESULT rc = i_checkStateDependency(MutableStateDep);
6524 if (FAILED(rc)) return rc;
6525
6526 ChipsetType_T aChipset = ChipsetType_PIIX3;
6527 COMGETTER(ChipsetType)(&aChipset);
6528
6529 if (aChipset != ChipsetType_ICH9)
6530 {
6531 return setError(E_INVALIDARG,
6532 tr("Host PCI attachment only supported with ICH9 chipset"));
6533 }
6534
6535 // check if device with this host PCI address already attached
6536 for (HWData::PCIDeviceAssignmentList::const_iterator
6537 it = mHWData->mPCIDeviceAssignments.begin();
6538 it != mHWData->mPCIDeviceAssignments.end();
6539 ++it)
6540 {
6541 LONG iHostAddress = -1;
6542 ComPtr<PCIDeviceAttachment> pAttach;
6543 pAttach = *it;
6544 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6545 if (iHostAddress == aHostAddress)
6546 return setError(E_INVALIDARG,
6547 tr("Device with host PCI address already attached to this VM"));
6548 }
6549
6550 ComObjPtr<PCIDeviceAttachment> pda;
6551 char name[32];
6552
6553 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6554 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6555 pda.createObject();
6556 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6557 i_setModified(IsModified_MachineData);
6558 mHWData.backup();
6559 mHWData->mPCIDeviceAssignments.push_back(pda);
6560 }
6561
6562 return S_OK;
6563}
6564
6565/**
6566 * Currently this method doesn't detach device from the running VM,
6567 * just makes sure it's not plugged on next VM start.
6568 */
6569HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6570{
6571 ComObjPtr<PCIDeviceAttachment> pAttach;
6572 bool fRemoved = false;
6573 HRESULT rc;
6574
6575 // lock scope
6576 {
6577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6578
6579 rc = i_checkStateDependency(MutableStateDep);
6580 if (FAILED(rc)) return rc;
6581
6582 for (HWData::PCIDeviceAssignmentList::const_iterator
6583 it = mHWData->mPCIDeviceAssignments.begin();
6584 it != mHWData->mPCIDeviceAssignments.end();
6585 ++it)
6586 {
6587 LONG iHostAddress = -1;
6588 pAttach = *it;
6589 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6590 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6591 {
6592 i_setModified(IsModified_MachineData);
6593 mHWData.backup();
6594 mHWData->mPCIDeviceAssignments.remove(pAttach);
6595 fRemoved = true;
6596 break;
6597 }
6598 }
6599 }
6600
6601
6602 /* Fire event outside of the lock */
6603 if (fRemoved)
6604 {
6605 Assert(!pAttach.isNull());
6606 ComPtr<IEventSource> es;
6607 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6608 Assert(SUCCEEDED(rc));
6609 Bstr mid;
6610 rc = this->COMGETTER(Id)(mid.asOutParam());
6611 Assert(SUCCEEDED(rc));
6612 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6613 }
6614
6615 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6616 tr("No host PCI device %08x attached"),
6617 aHostAddress
6618 );
6619}
6620
6621HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6622{
6623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6624
6625 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6626 size_t i = 0;
6627 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6628 it = mHWData->mPCIDeviceAssignments.begin();
6629 it != mHWData->mPCIDeviceAssignments.end();
6630 ++it, ++i)
6631 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6632
6633 return S_OK;
6634}
6635
6636HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6637{
6638 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6639
6640 return S_OK;
6641}
6642
6643HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6644{
6645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6646
6647 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6648
6649 return S_OK;
6650}
6651
6652HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6653{
6654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6655 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6656 if (SUCCEEDED(hrc))
6657 {
6658 hrc = mHWData.backupEx();
6659 if (SUCCEEDED(hrc))
6660 {
6661 i_setModified(IsModified_MachineData);
6662 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6663 }
6664 }
6665 return hrc;
6666}
6667
6668HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6669{
6670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6671 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6672 return S_OK;
6673}
6674
6675HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6676{
6677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6678 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6679 if (SUCCEEDED(hrc))
6680 {
6681 hrc = mHWData.backupEx();
6682 if (SUCCEEDED(hrc))
6683 {
6684 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6685 if (SUCCEEDED(hrc))
6686 i_setModified(IsModified_MachineData);
6687 }
6688 }
6689 return hrc;
6690}
6691
6692HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6693{
6694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6695
6696 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6697
6698 return S_OK;
6699}
6700
6701HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6702{
6703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6704 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6705 if (SUCCEEDED(hrc))
6706 {
6707 hrc = mHWData.backupEx();
6708 if (SUCCEEDED(hrc))
6709 {
6710 i_setModified(IsModified_MachineData);
6711 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6712 }
6713 }
6714 return hrc;
6715}
6716
6717HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6718{
6719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6720
6721 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6722
6723 return S_OK;
6724}
6725
6726HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6727{
6728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6729
6730 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6731 if ( SUCCEEDED(hrc)
6732 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6733 {
6734 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6735 int vrc;
6736
6737 if (aAutostartEnabled)
6738 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6739 else
6740 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6741
6742 if (RT_SUCCESS(vrc))
6743 {
6744 hrc = mHWData.backupEx();
6745 if (SUCCEEDED(hrc))
6746 {
6747 i_setModified(IsModified_MachineData);
6748 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6749 }
6750 }
6751 else if (vrc == VERR_NOT_SUPPORTED)
6752 hrc = setError(VBOX_E_NOT_SUPPORTED,
6753 tr("The VM autostart feature is not supported on this platform"));
6754 else if (vrc == VERR_PATH_NOT_FOUND)
6755 hrc = setError(E_FAIL,
6756 tr("The path to the autostart database is not set"));
6757 else
6758 hrc = setError(E_UNEXPECTED,
6759 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6760 aAutostartEnabled ? "Adding" : "Removing",
6761 mUserData->s.strName.c_str(), vrc);
6762 }
6763 return hrc;
6764}
6765
6766HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6767{
6768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6769
6770 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6771
6772 return S_OK;
6773}
6774
6775HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6776{
6777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6778 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6779 if (SUCCEEDED(hrc))
6780 {
6781 hrc = mHWData.backupEx();
6782 if (SUCCEEDED(hrc))
6783 {
6784 i_setModified(IsModified_MachineData);
6785 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6786 }
6787 }
6788 return hrc;
6789}
6790
6791HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6792{
6793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6794
6795 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6796
6797 return S_OK;
6798}
6799
6800HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6801{
6802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6803 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6804 if ( SUCCEEDED(hrc)
6805 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6806 {
6807 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6808 int vrc;
6809
6810 if (aAutostopType != AutostopType_Disabled)
6811 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6812 else
6813 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6814
6815 if (RT_SUCCESS(vrc))
6816 {
6817 hrc = mHWData.backupEx();
6818 if (SUCCEEDED(hrc))
6819 {
6820 i_setModified(IsModified_MachineData);
6821 mHWData->mAutostart.enmAutostopType = aAutostopType;
6822 }
6823 }
6824 else if (vrc == VERR_NOT_SUPPORTED)
6825 hrc = setError(VBOX_E_NOT_SUPPORTED,
6826 tr("The VM autostop feature is not supported on this platform"));
6827 else if (vrc == VERR_PATH_NOT_FOUND)
6828 hrc = setError(E_FAIL,
6829 tr("The path to the autostart database is not set"));
6830 else
6831 hrc = setError(E_UNEXPECTED,
6832 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6833 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6834 mUserData->s.strName.c_str(), vrc);
6835 }
6836 return hrc;
6837}
6838
6839HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6840{
6841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6842
6843 aDefaultFrontend = mHWData->mDefaultFrontend;
6844
6845 return S_OK;
6846}
6847
6848HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6849{
6850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6851 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6852 if (SUCCEEDED(hrc))
6853 {
6854 hrc = mHWData.backupEx();
6855 if (SUCCEEDED(hrc))
6856 {
6857 i_setModified(IsModified_MachineData);
6858 mHWData->mDefaultFrontend = aDefaultFrontend;
6859 }
6860 }
6861 return hrc;
6862}
6863
6864HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6865{
6866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6867 size_t cbIcon = mUserData->s.ovIcon.size();
6868 aIcon.resize(cbIcon);
6869 if (cbIcon)
6870 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6871 return S_OK;
6872}
6873
6874HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6875{
6876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6877 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6878 if (SUCCEEDED(hrc))
6879 {
6880 i_setModified(IsModified_MachineData);
6881 mUserData.backup();
6882 size_t cbIcon = aIcon.size();
6883 mUserData->s.ovIcon.resize(cbIcon);
6884 if (cbIcon)
6885 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6886 }
6887 return hrc;
6888}
6889
6890HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6891{
6892#ifdef VBOX_WITH_USB
6893 *aUSBProxyAvailable = true;
6894#else
6895 *aUSBProxyAvailable = false;
6896#endif
6897 return S_OK;
6898}
6899
6900HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6901{
6902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 *aVMProcessPriority = mUserData->s.enmVMPriority;
6905
6906 return S_OK;
6907}
6908
6909HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6910{
6911 RT_NOREF(aVMProcessPriority);
6912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6913 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6914 if (SUCCEEDED(hrc))
6915 {
6916 hrc = mUserData.backupEx();
6917 if (SUCCEEDED(hrc))
6918 {
6919 i_setModified(IsModified_MachineData);
6920 mUserData->s.enmVMPriority = aVMProcessPriority;
6921 }
6922 }
6923 alock.release();
6924 if (SUCCEEDED(hrc))
6925 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6926 return hrc;
6927}
6928
6929HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6930 ComPtr<IProgress> &aProgress)
6931{
6932 ComObjPtr<Progress> pP;
6933 Progress *ppP = pP;
6934 IProgress *iP = static_cast<IProgress *>(ppP);
6935 IProgress **pProgress = &iP;
6936
6937 IMachine *pTarget = aTarget;
6938
6939 /* Convert the options. */
6940 RTCList<CloneOptions_T> optList;
6941 if (aOptions.size())
6942 for (size_t i = 0; i < aOptions.size(); ++i)
6943 optList.append(aOptions[i]);
6944
6945 if (optList.contains(CloneOptions_Link))
6946 {
6947 if (!i_isSnapshotMachine())
6948 return setError(E_INVALIDARG,
6949 tr("Linked clone can only be created from a snapshot"));
6950 if (aMode != CloneMode_MachineState)
6951 return setError(E_INVALIDARG,
6952 tr("Linked clone can only be created for a single machine state"));
6953 }
6954 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6955
6956 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6957
6958 HRESULT rc = pWorker->start(pProgress);
6959
6960 pP = static_cast<Progress *>(*pProgress);
6961 pP.queryInterfaceTo(aProgress.asOutParam());
6962
6963 return rc;
6964
6965}
6966
6967HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6968 const com::Utf8Str &aType,
6969 ComPtr<IProgress> &aProgress)
6970{
6971 LogFlowThisFuncEnter();
6972
6973 ComObjPtr<Progress> ptrProgress;
6974 HRESULT hrc = ptrProgress.createObject();
6975 if (SUCCEEDED(hrc))
6976 {
6977 /* Initialize our worker task */
6978 MachineMoveVM *pTask = NULL;
6979 try
6980 {
6981 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6982 }
6983 catch (std::bad_alloc &)
6984 {
6985 return E_OUTOFMEMORY;
6986 }
6987
6988 hrc = pTask->init();//no exceptions are thrown
6989
6990 if (SUCCEEDED(hrc))
6991 {
6992 hrc = pTask->createThread();
6993 pTask = NULL; /* Consumed by createThread(). */
6994 if (SUCCEEDED(hrc))
6995 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6996 else
6997 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6998 }
6999 else
7000 delete pTask;
7001 }
7002
7003 LogFlowThisFuncLeave();
7004 return hrc;
7005
7006}
7007
7008HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7009{
7010 NOREF(aProgress);
7011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7012
7013 // This check should always fail.
7014 HRESULT rc = i_checkStateDependency(MutableStateDep);
7015 if (FAILED(rc)) return rc;
7016
7017 AssertFailedReturn(E_NOTIMPL);
7018}
7019
7020HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7021{
7022 NOREF(aSavedStateFile);
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::discardSavedState(BOOL aFRemoveFile)
7033{
7034 NOREF(aFRemoveFile);
7035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7036
7037 // This check should always fail.
7038 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7039 if (FAILED(rc)) return rc;
7040
7041 AssertFailedReturn(E_NOTIMPL);
7042}
7043
7044// public methods for internal purposes
7045/////////////////////////////////////////////////////////////////////////////
7046
7047/**
7048 * Adds the given IsModified_* flag to the dirty flags of the machine.
7049 * This must be called either during i_loadSettings or under the machine write lock.
7050 * @param fl Flag
7051 * @param fAllowStateModification If state modifications are allowed.
7052 */
7053void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7054{
7055 mData->flModifications |= fl;
7056 if (fAllowStateModification && i_isStateModificationAllowed())
7057 mData->mCurrentStateModified = true;
7058}
7059
7060/**
7061 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7062 * care of the write locking.
7063 *
7064 * @param fModification The flag to add.
7065 * @param fAllowStateModification If state modifications are allowed.
7066 */
7067void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7068{
7069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7070 i_setModified(fModification, fAllowStateModification);
7071}
7072
7073/**
7074 * Saves the registry entry of this machine to the given configuration node.
7075 *
7076 * @param data Machine registry data.
7077 *
7078 * @note locks this object for reading.
7079 */
7080HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7081{
7082 AutoLimitedCaller autoCaller(this);
7083 AssertComRCReturnRC(autoCaller.rc());
7084
7085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 data.uuid = mData->mUuid;
7088 data.strSettingsFile = mData->m_strConfigFile;
7089
7090 return S_OK;
7091}
7092
7093/**
7094 * Calculates the absolute path of the given path taking the directory of the
7095 * machine settings file as the current directory.
7096 *
7097 * @param strPath Path to calculate the absolute path for.
7098 * @param aResult Where to put the result (used only on success, can be the
7099 * same Utf8Str instance as passed in @a aPath).
7100 * @return IPRT result.
7101 *
7102 * @note Locks this object for reading.
7103 */
7104int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7105{
7106 AutoCaller autoCaller(this);
7107 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7108
7109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7110
7111 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7112
7113 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7114
7115 strSettingsDir.stripFilename();
7116 char szFolder[RTPATH_MAX];
7117 size_t cbFolder = sizeof(szFolder);
7118 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7119 if (RT_SUCCESS(vrc))
7120 aResult = szFolder;
7121
7122 return vrc;
7123}
7124
7125/**
7126 * Copies strSource to strTarget, making it relative to the machine folder
7127 * if it is a subdirectory thereof, or simply copying it otherwise.
7128 *
7129 * @param strSource Path to evaluate and copy.
7130 * @param strTarget Buffer to receive target path.
7131 *
7132 * @note Locks this object for reading.
7133 */
7134void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7135 Utf8Str &strTarget)
7136{
7137 AutoCaller autoCaller(this);
7138 AssertComRCReturn(autoCaller.rc(), (void)0);
7139
7140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7141
7142 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7143 // use strTarget as a temporary buffer to hold the machine settings dir
7144 strTarget = mData->m_strConfigFileFull;
7145 strTarget.stripFilename();
7146 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7147 {
7148 // is relative: then append what's left
7149 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7150 // for empty paths (only possible for subdirs) use "." to avoid
7151 // triggering default settings for not present config attributes.
7152 if (strTarget.isEmpty())
7153 strTarget = ".";
7154 }
7155 else
7156 // is not relative: then overwrite
7157 strTarget = strSource;
7158}
7159
7160/**
7161 * Returns the full path to the machine's log folder in the
7162 * \a aLogFolder argument.
7163 */
7164void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7165{
7166 AutoCaller autoCaller(this);
7167 AssertComRCReturnVoid(autoCaller.rc());
7168
7169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7170
7171 char szTmp[RTPATH_MAX];
7172 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7173 if (RT_SUCCESS(vrc))
7174 {
7175 if (szTmp[0] && !mUserData.isNull())
7176 {
7177 char szTmp2[RTPATH_MAX];
7178 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7179 if (RT_SUCCESS(vrc))
7180 aLogFolder.printf("%s%c%s",
7181 szTmp2,
7182 RTPATH_DELIMITER,
7183 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7184 }
7185 else
7186 vrc = VERR_PATH_IS_RELATIVE;
7187 }
7188
7189 if (RT_FAILURE(vrc))
7190 {
7191 // fallback if VBOX_USER_LOGHOME is not set or invalid
7192 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7193 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7194 aLogFolder.append(RTPATH_DELIMITER);
7195 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7196 }
7197}
7198
7199/**
7200 * Returns the full path to the machine's log file for an given index.
7201 */
7202Utf8Str Machine::i_getLogFilename(ULONG idx)
7203{
7204 Utf8Str logFolder;
7205 getLogFolder(logFolder);
7206 Assert(logFolder.length());
7207
7208 Utf8Str log;
7209 if (idx == 0)
7210 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7211#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7212 else if (idx == 1)
7213 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7214 else
7215 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7216#else
7217 else
7218 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7219#endif
7220 return log;
7221}
7222
7223/**
7224 * Returns the full path to the machine's hardened log file.
7225 */
7226Utf8Str Machine::i_getHardeningLogFilename(void)
7227{
7228 Utf8Str strFilename;
7229 getLogFolder(strFilename);
7230 Assert(strFilename.length());
7231 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7232 return strFilename;
7233}
7234
7235/**
7236 * Returns the default NVRAM filename based on the location of the VM config.
7237 * Note that this is a relative path.
7238 */
7239Utf8Str Machine::i_getDefaultNVRAMFilename()
7240{
7241 AutoCaller autoCaller(this);
7242 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7243
7244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7245
7246 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7247 || i_isSnapshotMachine())
7248 return Utf8Str::Empty;
7249
7250 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7251 strNVRAMFilePath.stripPath();
7252 strNVRAMFilePath.stripSuffix();
7253 strNVRAMFilePath += ".nvram";
7254
7255 return strNVRAMFilePath;
7256}
7257
7258/**
7259 * Returns the NVRAM filename for a new snapshot. This intentionally works
7260 * similarly to the saved state file naming. Note that this is usually
7261 * a relative path, unless the snapshot folder is absolute.
7262 */
7263Utf8Str Machine::i_getSnapshotNVRAMFilename()
7264{
7265 AutoCaller autoCaller(this);
7266 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7267
7268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7269
7270 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7271 return Utf8Str::Empty;
7272
7273 RTTIMESPEC ts;
7274 RTTimeNow(&ts);
7275 RTTIME time;
7276 RTTimeExplode(&time, &ts);
7277
7278 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7279 strNVRAMFilePath += RTPATH_DELIMITER;
7280 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7281 time.i32Year, time.u8Month, time.u8MonthDay,
7282 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7283
7284 return strNVRAMFilePath;
7285}
7286
7287/**
7288 * Composes a unique saved state filename based on the current system time. The filename is
7289 * granular to the second so this will work so long as no more than one snapshot is taken on
7290 * a machine per second.
7291 *
7292 * Before version 4.1, we used this formula for saved state files:
7293 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7294 * which no longer works because saved state files can now be shared between the saved state of the
7295 * "saved" machine and an online snapshot, and the following would cause problems:
7296 * 1) save machine
7297 * 2) create online snapshot from that machine state --> reusing saved state file
7298 * 3) save machine again --> filename would be reused, breaking the online snapshot
7299 *
7300 * So instead we now use a timestamp.
7301 *
7302 * @param strStateFilePath
7303 */
7304
7305void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7306{
7307 AutoCaller autoCaller(this);
7308 AssertComRCReturnVoid(autoCaller.rc());
7309
7310 {
7311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7312 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7313 }
7314
7315 RTTIMESPEC ts;
7316 RTTimeNow(&ts);
7317 RTTIME time;
7318 RTTimeExplode(&time, &ts);
7319
7320 strStateFilePath += RTPATH_DELIMITER;
7321 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7322 time.i32Year, time.u8Month, time.u8MonthDay,
7323 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7324}
7325
7326/**
7327 * Returns whether at least one USB controller is present for the VM.
7328 */
7329bool Machine::i_isUSBControllerPresent()
7330{
7331 AutoCaller autoCaller(this);
7332 AssertComRCReturn(autoCaller.rc(), false);
7333
7334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7335
7336 return (mUSBControllers->size() > 0);
7337}
7338
7339
7340/**
7341 * @note Locks this object for writing, calls the client process
7342 * (inside the lock).
7343 */
7344HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7345 const Utf8Str &strFrontend,
7346 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7347 ProgressProxy *aProgress)
7348{
7349 LogFlowThisFuncEnter();
7350
7351 AssertReturn(aControl, E_FAIL);
7352 AssertReturn(aProgress, E_FAIL);
7353 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7354
7355 AutoCaller autoCaller(this);
7356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7357
7358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7359
7360 if (!mData->mRegistered)
7361 return setError(E_UNEXPECTED,
7362 tr("The machine '%s' is not registered"),
7363 mUserData->s.strName.c_str());
7364
7365 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7366
7367 /* The process started when launching a VM with separate UI/VM processes is always
7368 * the UI process, i.e. needs special handling as it won't claim the session. */
7369 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7370
7371 if (fSeparate)
7372 {
7373 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7374 return setError(VBOX_E_INVALID_OBJECT_STATE,
7375 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7376 mUserData->s.strName.c_str());
7377 }
7378 else
7379 {
7380 if ( mData->mSession.mState == SessionState_Locked
7381 || mData->mSession.mState == SessionState_Spawning
7382 || mData->mSession.mState == SessionState_Unlocking)
7383 return setError(VBOX_E_INVALID_OBJECT_STATE,
7384 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7385 mUserData->s.strName.c_str());
7386
7387 /* may not be busy */
7388 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7389 }
7390
7391 /* Hardening logging */
7392#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7393 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7394 {
7395 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7396 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7397 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7398 {
7399 Utf8Str strStartupLogDir = strHardeningLogFile;
7400 strStartupLogDir.stripFilename();
7401 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7402 file without stripping the file. */
7403 }
7404 strSupHardeningLogArg.append(strHardeningLogFile);
7405
7406 /* Remove legacy log filename to avoid confusion. */
7407 Utf8Str strOldStartupLogFile;
7408 getLogFolder(strOldStartupLogFile);
7409 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7410 RTFileDelete(strOldStartupLogFile.c_str());
7411 }
7412#else
7413 Utf8Str strSupHardeningLogArg;
7414#endif
7415
7416 Utf8Str strAppOverride;
7417#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7418 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7419#endif
7420
7421 bool fUseVBoxSDS = false;
7422 Utf8Str strCanonicalName;
7423 if (false)
7424 { }
7425#ifdef VBOX_WITH_QTGUI
7426 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7427 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7428 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7429 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7430 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7431 {
7432 strCanonicalName = "GUI/Qt";
7433 fUseVBoxSDS = true;
7434 }
7435#endif
7436#ifdef VBOX_WITH_VBOXSDL
7437 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7438 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7439 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7440 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7441 {
7442 strCanonicalName = "GUI/SDL";
7443 fUseVBoxSDS = true;
7444 }
7445#endif
7446#ifdef VBOX_WITH_HEADLESS
7447 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7448 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7449 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7450 {
7451 strCanonicalName = "headless";
7452 }
7453#endif
7454 else
7455 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7456
7457 Utf8Str idStr = mData->mUuid.toString();
7458 Utf8Str const &strMachineName = mUserData->s.strName;
7459 RTPROCESS pid = NIL_RTPROCESS;
7460
7461#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7462 RT_NOREF(fUseVBoxSDS);
7463#else
7464 DWORD idCallerSession = ~(DWORD)0;
7465 if (fUseVBoxSDS)
7466 {
7467 /*
7468 * The VBoxSDS should be used for process launching the VM with
7469 * GUI only if the caller and the VBoxSDS are in different Windows
7470 * sessions and the caller in the interactive one.
7471 */
7472 fUseVBoxSDS = false;
7473
7474 /* Get windows session of the current process. The process token used
7475 due to several reasons:
7476 1. The token is absent for the current thread except someone set it
7477 for us.
7478 2. Needs to get the id of the session where the process is started.
7479 We only need to do this once, though. */
7480 static DWORD s_idCurrentSession = ~(DWORD)0;
7481 DWORD idCurrentSession = s_idCurrentSession;
7482 if (idCurrentSession == ~(DWORD)0)
7483 {
7484 HANDLE hCurrentProcessToken = NULL;
7485 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7486 {
7487 DWORD cbIgn = 0;
7488 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7489 s_idCurrentSession = idCurrentSession;
7490 else
7491 {
7492 idCurrentSession = ~(DWORD)0;
7493 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7494 }
7495 CloseHandle(hCurrentProcessToken);
7496 }
7497 else
7498 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7499 }
7500
7501 /* get the caller's session */
7502 HRESULT hrc = CoImpersonateClient();
7503 if (SUCCEEDED(hrc))
7504 {
7505 HANDLE hCallerThreadToken;
7506 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7507 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7508 &hCallerThreadToken))
7509 {
7510 SetLastError(NO_ERROR);
7511 DWORD cbIgn = 0;
7512 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7513 {
7514 /* Only need to use SDS if the session ID differs: */
7515 if (idCurrentSession != idCallerSession)
7516 {
7517 fUseVBoxSDS = false;
7518
7519 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7520 DWORD cbTokenGroups = 0;
7521 PTOKEN_GROUPS pTokenGroups = NULL;
7522 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7523 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7524 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7525 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7526 {
7527 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7528 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7529 PSID pInteractiveSid = NULL;
7530 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7531 {
7532 /* Iterate over the groups looking for the interactive SID: */
7533 fUseVBoxSDS = false;
7534 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7535 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7536 {
7537 fUseVBoxSDS = true;
7538 break;
7539 }
7540 FreeSid(pInteractiveSid);
7541 }
7542 }
7543 else
7544 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7545 RTMemTmpFree(pTokenGroups);
7546 }
7547 }
7548 else
7549 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7550 CloseHandle(hCallerThreadToken);
7551 }
7552 else
7553 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7554 CoRevertToSelf();
7555 }
7556 else
7557 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7558 }
7559 if (fUseVBoxSDS)
7560 {
7561 /* connect to VBoxSDS */
7562 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7563 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7564 if (FAILED(rc))
7565 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7566 strMachineName.c_str());
7567
7568 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7569 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7570 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7571 service to access the files. */
7572 rc = CoSetProxyBlanket(pVBoxSDS,
7573 RPC_C_AUTHN_DEFAULT,
7574 RPC_C_AUTHZ_DEFAULT,
7575 COLE_DEFAULT_PRINCIPAL,
7576 RPC_C_AUTHN_LEVEL_DEFAULT,
7577 RPC_C_IMP_LEVEL_IMPERSONATE,
7578 NULL,
7579 EOAC_DEFAULT);
7580 if (FAILED(rc))
7581 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7582
7583 size_t const cEnvVars = aEnvironmentChanges.size();
7584 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7585 for (size_t i = 0; i < cEnvVars; i++)
7586 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7587
7588 ULONG uPid = 0;
7589 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7590 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7591 idCallerSession, &uPid);
7592 if (FAILED(rc))
7593 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7594 pid = (RTPROCESS)uPid;
7595 }
7596 else
7597#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7598 {
7599 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7600 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7601 if (RT_FAILURE(vrc))
7602 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7603 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7604 }
7605
7606 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7607 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7608
7609 if (!fSeparate)
7610 {
7611 /*
7612 * Note that we don't release the lock here before calling the client,
7613 * because it doesn't need to call us back if called with a NULL argument.
7614 * Releasing the lock here is dangerous because we didn't prepare the
7615 * launch data yet, but the client we've just started may happen to be
7616 * too fast and call LockMachine() that will fail (because of PID, etc.),
7617 * so that the Machine will never get out of the Spawning session state.
7618 */
7619
7620 /* inform the session that it will be a remote one */
7621 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7622#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7623 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7624#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7625 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7626#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7627 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7628
7629 if (FAILED(rc))
7630 {
7631 /* restore the session state */
7632 mData->mSession.mState = SessionState_Unlocked;
7633 alock.release();
7634 mParent->i_addProcessToReap(pid);
7635 /* The failure may occur w/o any error info (from RPC), so provide one */
7636 return setError(VBOX_E_VM_ERROR,
7637 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7638 }
7639
7640 /* attach launch data to the machine */
7641 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7642 mData->mSession.mRemoteControls.push_back(aControl);
7643 mData->mSession.mProgress = aProgress;
7644 mData->mSession.mPID = pid;
7645 mData->mSession.mState = SessionState_Spawning;
7646 Assert(strCanonicalName.isNotEmpty());
7647 mData->mSession.mName = strCanonicalName;
7648 }
7649 else
7650 {
7651 /* For separate UI process we declare the launch as completed instantly, as the
7652 * actual headless VM start may or may not come. No point in remembering anything
7653 * yet, as what matters for us is when the headless VM gets started. */
7654 aProgress->i_notifyComplete(S_OK);
7655 }
7656
7657 alock.release();
7658 mParent->i_addProcessToReap(pid);
7659
7660 LogFlowThisFuncLeave();
7661 return S_OK;
7662}
7663
7664/**
7665 * Returns @c true if the given session machine instance has an open direct
7666 * session (and optionally also for direct sessions which are closing) and
7667 * returns the session control machine instance if so.
7668 *
7669 * Note that when the method returns @c false, the arguments remain unchanged.
7670 *
7671 * @param aMachine Session machine object.
7672 * @param aControl Direct session control object (optional).
7673 * @param aRequireVM If true then only allow VM sessions.
7674 * @param aAllowClosing If true then additionally a session which is currently
7675 * being closed will also be allowed.
7676 *
7677 * @note locks this object for reading.
7678 */
7679bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7680 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7681 bool aRequireVM /*= false*/,
7682 bool aAllowClosing /*= false*/)
7683{
7684 AutoLimitedCaller autoCaller(this);
7685 AssertComRCReturn(autoCaller.rc(), false);
7686
7687 /* just return false for inaccessible machines */
7688 if (getObjectState().getState() != ObjectState::Ready)
7689 return false;
7690
7691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7692
7693 if ( ( mData->mSession.mState == SessionState_Locked
7694 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7695 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7696 )
7697 {
7698 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7699
7700 aMachine = mData->mSession.mMachine;
7701
7702 if (aControl != NULL)
7703 *aControl = mData->mSession.mDirectControl;
7704
7705 return true;
7706 }
7707
7708 return false;
7709}
7710
7711/**
7712 * Returns @c true if the given machine has an spawning direct session.
7713 *
7714 * @note locks this object for reading.
7715 */
7716bool Machine::i_isSessionSpawning()
7717{
7718 AutoLimitedCaller autoCaller(this);
7719 AssertComRCReturn(autoCaller.rc(), false);
7720
7721 /* just return false for inaccessible machines */
7722 if (getObjectState().getState() != ObjectState::Ready)
7723 return false;
7724
7725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7726
7727 if (mData->mSession.mState == SessionState_Spawning)
7728 return true;
7729
7730 return false;
7731}
7732
7733/**
7734 * Called from the client watcher thread to check for unexpected client process
7735 * death during Session_Spawning state (e.g. before it successfully opened a
7736 * direct session).
7737 *
7738 * On Win32 and on OS/2, this method is called only when we've got the
7739 * direct client's process termination notification, so it always returns @c
7740 * true.
7741 *
7742 * On other platforms, this method returns @c true if the client process is
7743 * terminated and @c false if it's still alive.
7744 *
7745 * @note Locks this object for writing.
7746 */
7747bool Machine::i_checkForSpawnFailure()
7748{
7749 AutoCaller autoCaller(this);
7750 if (!autoCaller.isOk())
7751 {
7752 /* nothing to do */
7753 LogFlowThisFunc(("Already uninitialized!\n"));
7754 return true;
7755 }
7756
7757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7758
7759 if (mData->mSession.mState != SessionState_Spawning)
7760 {
7761 /* nothing to do */
7762 LogFlowThisFunc(("Not spawning any more!\n"));
7763 return true;
7764 }
7765
7766 HRESULT rc = S_OK;
7767
7768 /* PID not yet initialized, skip check. */
7769 if (mData->mSession.mPID == NIL_RTPROCESS)
7770 return false;
7771
7772 RTPROCSTATUS status;
7773 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7774
7775 if (vrc != VERR_PROCESS_RUNNING)
7776 {
7777 Utf8Str strExtraInfo;
7778
7779#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7780 /* If the startup logfile exists and is of non-zero length, tell the
7781 user to look there for more details to encourage them to attach it
7782 when reporting startup issues. */
7783 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7784 uint64_t cbStartupLogFile = 0;
7785 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7786 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7787 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7788#endif
7789
7790 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7791 rc = setError(E_FAIL,
7792 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7793 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7794 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7795 rc = setError(E_FAIL,
7796 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7797 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7798 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7799 rc = setError(E_FAIL,
7800 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7801 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7802 else
7803 rc = setErrorBoth(E_FAIL, vrc,
7804 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7805 i_getName().c_str(), vrc, strExtraInfo.c_str());
7806 }
7807
7808 if (FAILED(rc))
7809 {
7810 /* Close the remote session, remove the remote control from the list
7811 * and reset session state to Closed (@note keep the code in sync with
7812 * the relevant part in LockMachine()). */
7813
7814 Assert(mData->mSession.mRemoteControls.size() == 1);
7815 if (mData->mSession.mRemoteControls.size() == 1)
7816 {
7817 ErrorInfoKeeper eik;
7818 mData->mSession.mRemoteControls.front()->Uninitialize();
7819 }
7820
7821 mData->mSession.mRemoteControls.clear();
7822 mData->mSession.mState = SessionState_Unlocked;
7823
7824 /* finalize the progress after setting the state */
7825 if (!mData->mSession.mProgress.isNull())
7826 {
7827 mData->mSession.mProgress->notifyComplete(rc);
7828 mData->mSession.mProgress.setNull();
7829 }
7830
7831 mData->mSession.mPID = NIL_RTPROCESS;
7832
7833 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7834 return true;
7835 }
7836
7837 return false;
7838}
7839
7840/**
7841 * Checks whether the machine can be registered. If so, commits and saves
7842 * all settings.
7843 *
7844 * @note Must be called from mParent's write lock. Locks this object and
7845 * children for writing.
7846 */
7847HRESULT Machine::i_prepareRegister()
7848{
7849 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7850
7851 AutoLimitedCaller autoCaller(this);
7852 AssertComRCReturnRC(autoCaller.rc());
7853
7854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7855
7856 /* wait for state dependents to drop to zero */
7857 i_ensureNoStateDependencies();
7858
7859 if (!mData->mAccessible)
7860 return setError(VBOX_E_INVALID_OBJECT_STATE,
7861 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7862 mUserData->s.strName.c_str(),
7863 mData->mUuid.toString().c_str());
7864
7865 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7866
7867 if (mData->mRegistered)
7868 return setError(VBOX_E_INVALID_OBJECT_STATE,
7869 tr("The machine '%s' with UUID {%s} is already registered"),
7870 mUserData->s.strName.c_str(),
7871 mData->mUuid.toString().c_str());
7872
7873 HRESULT rc = S_OK;
7874
7875 // Ensure the settings are saved. If we are going to be registered and
7876 // no config file exists yet, create it by calling i_saveSettings() too.
7877 if ( (mData->flModifications)
7878 || (!mData->pMachineConfigFile->fileExists())
7879 )
7880 {
7881 rc = i_saveSettings(NULL);
7882 // no need to check whether VirtualBox.xml needs saving too since
7883 // we can't have a machine XML file rename pending
7884 if (FAILED(rc)) return rc;
7885 }
7886
7887 /* more config checking goes here */
7888
7889 if (SUCCEEDED(rc))
7890 {
7891 /* we may have had implicit modifications we want to fix on success */
7892 i_commit();
7893
7894 mData->mRegistered = true;
7895 }
7896 else
7897 {
7898 /* we may have had implicit modifications we want to cancel on failure*/
7899 i_rollback(false /* aNotify */);
7900 }
7901
7902 return rc;
7903}
7904
7905/**
7906 * Increases the number of objects dependent on the machine state or on the
7907 * registered state. Guarantees that these two states will not change at least
7908 * until #i_releaseStateDependency() is called.
7909 *
7910 * Depending on the @a aDepType value, additional state checks may be made.
7911 * These checks will set extended error info on failure. See
7912 * #i_checkStateDependency() for more info.
7913 *
7914 * If this method returns a failure, the dependency is not added and the caller
7915 * is not allowed to rely on any particular machine state or registration state
7916 * value and may return the failed result code to the upper level.
7917 *
7918 * @param aDepType Dependency type to add.
7919 * @param aState Current machine state (NULL if not interested).
7920 * @param aRegistered Current registered state (NULL if not interested).
7921 *
7922 * @note Locks this object for writing.
7923 */
7924HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7925 MachineState_T *aState /* = NULL */,
7926 BOOL *aRegistered /* = NULL */)
7927{
7928 AutoCaller autoCaller(this);
7929 AssertComRCReturnRC(autoCaller.rc());
7930
7931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7932
7933 HRESULT rc = i_checkStateDependency(aDepType);
7934 if (FAILED(rc)) return rc;
7935
7936 {
7937 if (mData->mMachineStateChangePending != 0)
7938 {
7939 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7940 * drop to zero so don't add more. It may make sense to wait a bit
7941 * and retry before reporting an error (since the pending state
7942 * transition should be really quick) but let's just assert for
7943 * now to see if it ever happens on practice. */
7944
7945 AssertFailed();
7946
7947 return setError(E_ACCESSDENIED,
7948 tr("Machine state change is in progress. Please retry the operation later."));
7949 }
7950
7951 ++mData->mMachineStateDeps;
7952 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7953 }
7954
7955 if (aState)
7956 *aState = mData->mMachineState;
7957 if (aRegistered)
7958 *aRegistered = mData->mRegistered;
7959
7960 return S_OK;
7961}
7962
7963/**
7964 * Decreases the number of objects dependent on the machine state.
7965 * Must always complete the #i_addStateDependency() call after the state
7966 * dependency is no more necessary.
7967 */
7968void Machine::i_releaseStateDependency()
7969{
7970 AutoCaller autoCaller(this);
7971 AssertComRCReturnVoid(autoCaller.rc());
7972
7973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7974
7975 /* releaseStateDependency() w/o addStateDependency()? */
7976 AssertReturnVoid(mData->mMachineStateDeps != 0);
7977 -- mData->mMachineStateDeps;
7978
7979 if (mData->mMachineStateDeps == 0)
7980 {
7981 /* inform i_ensureNoStateDependencies() that there are no more deps */
7982 if (mData->mMachineStateChangePending != 0)
7983 {
7984 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7985 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7986 }
7987 }
7988}
7989
7990Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7991{
7992 /* start with nothing found */
7993 Utf8Str strResult("");
7994
7995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7996
7997 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7998 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7999 // found:
8000 strResult = it->second; // source is a Utf8Str
8001
8002 return strResult;
8003}
8004
8005// protected methods
8006/////////////////////////////////////////////////////////////////////////////
8007
8008/**
8009 * Performs machine state checks based on the @a aDepType value. If a check
8010 * fails, this method will set extended error info, otherwise it will return
8011 * S_OK. It is supposed, that on failure, the caller will immediately return
8012 * the return value of this method to the upper level.
8013 *
8014 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8015 *
8016 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8017 * current state of this machine object allows to change settings of the
8018 * machine (i.e. the machine is not registered, or registered but not running
8019 * and not saved). It is useful to call this method from Machine setters
8020 * before performing any change.
8021 *
8022 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8023 * as for MutableStateDep except that if the machine is saved, S_OK is also
8024 * returned. This is useful in setters which allow changing machine
8025 * properties when it is in the saved state.
8026 *
8027 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8028 * if the current state of this machine object allows to change runtime
8029 * changeable settings of the machine (i.e. the machine is not registered, or
8030 * registered but either running or not running and not saved). It is useful
8031 * to call this method from Machine setters before performing any changes to
8032 * runtime changeable settings.
8033 *
8034 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8035 * the same as for MutableOrRunningStateDep except that if the machine is
8036 * saved, S_OK is also returned. This is useful in setters which allow
8037 * changing runtime and saved state changeable machine properties.
8038 *
8039 * @param aDepType Dependency type to check.
8040 *
8041 * @note Non Machine based classes should use #i_addStateDependency() and
8042 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8043 * template.
8044 *
8045 * @note This method must be called from under this object's read or write
8046 * lock.
8047 */
8048HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8049{
8050 switch (aDepType)
8051 {
8052 case AnyStateDep:
8053 {
8054 break;
8055 }
8056 case MutableStateDep:
8057 {
8058 if ( mData->mRegistered
8059 && ( !i_isSessionMachine()
8060 || ( mData->mMachineState != MachineState_Aborted
8061 && mData->mMachineState != MachineState_Teleported
8062 && mData->mMachineState != MachineState_PoweredOff
8063 )
8064 )
8065 )
8066 return setError(VBOX_E_INVALID_VM_STATE,
8067 tr("The machine is not mutable (state is %s)"),
8068 Global::stringifyMachineState(mData->mMachineState));
8069 break;
8070 }
8071 case MutableOrSavedStateDep:
8072 {
8073 if ( mData->mRegistered
8074 && ( !i_isSessionMachine()
8075 || ( mData->mMachineState != MachineState_Aborted
8076 && mData->mMachineState != MachineState_Teleported
8077 && mData->mMachineState != MachineState_Saved
8078 && mData->mMachineState != MachineState_PoweredOff
8079 )
8080 )
8081 )
8082 return setError(VBOX_E_INVALID_VM_STATE,
8083 tr("The machine is not mutable or saved (state is %s)"),
8084 Global::stringifyMachineState(mData->mMachineState));
8085 break;
8086 }
8087 case MutableOrRunningStateDep:
8088 {
8089 if ( mData->mRegistered
8090 && ( !i_isSessionMachine()
8091 || ( mData->mMachineState != MachineState_Aborted
8092 && mData->mMachineState != MachineState_Teleported
8093 && mData->mMachineState != MachineState_PoweredOff
8094 && !Global::IsOnline(mData->mMachineState)
8095 )
8096 )
8097 )
8098 return setError(VBOX_E_INVALID_VM_STATE,
8099 tr("The machine is not mutable or running (state is %s)"),
8100 Global::stringifyMachineState(mData->mMachineState));
8101 break;
8102 }
8103 case MutableOrSavedOrRunningStateDep:
8104 {
8105 if ( mData->mRegistered
8106 && ( !i_isSessionMachine()
8107 || ( mData->mMachineState != MachineState_Aborted
8108 && mData->mMachineState != MachineState_Teleported
8109 && mData->mMachineState != MachineState_Saved
8110 && mData->mMachineState != MachineState_PoweredOff
8111 && !Global::IsOnline(mData->mMachineState)
8112 )
8113 )
8114 )
8115 return setError(VBOX_E_INVALID_VM_STATE,
8116 tr("The machine is not mutable, saved or running (state is %s)"),
8117 Global::stringifyMachineState(mData->mMachineState));
8118 break;
8119 }
8120 }
8121
8122 return S_OK;
8123}
8124
8125/**
8126 * Helper to initialize all associated child objects and allocate data
8127 * structures.
8128 *
8129 * This method must be called as a part of the object's initialization procedure
8130 * (usually done in the #init() method).
8131 *
8132 * @note Must be called only from #init() or from #i_registeredInit().
8133 */
8134HRESULT Machine::initDataAndChildObjects()
8135{
8136 AutoCaller autoCaller(this);
8137 AssertComRCReturnRC(autoCaller.rc());
8138 AssertReturn( getObjectState().getState() == ObjectState::InInit
8139 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8140
8141 AssertReturn(!mData->mAccessible, E_FAIL);
8142
8143 /* allocate data structures */
8144 mSSData.allocate();
8145 mUserData.allocate();
8146 mHWData.allocate();
8147 mMediumAttachments.allocate();
8148 mStorageControllers.allocate();
8149 mUSBControllers.allocate();
8150
8151 /* initialize mOSTypeId */
8152 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8153
8154/** @todo r=bird: init() methods never fails, right? Why don't we make them
8155 * return void then! */
8156
8157 /* create associated BIOS settings object */
8158 unconst(mBIOSSettings).createObject();
8159 mBIOSSettings->init(this);
8160
8161 /* create associated record settings object */
8162 unconst(mRecordingSettings).createObject();
8163 mRecordingSettings->init(this);
8164
8165 /* create the graphics adapter object (always present) */
8166 unconst(mGraphicsAdapter).createObject();
8167 mGraphicsAdapter->init(this);
8168
8169 /* create an associated VRDE object (default is disabled) */
8170 unconst(mVRDEServer).createObject();
8171 mVRDEServer->init(this);
8172
8173 /* create associated serial port objects */
8174 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8175 {
8176 unconst(mSerialPorts[slot]).createObject();
8177 mSerialPorts[slot]->init(this, slot);
8178 }
8179
8180 /* create associated parallel port objects */
8181 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8182 {
8183 unconst(mParallelPorts[slot]).createObject();
8184 mParallelPorts[slot]->init(this, slot);
8185 }
8186
8187 /* create the audio adapter object (always present, default is disabled) */
8188 unconst(mAudioAdapter).createObject();
8189 mAudioAdapter->init(this);
8190
8191 /* create the USB device filters object (always present) */
8192 unconst(mUSBDeviceFilters).createObject();
8193 mUSBDeviceFilters->init(this);
8194
8195 /* create associated network adapter objects */
8196 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8197 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8198 {
8199 unconst(mNetworkAdapters[slot]).createObject();
8200 mNetworkAdapters[slot]->init(this, slot);
8201 }
8202
8203 /* create the bandwidth control */
8204 unconst(mBandwidthControl).createObject();
8205 mBandwidthControl->init(this);
8206
8207 return S_OK;
8208}
8209
8210/**
8211 * Helper to uninitialize all associated child objects and to free all data
8212 * structures.
8213 *
8214 * This method must be called as a part of the object's uninitialization
8215 * procedure (usually done in the #uninit() method).
8216 *
8217 * @note Must be called only from #uninit() or from #i_registeredInit().
8218 */
8219void Machine::uninitDataAndChildObjects()
8220{
8221 AutoCaller autoCaller(this);
8222 AssertComRCReturnVoid(autoCaller.rc());
8223 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8224 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8225 || getObjectState().getState() == ObjectState::InUninit
8226 || getObjectState().getState() == ObjectState::Limited);
8227
8228 /* tell all our other child objects we've been uninitialized */
8229 if (mBandwidthControl)
8230 {
8231 mBandwidthControl->uninit();
8232 unconst(mBandwidthControl).setNull();
8233 }
8234
8235 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8236 {
8237 if (mNetworkAdapters[slot])
8238 {
8239 mNetworkAdapters[slot]->uninit();
8240 unconst(mNetworkAdapters[slot]).setNull();
8241 }
8242 }
8243
8244 if (mUSBDeviceFilters)
8245 {
8246 mUSBDeviceFilters->uninit();
8247 unconst(mUSBDeviceFilters).setNull();
8248 }
8249
8250 if (mAudioAdapter)
8251 {
8252 mAudioAdapter->uninit();
8253 unconst(mAudioAdapter).setNull();
8254 }
8255
8256 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8257 {
8258 if (mParallelPorts[slot])
8259 {
8260 mParallelPorts[slot]->uninit();
8261 unconst(mParallelPorts[slot]).setNull();
8262 }
8263 }
8264
8265 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8266 {
8267 if (mSerialPorts[slot])
8268 {
8269 mSerialPorts[slot]->uninit();
8270 unconst(mSerialPorts[slot]).setNull();
8271 }
8272 }
8273
8274 if (mVRDEServer)
8275 {
8276 mVRDEServer->uninit();
8277 unconst(mVRDEServer).setNull();
8278 }
8279
8280 if (mGraphicsAdapter)
8281 {
8282 mGraphicsAdapter->uninit();
8283 unconst(mGraphicsAdapter).setNull();
8284 }
8285
8286 if (mBIOSSettings)
8287 {
8288 mBIOSSettings->uninit();
8289 unconst(mBIOSSettings).setNull();
8290 }
8291
8292 if (mRecordingSettings)
8293 {
8294 mRecordingSettings->uninit();
8295 unconst(mRecordingSettings).setNull();
8296 }
8297
8298 /* Deassociate media (only when a real Machine or a SnapshotMachine
8299 * instance is uninitialized; SessionMachine instances refer to real
8300 * Machine media). This is necessary for a clean re-initialization of
8301 * the VM after successfully re-checking the accessibility state. Note
8302 * that in case of normal Machine or SnapshotMachine uninitialization (as
8303 * a result of unregistering or deleting the snapshot), outdated media
8304 * attachments will already be uninitialized and deleted, so this
8305 * code will not affect them. */
8306 if ( !mMediumAttachments.isNull()
8307 && !i_isSessionMachine()
8308 )
8309 {
8310 for (MediumAttachmentList::const_iterator
8311 it = mMediumAttachments->begin();
8312 it != mMediumAttachments->end();
8313 ++it)
8314 {
8315 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8316 if (pMedium.isNull())
8317 continue;
8318 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8319 AssertComRC(rc);
8320 }
8321 }
8322
8323 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8324 {
8325 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8326 if (mData->mFirstSnapshot)
8327 {
8328 // snapshots tree is protected by machine write lock; strictly
8329 // this isn't necessary here since we're deleting the entire
8330 // machine, but otherwise we assert in Snapshot::uninit()
8331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8332 mData->mFirstSnapshot->uninit();
8333 mData->mFirstSnapshot.setNull();
8334 }
8335
8336 mData->mCurrentSnapshot.setNull();
8337 }
8338
8339 /* free data structures (the essential mData structure is not freed here
8340 * since it may be still in use) */
8341 mMediumAttachments.free();
8342 mStorageControllers.free();
8343 mUSBControllers.free();
8344 mHWData.free();
8345 mUserData.free();
8346 mSSData.free();
8347}
8348
8349/**
8350 * Returns a pointer to the Machine object for this machine that acts like a
8351 * parent for complex machine data objects such as shared folders, etc.
8352 *
8353 * For primary Machine objects and for SnapshotMachine objects, returns this
8354 * object's pointer itself. For SessionMachine objects, returns the peer
8355 * (primary) machine pointer.
8356 */
8357Machine *Machine::i_getMachine()
8358{
8359 if (i_isSessionMachine())
8360 return (Machine*)mPeer;
8361 return this;
8362}
8363
8364/**
8365 * Makes sure that there are no machine state dependents. If necessary, waits
8366 * for the number of dependents to drop to zero.
8367 *
8368 * Make sure this method is called from under this object's write lock to
8369 * guarantee that no new dependents may be added when this method returns
8370 * control to the caller.
8371 *
8372 * @note Locks this object for writing. The lock will be released while waiting
8373 * (if necessary).
8374 *
8375 * @warning To be used only in methods that change the machine state!
8376 */
8377void Machine::i_ensureNoStateDependencies()
8378{
8379 AssertReturnVoid(isWriteLockOnCurrentThread());
8380
8381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8382
8383 /* Wait for all state dependents if necessary */
8384 if (mData->mMachineStateDeps != 0)
8385 {
8386 /* lazy semaphore creation */
8387 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8388 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8389
8390 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8391 mData->mMachineStateDeps));
8392
8393 ++mData->mMachineStateChangePending;
8394
8395 /* reset the semaphore before waiting, the last dependent will signal
8396 * it */
8397 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8398
8399 alock.release();
8400
8401 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8402
8403 alock.acquire();
8404
8405 -- mData->mMachineStateChangePending;
8406 }
8407}
8408
8409/**
8410 * Changes the machine state and informs callbacks.
8411 *
8412 * This method is not intended to fail so it either returns S_OK or asserts (and
8413 * returns a failure).
8414 *
8415 * @note Locks this object for writing.
8416 */
8417HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8418{
8419 LogFlowThisFuncEnter();
8420 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8421 Assert(aMachineState != MachineState_Null);
8422
8423 AutoCaller autoCaller(this);
8424 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8425
8426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8427
8428 /* wait for state dependents to drop to zero */
8429 i_ensureNoStateDependencies();
8430
8431 MachineState_T const enmOldState = mData->mMachineState;
8432 if (enmOldState != aMachineState)
8433 {
8434 mData->mMachineState = aMachineState;
8435 RTTimeNow(&mData->mLastStateChange);
8436
8437#ifdef VBOX_WITH_DTRACE_R3_MAIN
8438 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8439#endif
8440 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8441 }
8442
8443 LogFlowThisFuncLeave();
8444 return S_OK;
8445}
8446
8447/**
8448 * Searches for a shared folder with the given logical name
8449 * in the collection of shared folders.
8450 *
8451 * @param aName logical name of the shared folder
8452 * @param aSharedFolder where to return the found object
8453 * @param aSetError whether to set the error info if the folder is
8454 * not found
8455 * @return
8456 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8457 *
8458 * @note
8459 * must be called from under the object's lock!
8460 */
8461HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8462 ComObjPtr<SharedFolder> &aSharedFolder,
8463 bool aSetError /* = false */)
8464{
8465 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8466 for (HWData::SharedFolderList::const_iterator
8467 it = mHWData->mSharedFolders.begin();
8468 it != mHWData->mSharedFolders.end();
8469 ++it)
8470 {
8471 SharedFolder *pSF = *it;
8472 AutoCaller autoCaller(pSF);
8473 if (pSF->i_getName() == aName)
8474 {
8475 aSharedFolder = pSF;
8476 rc = S_OK;
8477 break;
8478 }
8479 }
8480
8481 if (aSetError && FAILED(rc))
8482 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8483
8484 return rc;
8485}
8486
8487/**
8488 * Initializes all machine instance data from the given settings structures
8489 * from XML. The exception is the machine UUID which needs special handling
8490 * depending on the caller's use case, so the caller needs to set that herself.
8491 *
8492 * This gets called in several contexts during machine initialization:
8493 *
8494 * -- When machine XML exists on disk already and needs to be loaded into memory,
8495 * for example, from #i_registeredInit() to load all registered machines on
8496 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8497 * attached to the machine should be part of some media registry already.
8498 *
8499 * -- During OVF import, when a machine config has been constructed from an
8500 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8501 * ensure that the media listed as attachments in the config (which have
8502 * been imported from the OVF) receive the correct registry ID.
8503 *
8504 * -- During VM cloning.
8505 *
8506 * @param config Machine settings from XML.
8507 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8508 * for each attached medium in the config.
8509 * @return
8510 */
8511HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8512 const Guid *puuidRegistry)
8513{
8514 // copy name, description, OS type, teleporter, UTC etc.
8515 mUserData->s = config.machineUserData;
8516
8517 // look up the object by Id to check it is valid
8518 ComObjPtr<GuestOSType> pGuestOSType;
8519 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8520 if (!pGuestOSType.isNull())
8521 mUserData->s.strOsType = pGuestOSType->i_id();
8522
8523 // stateFile (optional)
8524 if (config.strStateFile.isEmpty())
8525 mSSData->strStateFilePath.setNull();
8526 else
8527 {
8528 Utf8Str stateFilePathFull(config.strStateFile);
8529 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8530 if (RT_FAILURE(vrc))
8531 return setErrorBoth(E_FAIL, vrc,
8532 tr("Invalid saved state file path '%s' (%Rrc)"),
8533 config.strStateFile.c_str(),
8534 vrc);
8535 mSSData->strStateFilePath = stateFilePathFull;
8536 }
8537
8538 // snapshot folder needs special processing so set it again
8539 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8540 if (FAILED(rc)) return rc;
8541
8542 /* Copy the extra data items (config may or may not be the same as
8543 * mData->pMachineConfigFile) if necessary. When loading the XML files
8544 * from disk they are the same, but not for OVF import. */
8545 if (mData->pMachineConfigFile != &config)
8546 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8547
8548 /* currentStateModified (optional, default is true) */
8549 mData->mCurrentStateModified = config.fCurrentStateModified;
8550
8551 mData->mLastStateChange = config.timeLastStateChange;
8552
8553 /*
8554 * note: all mUserData members must be assigned prior this point because
8555 * we need to commit changes in order to let mUserData be shared by all
8556 * snapshot machine instances.
8557 */
8558 mUserData.commitCopy();
8559
8560 // machine registry, if present (must be loaded before snapshots)
8561 if (config.canHaveOwnMediaRegistry())
8562 {
8563 // determine machine folder
8564 Utf8Str strMachineFolder = i_getSettingsFileFull();
8565 strMachineFolder.stripFilename();
8566 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8567 config.mediaRegistry,
8568 strMachineFolder);
8569 if (FAILED(rc)) return rc;
8570 }
8571
8572 /* Snapshot node (optional) */
8573 size_t cRootSnapshots;
8574 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8575 {
8576 // there must be only one root snapshot
8577 Assert(cRootSnapshots == 1);
8578
8579 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8580
8581 rc = i_loadSnapshot(snap,
8582 config.uuidCurrentSnapshot,
8583 NULL); // no parent == first snapshot
8584 if (FAILED(rc)) return rc;
8585 }
8586
8587 // hardware data
8588 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8589 if (FAILED(rc)) return rc;
8590
8591 /*
8592 * NOTE: the assignment below must be the last thing to do,
8593 * otherwise it will be not possible to change the settings
8594 * somewhere in the code above because all setters will be
8595 * blocked by i_checkStateDependency(MutableStateDep).
8596 */
8597
8598 /* set the machine state to Aborted or Saved when appropriate */
8599 if (config.fAborted)
8600 {
8601 mSSData->strStateFilePath.setNull();
8602
8603 /* no need to use i_setMachineState() during init() */
8604 mData->mMachineState = MachineState_Aborted;
8605 }
8606 else if (!mSSData->strStateFilePath.isEmpty())
8607 {
8608 /* no need to use i_setMachineState() during init() */
8609 mData->mMachineState = MachineState_Saved;
8610 }
8611
8612 // after loading settings, we are no longer different from the XML on disk
8613 mData->flModifications = 0;
8614
8615 return S_OK;
8616}
8617
8618/**
8619 * Recursively loads all snapshots starting from the given.
8620 *
8621 * @param data snapshot settings.
8622 * @param aCurSnapshotId Current snapshot ID from the settings file.
8623 * @param aParentSnapshot Parent snapshot.
8624 */
8625HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8626 const Guid &aCurSnapshotId,
8627 Snapshot *aParentSnapshot)
8628{
8629 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8630 AssertReturn(!i_isSessionMachine(), E_FAIL);
8631
8632 HRESULT rc = S_OK;
8633
8634 Utf8Str strStateFile;
8635 if (!data.strStateFile.isEmpty())
8636 {
8637 /* optional */
8638 strStateFile = data.strStateFile;
8639 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8640 if (RT_FAILURE(vrc))
8641 return setErrorBoth(E_FAIL, vrc,
8642 tr("Invalid saved state file path '%s' (%Rrc)"),
8643 strStateFile.c_str(),
8644 vrc);
8645 }
8646
8647 /* create a snapshot machine object */
8648 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8649 pSnapshotMachine.createObject();
8650 rc = pSnapshotMachine->initFromSettings(this,
8651 data.hardware,
8652 &data.debugging,
8653 &data.autostart,
8654 data.uuid.ref(),
8655 strStateFile);
8656 if (FAILED(rc)) return rc;
8657
8658 /* create a snapshot object */
8659 ComObjPtr<Snapshot> pSnapshot;
8660 pSnapshot.createObject();
8661 /* initialize the snapshot */
8662 rc = pSnapshot->init(mParent, // VirtualBox object
8663 data.uuid,
8664 data.strName,
8665 data.strDescription,
8666 data.timestamp,
8667 pSnapshotMachine,
8668 aParentSnapshot);
8669 if (FAILED(rc)) return rc;
8670
8671 /* memorize the first snapshot if necessary */
8672 if (!mData->mFirstSnapshot)
8673 mData->mFirstSnapshot = pSnapshot;
8674
8675 /* memorize the current snapshot when appropriate */
8676 if ( !mData->mCurrentSnapshot
8677 && pSnapshot->i_getId() == aCurSnapshotId
8678 )
8679 mData->mCurrentSnapshot = pSnapshot;
8680
8681 // now create the children
8682 for (settings::SnapshotsList::const_iterator
8683 it = data.llChildSnapshots.begin();
8684 it != data.llChildSnapshots.end();
8685 ++it)
8686 {
8687 const settings::Snapshot &childData = *it;
8688 // recurse
8689 rc = i_loadSnapshot(childData,
8690 aCurSnapshotId,
8691 pSnapshot); // parent = the one we created above
8692 if (FAILED(rc)) return rc;
8693 }
8694
8695 return rc;
8696}
8697
8698/**
8699 * Loads settings into mHWData.
8700 *
8701 * @param puuidRegistry Registry ID.
8702 * @param puuidSnapshot Snapshot ID
8703 * @param data Reference to the hardware settings.
8704 * @param pDbg Pointer to the debugging settings.
8705 * @param pAutostart Pointer to the autostart settings.
8706 */
8707HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8708 const Guid *puuidSnapshot,
8709 const settings::Hardware &data,
8710 const settings::Debugging *pDbg,
8711 const settings::Autostart *pAutostart)
8712{
8713 AssertReturn(!i_isSessionMachine(), E_FAIL);
8714
8715 HRESULT rc = S_OK;
8716
8717 try
8718 {
8719 ComObjPtr<GuestOSType> pGuestOSType;
8720 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8721
8722 /* The hardware version attribute (optional). */
8723 mHWData->mHWVersion = data.strVersion;
8724 mHWData->mHardwareUUID = data.uuid;
8725
8726 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8727 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8728 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8729 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8730 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8731 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8732 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8733 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8734 mHWData->mPAEEnabled = data.fPAE;
8735 mHWData->mLongMode = data.enmLongMode;
8736 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8737 mHWData->mAPIC = data.fAPIC;
8738 mHWData->mX2APIC = data.fX2APIC;
8739 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8740 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8741 mHWData->mSpecCtrl = data.fSpecCtrl;
8742 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8743 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8744 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8745 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8746 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8747 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8748 mHWData->mCPUCount = data.cCPUs;
8749 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8750 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8751 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8752 mHWData->mCpuProfile = data.strCpuProfile;
8753
8754 // cpu
8755 if (mHWData->mCPUHotPlugEnabled)
8756 {
8757 for (settings::CpuList::const_iterator
8758 it = data.llCpus.begin();
8759 it != data.llCpus.end();
8760 ++it)
8761 {
8762 const settings::Cpu &cpu = *it;
8763
8764 mHWData->mCPUAttached[cpu.ulId] = true;
8765 }
8766 }
8767
8768 // cpuid leafs
8769 for (settings::CpuIdLeafsList::const_iterator
8770 it = data.llCpuIdLeafs.begin();
8771 it != data.llCpuIdLeafs.end();
8772 ++it)
8773 {
8774 const settings::CpuIdLeaf &rLeaf= *it;
8775 if ( rLeaf.idx < UINT32_C(0x20)
8776 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8777 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8778 mHWData->mCpuIdLeafList.push_back(rLeaf);
8779 /* else: just ignore */
8780 }
8781
8782 mHWData->mMemorySize = data.ulMemorySizeMB;
8783 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8784
8785 // boot order
8786 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8787 {
8788 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8789 if (it == data.mapBootOrder.end())
8790 mHWData->mBootOrder[i] = DeviceType_Null;
8791 else
8792 mHWData->mBootOrder[i] = it->second;
8793 }
8794
8795 mHWData->mFirmwareType = data.firmwareType;
8796 mHWData->mPointingHIDType = data.pointingHIDType;
8797 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8798 mHWData->mChipsetType = data.chipsetType;
8799 mHWData->mIommuType = data.iommuType;
8800 mHWData->mParavirtProvider = data.paravirtProvider;
8801 mHWData->mParavirtDebug = data.strParavirtDebug;
8802 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8803 mHWData->mHPETEnabled = data.fHPETEnabled;
8804
8805 /* GraphicsAdapter */
8806 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8807 if (FAILED(rc)) return rc;
8808
8809 /* VRDEServer */
8810 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8811 if (FAILED(rc)) return rc;
8812
8813 /* BIOS */
8814 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8815 if (FAILED(rc)) return rc;
8816
8817 /* Recording settings */
8818 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8819 if (FAILED(rc)) return rc;
8820
8821 // Bandwidth control (must come before network adapters)
8822 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8823 if (FAILED(rc)) return rc;
8824
8825 /* USB controllers */
8826 for (settings::USBControllerList::const_iterator
8827 it = data.usbSettings.llUSBControllers.begin();
8828 it != data.usbSettings.llUSBControllers.end();
8829 ++it)
8830 {
8831 const settings::USBController &settingsCtrl = *it;
8832 ComObjPtr<USBController> newCtrl;
8833
8834 newCtrl.createObject();
8835 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8836 mUSBControllers->push_back(newCtrl);
8837 }
8838
8839 /* USB device filters */
8840 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8841 if (FAILED(rc)) return rc;
8842
8843 // network adapters (establish array size first and apply defaults, to
8844 // ensure reading the same settings as we saved, since the list skips
8845 // adapters having defaults)
8846 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8847 size_t oldCount = mNetworkAdapters.size();
8848 if (newCount > oldCount)
8849 {
8850 mNetworkAdapters.resize(newCount);
8851 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8852 {
8853 unconst(mNetworkAdapters[slot]).createObject();
8854 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8855 }
8856 }
8857 else if (newCount < oldCount)
8858 mNetworkAdapters.resize(newCount);
8859 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8860 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8861 for (settings::NetworkAdaptersList::const_iterator
8862 it = data.llNetworkAdapters.begin();
8863 it != data.llNetworkAdapters.end();
8864 ++it)
8865 {
8866 const settings::NetworkAdapter &nic = *it;
8867
8868 /* slot uniqueness is guaranteed by XML Schema */
8869 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8870 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8871 if (FAILED(rc)) return rc;
8872 }
8873
8874 // serial ports (establish defaults first, to ensure reading the same
8875 // settings as we saved, since the list skips ports having defaults)
8876 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8877 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8878 for (settings::SerialPortsList::const_iterator
8879 it = data.llSerialPorts.begin();
8880 it != data.llSerialPorts.end();
8881 ++it)
8882 {
8883 const settings::SerialPort &s = *it;
8884
8885 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8886 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8887 if (FAILED(rc)) return rc;
8888 }
8889
8890 // parallel ports (establish defaults first, to ensure reading the same
8891 // settings as we saved, since the list skips ports having defaults)
8892 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8893 mParallelPorts[i]->i_applyDefaults();
8894 for (settings::ParallelPortsList::const_iterator
8895 it = data.llParallelPorts.begin();
8896 it != data.llParallelPorts.end();
8897 ++it)
8898 {
8899 const settings::ParallelPort &p = *it;
8900
8901 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8902 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8903 if (FAILED(rc)) return rc;
8904 }
8905
8906 /* AudioAdapter */
8907 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8908 if (FAILED(rc)) return rc;
8909
8910 /* storage controllers */
8911 rc = i_loadStorageControllers(data.storage,
8912 puuidRegistry,
8913 puuidSnapshot);
8914 if (FAILED(rc)) return rc;
8915
8916 /* Shared folders */
8917 for (settings::SharedFoldersList::const_iterator
8918 it = data.llSharedFolders.begin();
8919 it != data.llSharedFolders.end();
8920 ++it)
8921 {
8922 const settings::SharedFolder &sf = *it;
8923
8924 ComObjPtr<SharedFolder> sharedFolder;
8925 /* Check for double entries. Not allowed! */
8926 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8927 if (SUCCEEDED(rc))
8928 return setError(VBOX_E_OBJECT_IN_USE,
8929 tr("Shared folder named '%s' already exists"),
8930 sf.strName.c_str());
8931
8932 /* Create the new shared folder. Don't break on error. This will be
8933 * reported when the machine starts. */
8934 sharedFolder.createObject();
8935 rc = sharedFolder->init(i_getMachine(),
8936 sf.strName,
8937 sf.strHostPath,
8938 RT_BOOL(sf.fWritable),
8939 RT_BOOL(sf.fAutoMount),
8940 sf.strAutoMountPoint,
8941 false /* fFailOnError */);
8942 if (FAILED(rc)) return rc;
8943 mHWData->mSharedFolders.push_back(sharedFolder);
8944 }
8945
8946 // Clipboard
8947 mHWData->mClipboardMode = data.clipboardMode;
8948 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8949
8950 // drag'n'drop
8951 mHWData->mDnDMode = data.dndMode;
8952
8953 // guest settings
8954 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8955
8956 // IO settings
8957 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8958 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8959
8960 // Host PCI devices
8961 for (settings::HostPCIDeviceAttachmentList::const_iterator
8962 it = data.pciAttachments.begin();
8963 it != data.pciAttachments.end();
8964 ++it)
8965 {
8966 const settings::HostPCIDeviceAttachment &hpda = *it;
8967 ComObjPtr<PCIDeviceAttachment> pda;
8968
8969 pda.createObject();
8970 pda->i_loadSettings(this, hpda);
8971 mHWData->mPCIDeviceAssignments.push_back(pda);
8972 }
8973
8974 /*
8975 * (The following isn't really real hardware, but it lives in HWData
8976 * for reasons of convenience.)
8977 */
8978
8979#ifdef VBOX_WITH_GUEST_PROPS
8980 /* Guest properties (optional) */
8981
8982 /* Only load transient guest properties for configs which have saved
8983 * state, because there shouldn't be any for powered off VMs. The same
8984 * logic applies for snapshots, as offline snapshots shouldn't have
8985 * any such properties. They confuse the code in various places.
8986 * Note: can't rely on the machine state, as it isn't set yet. */
8987 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8988 /* apologies for the hacky unconst() usage, but this needs hacking
8989 * actually inconsistent settings into consistency, otherwise there
8990 * will be some corner cases where the inconsistency survives
8991 * surprisingly long without getting fixed, especially for snapshots
8992 * as there are no config changes. */
8993 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8994 for (settings::GuestPropertiesList::iterator
8995 it = llGuestProperties.begin();
8996 it != llGuestProperties.end();
8997 /*nothing*/)
8998 {
8999 const settings::GuestProperty &prop = *it;
9000 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9001 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9002 if ( fSkipTransientGuestProperties
9003 && ( fFlags & GUEST_PROP_F_TRANSIENT
9004 || fFlags & GUEST_PROP_F_TRANSRESET))
9005 {
9006 it = llGuestProperties.erase(it);
9007 continue;
9008 }
9009 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9010 mHWData->mGuestProperties[prop.strName] = property;
9011 ++it;
9012 }
9013#endif /* VBOX_WITH_GUEST_PROPS defined */
9014
9015 rc = i_loadDebugging(pDbg);
9016 if (FAILED(rc))
9017 return rc;
9018
9019 mHWData->mAutostart = *pAutostart;
9020
9021 /* default frontend */
9022 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9023 }
9024 catch (std::bad_alloc &)
9025 {
9026 return E_OUTOFMEMORY;
9027 }
9028
9029 AssertComRC(rc);
9030 return rc;
9031}
9032
9033/**
9034 * Called from i_loadHardware() to load the debugging settings of the
9035 * machine.
9036 *
9037 * @param pDbg Pointer to the settings.
9038 */
9039HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9040{
9041 mHWData->mDebugging = *pDbg;
9042 /* no more processing currently required, this will probably change. */
9043 return S_OK;
9044}
9045
9046/**
9047 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9048 *
9049 * @param data storage settings.
9050 * @param puuidRegistry media registry ID to set media to or NULL;
9051 * see Machine::i_loadMachineDataFromSettings()
9052 * @param puuidSnapshot snapshot ID
9053 * @return
9054 */
9055HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9056 const Guid *puuidRegistry,
9057 const Guid *puuidSnapshot)
9058{
9059 AssertReturn(!i_isSessionMachine(), E_FAIL);
9060
9061 HRESULT rc = S_OK;
9062
9063 for (settings::StorageControllersList::const_iterator
9064 it = data.llStorageControllers.begin();
9065 it != data.llStorageControllers.end();
9066 ++it)
9067 {
9068 const settings::StorageController &ctlData = *it;
9069
9070 ComObjPtr<StorageController> pCtl;
9071 /* Try to find one with the name first. */
9072 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9073 if (SUCCEEDED(rc))
9074 return setError(VBOX_E_OBJECT_IN_USE,
9075 tr("Storage controller named '%s' already exists"),
9076 ctlData.strName.c_str());
9077
9078 pCtl.createObject();
9079 rc = pCtl->init(this,
9080 ctlData.strName,
9081 ctlData.storageBus,
9082 ctlData.ulInstance,
9083 ctlData.fBootable);
9084 if (FAILED(rc)) return rc;
9085
9086 mStorageControllers->push_back(pCtl);
9087
9088 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9089 if (FAILED(rc)) return rc;
9090
9091 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9092 if (FAILED(rc)) return rc;
9093
9094 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9095 if (FAILED(rc)) return rc;
9096
9097 /* Load the attached devices now. */
9098 rc = i_loadStorageDevices(pCtl,
9099 ctlData,
9100 puuidRegistry,
9101 puuidSnapshot);
9102 if (FAILED(rc)) return rc;
9103 }
9104
9105 return S_OK;
9106}
9107
9108/**
9109 * Called from i_loadStorageControllers for a controller's devices.
9110 *
9111 * @param aStorageController
9112 * @param data
9113 * @param puuidRegistry media registry ID to set media to or NULL; see
9114 * Machine::i_loadMachineDataFromSettings()
9115 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9116 * @return
9117 */
9118HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9119 const settings::StorageController &data,
9120 const Guid *puuidRegistry,
9121 const Guid *puuidSnapshot)
9122{
9123 HRESULT rc = S_OK;
9124
9125 /* paranoia: detect duplicate attachments */
9126 for (settings::AttachedDevicesList::const_iterator
9127 it = data.llAttachedDevices.begin();
9128 it != data.llAttachedDevices.end();
9129 ++it)
9130 {
9131 const settings::AttachedDevice &ad = *it;
9132
9133 for (settings::AttachedDevicesList::const_iterator it2 = it;
9134 it2 != data.llAttachedDevices.end();
9135 ++it2)
9136 {
9137 if (it == it2)
9138 continue;
9139
9140 const settings::AttachedDevice &ad2 = *it2;
9141
9142 if ( ad.lPort == ad2.lPort
9143 && ad.lDevice == ad2.lDevice)
9144 {
9145 return setError(E_FAIL,
9146 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9147 aStorageController->i_getName().c_str(),
9148 ad.lPort,
9149 ad.lDevice,
9150 mUserData->s.strName.c_str());
9151 }
9152 }
9153 }
9154
9155 for (settings::AttachedDevicesList::const_iterator
9156 it = data.llAttachedDevices.begin();
9157 it != data.llAttachedDevices.end();
9158 ++it)
9159 {
9160 const settings::AttachedDevice &dev = *it;
9161 ComObjPtr<Medium> medium;
9162
9163 switch (dev.deviceType)
9164 {
9165 case DeviceType_Floppy:
9166 case DeviceType_DVD:
9167 if (dev.strHostDriveSrc.isNotEmpty())
9168 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9169 false /* fRefresh */, medium);
9170 else
9171 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9172 dev.uuid,
9173 false /* fRefresh */,
9174 false /* aSetError */,
9175 medium);
9176 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9177 // This is not an error. The host drive or UUID might have vanished, so just go
9178 // ahead without this removeable medium attachment
9179 rc = S_OK;
9180 break;
9181
9182 case DeviceType_HardDisk:
9183 {
9184 /* find a hard disk by UUID */
9185 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9186 if (FAILED(rc))
9187 {
9188 if (i_isSnapshotMachine())
9189 {
9190 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9191 // so the user knows that the bad disk is in a snapshot somewhere
9192 com::ErrorInfo info;
9193 return setError(E_FAIL,
9194 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9195 puuidSnapshot->raw(),
9196 info.getText().raw());
9197 }
9198 else
9199 return rc;
9200 }
9201
9202 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9203
9204 if (medium->i_getType() == MediumType_Immutable)
9205 {
9206 if (i_isSnapshotMachine())
9207 return setError(E_FAIL,
9208 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9209 "of the virtual machine '%s' ('%s')"),
9210 medium->i_getLocationFull().c_str(),
9211 dev.uuid.raw(),
9212 puuidSnapshot->raw(),
9213 mUserData->s.strName.c_str(),
9214 mData->m_strConfigFileFull.c_str());
9215
9216 return setError(E_FAIL,
9217 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9218 medium->i_getLocationFull().c_str(),
9219 dev.uuid.raw(),
9220 mUserData->s.strName.c_str(),
9221 mData->m_strConfigFileFull.c_str());
9222 }
9223
9224 if (medium->i_getType() == MediumType_MultiAttach)
9225 {
9226 if (i_isSnapshotMachine())
9227 return setError(E_FAIL,
9228 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9229 "of the virtual machine '%s' ('%s')"),
9230 medium->i_getLocationFull().c_str(),
9231 dev.uuid.raw(),
9232 puuidSnapshot->raw(),
9233 mUserData->s.strName.c_str(),
9234 mData->m_strConfigFileFull.c_str());
9235
9236 return setError(E_FAIL,
9237 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9238 medium->i_getLocationFull().c_str(),
9239 dev.uuid.raw(),
9240 mUserData->s.strName.c_str(),
9241 mData->m_strConfigFileFull.c_str());
9242 }
9243
9244 if ( !i_isSnapshotMachine()
9245 && medium->i_getChildren().size() != 0
9246 )
9247 return setError(E_FAIL,
9248 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9249 "because it has %d differencing child hard disks"),
9250 medium->i_getLocationFull().c_str(),
9251 dev.uuid.raw(),
9252 mUserData->s.strName.c_str(),
9253 mData->m_strConfigFileFull.c_str(),
9254 medium->i_getChildren().size());
9255
9256 if (i_findAttachment(*mMediumAttachments.data(),
9257 medium))
9258 return setError(E_FAIL,
9259 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9260 medium->i_getLocationFull().c_str(),
9261 dev.uuid.raw(),
9262 mUserData->s.strName.c_str(),
9263 mData->m_strConfigFileFull.c_str());
9264
9265 break;
9266 }
9267
9268 default:
9269 return setError(E_FAIL,
9270 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9271 medium->i_getLocationFull().c_str(),
9272 mUserData->s.strName.c_str(),
9273 mData->m_strConfigFileFull.c_str());
9274 }
9275
9276 if (FAILED(rc))
9277 break;
9278
9279 /* Bandwidth groups are loaded at this point. */
9280 ComObjPtr<BandwidthGroup> pBwGroup;
9281
9282 if (!dev.strBwGroup.isEmpty())
9283 {
9284 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9285 if (FAILED(rc))
9286 return setError(E_FAIL,
9287 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9288 medium->i_getLocationFull().c_str(),
9289 dev.strBwGroup.c_str(),
9290 mUserData->s.strName.c_str(),
9291 mData->m_strConfigFileFull.c_str());
9292 pBwGroup->i_reference();
9293 }
9294
9295 const Utf8Str controllerName = aStorageController->i_getName();
9296 ComObjPtr<MediumAttachment> pAttachment;
9297 pAttachment.createObject();
9298 rc = pAttachment->init(this,
9299 medium,
9300 controllerName,
9301 dev.lPort,
9302 dev.lDevice,
9303 dev.deviceType,
9304 false,
9305 dev.fPassThrough,
9306 dev.fTempEject,
9307 dev.fNonRotational,
9308 dev.fDiscard,
9309 dev.fHotPluggable,
9310 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9311 if (FAILED(rc)) break;
9312
9313 /* associate the medium with this machine and snapshot */
9314 if (!medium.isNull())
9315 {
9316 AutoCaller medCaller(medium);
9317 if (FAILED(medCaller.rc())) return medCaller.rc();
9318 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9319
9320 if (i_isSnapshotMachine())
9321 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9322 else
9323 rc = medium->i_addBackReference(mData->mUuid);
9324 /* If the medium->addBackReference fails it sets an appropriate
9325 * error message, so no need to do any guesswork here. */
9326
9327 if (puuidRegistry)
9328 // caller wants registry ID to be set on all attached media (OVF import case)
9329 medium->i_addRegistry(*puuidRegistry);
9330 }
9331
9332 if (FAILED(rc))
9333 break;
9334
9335 /* back up mMediumAttachments to let registeredInit() properly rollback
9336 * on failure (= limited accessibility) */
9337 i_setModified(IsModified_Storage);
9338 mMediumAttachments.backup();
9339 mMediumAttachments->push_back(pAttachment);
9340 }
9341
9342 return rc;
9343}
9344
9345/**
9346 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9347 *
9348 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9349 * @param aSnapshot where to return the found snapshot
9350 * @param aSetError true to set extended error info on failure
9351 */
9352HRESULT Machine::i_findSnapshotById(const Guid &aId,
9353 ComObjPtr<Snapshot> &aSnapshot,
9354 bool aSetError /* = false */)
9355{
9356 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9357
9358 if (!mData->mFirstSnapshot)
9359 {
9360 if (aSetError)
9361 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9362 return E_FAIL;
9363 }
9364
9365 if (aId.isZero())
9366 aSnapshot = mData->mFirstSnapshot;
9367 else
9368 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9369
9370 if (!aSnapshot)
9371 {
9372 if (aSetError)
9373 return setError(E_FAIL,
9374 tr("Could not find a snapshot with UUID {%s}"),
9375 aId.toString().c_str());
9376 return E_FAIL;
9377 }
9378
9379 return S_OK;
9380}
9381
9382/**
9383 * Returns the snapshot with the given name or fails of no such snapshot.
9384 *
9385 * @param strName snapshot name to find
9386 * @param aSnapshot where to return the found snapshot
9387 * @param aSetError true to set extended error info on failure
9388 */
9389HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9390 ComObjPtr<Snapshot> &aSnapshot,
9391 bool aSetError /* = false */)
9392{
9393 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9394
9395 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9396
9397 if (!mData->mFirstSnapshot)
9398 {
9399 if (aSetError)
9400 return setError(VBOX_E_OBJECT_NOT_FOUND,
9401 tr("This machine does not have any snapshots"));
9402 return VBOX_E_OBJECT_NOT_FOUND;
9403 }
9404
9405 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9406
9407 if (!aSnapshot)
9408 {
9409 if (aSetError)
9410 return setError(VBOX_E_OBJECT_NOT_FOUND,
9411 tr("Could not find a snapshot named '%s'"), strName.c_str());
9412 return VBOX_E_OBJECT_NOT_FOUND;
9413 }
9414
9415 return S_OK;
9416}
9417
9418/**
9419 * Returns a storage controller object with the given name.
9420 *
9421 * @param aName storage controller name to find
9422 * @param aStorageController where to return the found storage controller
9423 * @param aSetError true to set extended error info on failure
9424 */
9425HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9426 ComObjPtr<StorageController> &aStorageController,
9427 bool aSetError /* = false */)
9428{
9429 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9430
9431 for (StorageControllerList::const_iterator
9432 it = mStorageControllers->begin();
9433 it != mStorageControllers->end();
9434 ++it)
9435 {
9436 if ((*it)->i_getName() == aName)
9437 {
9438 aStorageController = (*it);
9439 return S_OK;
9440 }
9441 }
9442
9443 if (aSetError)
9444 return setError(VBOX_E_OBJECT_NOT_FOUND,
9445 tr("Could not find a storage controller named '%s'"),
9446 aName.c_str());
9447 return VBOX_E_OBJECT_NOT_FOUND;
9448}
9449
9450/**
9451 * Returns a USB controller object with the given name.
9452 *
9453 * @param aName USB controller name to find
9454 * @param aUSBController where to return the found USB controller
9455 * @param aSetError true to set extended error info on failure
9456 */
9457HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9458 ComObjPtr<USBController> &aUSBController,
9459 bool aSetError /* = false */)
9460{
9461 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9462
9463 for (USBControllerList::const_iterator
9464 it = mUSBControllers->begin();
9465 it != mUSBControllers->end();
9466 ++it)
9467 {
9468 if ((*it)->i_getName() == aName)
9469 {
9470 aUSBController = (*it);
9471 return S_OK;
9472 }
9473 }
9474
9475 if (aSetError)
9476 return setError(VBOX_E_OBJECT_NOT_FOUND,
9477 tr("Could not find a storage controller named '%s'"),
9478 aName.c_str());
9479 return VBOX_E_OBJECT_NOT_FOUND;
9480}
9481
9482/**
9483 * Returns the number of USB controller instance of the given type.
9484 *
9485 * @param enmType USB controller type.
9486 */
9487ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9488{
9489 ULONG cCtrls = 0;
9490
9491 for (USBControllerList::const_iterator
9492 it = mUSBControllers->begin();
9493 it != mUSBControllers->end();
9494 ++it)
9495 {
9496 if ((*it)->i_getControllerType() == enmType)
9497 cCtrls++;
9498 }
9499
9500 return cCtrls;
9501}
9502
9503HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9504 MediumAttachmentList &atts)
9505{
9506 AutoCaller autoCaller(this);
9507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9508
9509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9510
9511 for (MediumAttachmentList::const_iterator
9512 it = mMediumAttachments->begin();
9513 it != mMediumAttachments->end();
9514 ++it)
9515 {
9516 const ComObjPtr<MediumAttachment> &pAtt = *it;
9517 // should never happen, but deal with NULL pointers in the list.
9518 AssertContinue(!pAtt.isNull());
9519
9520 // getControllerName() needs caller+read lock
9521 AutoCaller autoAttCaller(pAtt);
9522 if (FAILED(autoAttCaller.rc()))
9523 {
9524 atts.clear();
9525 return autoAttCaller.rc();
9526 }
9527 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9528
9529 if (pAtt->i_getControllerName() == aName)
9530 atts.push_back(pAtt);
9531 }
9532
9533 return S_OK;
9534}
9535
9536
9537/**
9538 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9539 * file if the machine name was changed and about creating a new settings file
9540 * if this is a new machine.
9541 *
9542 * @note Must be never called directly but only from #saveSettings().
9543 */
9544HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9545{
9546 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9547
9548 HRESULT rc = S_OK;
9549
9550 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9551
9552 /// @todo need to handle primary group change, too
9553
9554 /* attempt to rename the settings file if machine name is changed */
9555 if ( mUserData->s.fNameSync
9556 && mUserData.isBackedUp()
9557 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9558 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9559 )
9560 {
9561 bool dirRenamed = false;
9562 bool fileRenamed = false;
9563
9564 Utf8Str configFile, newConfigFile;
9565 Utf8Str configFilePrev, newConfigFilePrev;
9566 Utf8Str NVRAMFile, newNVRAMFile;
9567 Utf8Str configDir, newConfigDir;
9568
9569 do
9570 {
9571 int vrc = VINF_SUCCESS;
9572
9573 Utf8Str name = mUserData.backedUpData()->s.strName;
9574 Utf8Str newName = mUserData->s.strName;
9575 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9576 if (group == "/")
9577 group.setNull();
9578 Utf8Str newGroup = mUserData->s.llGroups.front();
9579 if (newGroup == "/")
9580 newGroup.setNull();
9581
9582 configFile = mData->m_strConfigFileFull;
9583
9584 /* first, rename the directory if it matches the group and machine name */
9585 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9586 /** @todo hack, make somehow use of ComposeMachineFilename */
9587 if (mUserData->s.fDirectoryIncludesUUID)
9588 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9589 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9590 /** @todo hack, make somehow use of ComposeMachineFilename */
9591 if (mUserData->s.fDirectoryIncludesUUID)
9592 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9593 configDir = configFile;
9594 configDir.stripFilename();
9595 newConfigDir = configDir;
9596 if ( configDir.length() >= groupPlusName.length()
9597 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9598 groupPlusName.c_str()))
9599 {
9600 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9601 Utf8Str newConfigBaseDir(newConfigDir);
9602 newConfigDir.append(newGroupPlusName);
9603 /* consistency: use \ if appropriate on the platform */
9604 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9605 /* new dir and old dir cannot be equal here because of 'if'
9606 * above and because name != newName */
9607 Assert(configDir != newConfigDir);
9608 if (!fSettingsFileIsNew)
9609 {
9610 /* perform real rename only if the machine is not new */
9611 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9612 if ( vrc == VERR_FILE_NOT_FOUND
9613 || vrc == VERR_PATH_NOT_FOUND)
9614 {
9615 /* create the parent directory, then retry renaming */
9616 Utf8Str parent(newConfigDir);
9617 parent.stripFilename();
9618 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9619 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9620 }
9621 if (RT_FAILURE(vrc))
9622 {
9623 rc = setErrorBoth(E_FAIL, vrc,
9624 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9625 configDir.c_str(),
9626 newConfigDir.c_str(),
9627 vrc);
9628 break;
9629 }
9630 /* delete subdirectories which are no longer needed */
9631 Utf8Str dir(configDir);
9632 dir.stripFilename();
9633 while (dir != newConfigBaseDir && dir != ".")
9634 {
9635 vrc = RTDirRemove(dir.c_str());
9636 if (RT_FAILURE(vrc))
9637 break;
9638 dir.stripFilename();
9639 }
9640 dirRenamed = true;
9641 }
9642 }
9643
9644 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9645
9646 /* then try to rename the settings file itself */
9647 if (newConfigFile != configFile)
9648 {
9649 /* get the path to old settings file in renamed directory */
9650 Assert(mData->m_strConfigFileFull == configFile);
9651 configFile.printf("%s%c%s",
9652 newConfigDir.c_str(),
9653 RTPATH_DELIMITER,
9654 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9655 if (!fSettingsFileIsNew)
9656 {
9657 /* perform real rename only if the machine is not new */
9658 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9659 if (RT_FAILURE(vrc))
9660 {
9661 rc = setErrorBoth(E_FAIL, vrc,
9662 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9663 configFile.c_str(),
9664 newConfigFile.c_str(),
9665 vrc);
9666 break;
9667 }
9668 fileRenamed = true;
9669 configFilePrev = configFile;
9670 configFilePrev += "-prev";
9671 newConfigFilePrev = newConfigFile;
9672 newConfigFilePrev += "-prev";
9673 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9674 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9675 if (NVRAMFile.isNotEmpty())
9676 {
9677 // in the NVRAM file path, replace the old directory with the new directory
9678 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9679 {
9680 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9681 NVRAMFile = newConfigDir + strNVRAMFile;
9682 }
9683 newNVRAMFile = newConfigFile;
9684 newNVRAMFile.stripSuffix();
9685 newNVRAMFile += ".nvram";
9686 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9687 }
9688 }
9689 }
9690
9691 // update m_strConfigFileFull amd mConfigFile
9692 mData->m_strConfigFileFull = newConfigFile;
9693 // compute the relative path too
9694 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9695
9696 // store the old and new so that VirtualBox::i_saveSettings() can update
9697 // the media registry
9698 if ( mData->mRegistered
9699 && (configDir != newConfigDir || configFile != newConfigFile))
9700 {
9701 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9702
9703 if (pfNeedsGlobalSaveSettings)
9704 *pfNeedsGlobalSaveSettings = true;
9705 }
9706
9707 // in the saved state file path, replace the old directory with the new directory
9708 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9709 {
9710 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9711 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9712 }
9713 if (newNVRAMFile.isNotEmpty())
9714 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9715
9716 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9717 if (mData->mFirstSnapshot)
9718 {
9719 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9720 newConfigDir.c_str());
9721 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9722 newConfigDir.c_str());
9723 }
9724 }
9725 while (0);
9726
9727 if (FAILED(rc))
9728 {
9729 /* silently try to rename everything back */
9730 if (fileRenamed)
9731 {
9732 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9733 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9734 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9735 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9736 }
9737 if (dirRenamed)
9738 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9739 }
9740
9741 if (FAILED(rc)) return rc;
9742 }
9743
9744 if (fSettingsFileIsNew)
9745 {
9746 /* create a virgin config file */
9747 int vrc = VINF_SUCCESS;
9748
9749 /* ensure the settings directory exists */
9750 Utf8Str path(mData->m_strConfigFileFull);
9751 path.stripFilename();
9752 if (!RTDirExists(path.c_str()))
9753 {
9754 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9755 if (RT_FAILURE(vrc))
9756 {
9757 return setErrorBoth(E_FAIL, vrc,
9758 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9759 path.c_str(),
9760 vrc);
9761 }
9762 }
9763
9764 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9765 path = Utf8Str(mData->m_strConfigFileFull);
9766 RTFILE f = NIL_RTFILE;
9767 vrc = RTFileOpen(&f, path.c_str(),
9768 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9769 if (RT_FAILURE(vrc))
9770 return setErrorBoth(E_FAIL, vrc,
9771 tr("Could not create the settings file '%s' (%Rrc)"),
9772 path.c_str(),
9773 vrc);
9774 RTFileClose(f);
9775 }
9776
9777 return rc;
9778}
9779
9780/**
9781 * Saves and commits machine data, user data and hardware data.
9782 *
9783 * Note that on failure, the data remains uncommitted.
9784 *
9785 * @a aFlags may combine the following flags:
9786 *
9787 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9788 * Used when saving settings after an operation that makes them 100%
9789 * correspond to the settings from the current snapshot.
9790 * - SaveS_Force: settings will be saved without doing a deep compare of the
9791 * settings structures. This is used when this is called because snapshots
9792 * have changed to avoid the overhead of the deep compare.
9793 *
9794 * @note Must be called from under this object's write lock. Locks children for
9795 * writing.
9796 *
9797 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9798 * initialized to false and that will be set to true by this function if
9799 * the caller must invoke VirtualBox::i_saveSettings() because the global
9800 * settings have changed. This will happen if a machine rename has been
9801 * saved and the global machine and media registries will therefore need
9802 * updating.
9803 * @param aFlags Flags.
9804 */
9805HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9806 int aFlags /*= 0*/)
9807{
9808 LogFlowThisFuncEnter();
9809
9810 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9811
9812 /* make sure child objects are unable to modify the settings while we are
9813 * saving them */
9814 i_ensureNoStateDependencies();
9815
9816 AssertReturn(!i_isSnapshotMachine(),
9817 E_FAIL);
9818
9819 if (!mData->mAccessible)
9820 return setError(VBOX_E_INVALID_VM_STATE,
9821 tr("The machine is not accessible, so cannot save settings"));
9822
9823 HRESULT rc = S_OK;
9824 bool fNeedsWrite = false;
9825
9826 /* First, prepare to save settings. It will care about renaming the
9827 * settings directory and file if the machine name was changed and about
9828 * creating a new settings file if this is a new machine. */
9829 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9830 if (FAILED(rc)) return rc;
9831
9832 // keep a pointer to the current settings structures
9833 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9834 settings::MachineConfigFile *pNewConfig = NULL;
9835
9836 try
9837 {
9838 // make a fresh one to have everyone write stuff into
9839 pNewConfig = new settings::MachineConfigFile(NULL);
9840 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9841
9842 // now go and copy all the settings data from COM to the settings structures
9843 // (this calls i_saveSettings() on all the COM objects in the machine)
9844 i_copyMachineDataToSettings(*pNewConfig);
9845
9846 if (aFlags & SaveS_ResetCurStateModified)
9847 {
9848 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9849 mData->mCurrentStateModified = FALSE;
9850 fNeedsWrite = true; // always, no need to compare
9851 }
9852 else if (aFlags & SaveS_Force)
9853 {
9854 fNeedsWrite = true; // always, no need to compare
9855 }
9856 else
9857 {
9858 if (!mData->mCurrentStateModified)
9859 {
9860 // do a deep compare of the settings that we just saved with the settings
9861 // previously stored in the config file; this invokes MachineConfigFile::operator==
9862 // which does a deep compare of all the settings, which is expensive but less expensive
9863 // than writing out XML in vain
9864 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9865
9866 // could still be modified if any settings changed
9867 mData->mCurrentStateModified = fAnySettingsChanged;
9868
9869 fNeedsWrite = fAnySettingsChanged;
9870 }
9871 else
9872 fNeedsWrite = true;
9873 }
9874
9875 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9876
9877 if (fNeedsWrite)
9878 // now spit it all out!
9879 pNewConfig->write(mData->m_strConfigFileFull);
9880
9881 mData->pMachineConfigFile = pNewConfig;
9882 delete pOldConfig;
9883 i_commit();
9884
9885 // after saving settings, we are no longer different from the XML on disk
9886 mData->flModifications = 0;
9887 }
9888 catch (HRESULT err)
9889 {
9890 // we assume that error info is set by the thrower
9891 rc = err;
9892
9893 // restore old config
9894 delete pNewConfig;
9895 mData->pMachineConfigFile = pOldConfig;
9896 }
9897 catch (...)
9898 {
9899 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9900 }
9901
9902 if (fNeedsWrite)
9903 {
9904 /* Fire the data change event, even on failure (since we've already
9905 * committed all data). This is done only for SessionMachines because
9906 * mutable Machine instances are always not registered (i.e. private
9907 * to the client process that creates them) and thus don't need to
9908 * inform callbacks. */
9909 if (i_isSessionMachine())
9910 mParent->i_onMachineDataChanged(mData->mUuid);
9911 }
9912
9913 LogFlowThisFunc(("rc=%08X\n", rc));
9914 LogFlowThisFuncLeave();
9915 return rc;
9916}
9917
9918/**
9919 * Implementation for saving the machine settings into the given
9920 * settings::MachineConfigFile instance. This copies machine extradata
9921 * from the previous machine config file in the instance data, if any.
9922 *
9923 * This gets called from two locations:
9924 *
9925 * -- Machine::i_saveSettings(), during the regular XML writing;
9926 *
9927 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9928 * exported to OVF and we write the VirtualBox proprietary XML
9929 * into a <vbox:Machine> tag.
9930 *
9931 * This routine fills all the fields in there, including snapshots, *except*
9932 * for the following:
9933 *
9934 * -- fCurrentStateModified. There is some special logic associated with that.
9935 *
9936 * The caller can then call MachineConfigFile::write() or do something else
9937 * with it.
9938 *
9939 * Caller must hold the machine lock!
9940 *
9941 * This throws XML errors and HRESULT, so the caller must have a catch block!
9942 */
9943void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9944{
9945 // deep copy extradata, being extra careful with self assignment (the STL
9946 // map assignment on Mac OS X clang based Xcode isn't checking)
9947 if (&config != mData->pMachineConfigFile)
9948 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9949
9950 config.uuid = mData->mUuid;
9951
9952 // copy name, description, OS type, teleport, UTC etc.
9953 config.machineUserData = mUserData->s;
9954
9955 if ( mData->mMachineState == MachineState_Saved
9956 || mData->mMachineState == MachineState_Restoring
9957 // when doing certain snapshot operations we may or may not have
9958 // a saved state in the current state, so keep everything as is
9959 || ( ( mData->mMachineState == MachineState_Snapshotting
9960 || mData->mMachineState == MachineState_DeletingSnapshot
9961 || mData->mMachineState == MachineState_RestoringSnapshot)
9962 && (!mSSData->strStateFilePath.isEmpty())
9963 )
9964 )
9965 {
9966 Assert(!mSSData->strStateFilePath.isEmpty());
9967 /* try to make the file name relative to the settings file dir */
9968 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9969 }
9970 else
9971 {
9972 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9973 config.strStateFile.setNull();
9974 }
9975
9976 if (mData->mCurrentSnapshot)
9977 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9978 else
9979 config.uuidCurrentSnapshot.clear();
9980
9981 config.timeLastStateChange = mData->mLastStateChange;
9982 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9983 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9984
9985 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9986 if (FAILED(rc)) throw rc;
9987
9988 // save machine's media registry if this is VirtualBox 4.0 or later
9989 if (config.canHaveOwnMediaRegistry())
9990 {
9991 // determine machine folder
9992 Utf8Str strMachineFolder = i_getSettingsFileFull();
9993 strMachineFolder.stripFilename();
9994 mParent->i_saveMediaRegistry(config.mediaRegistry,
9995 i_getId(), // only media with registry ID == machine UUID
9996 strMachineFolder);
9997 // this throws HRESULT
9998 }
9999
10000 // save snapshots
10001 rc = i_saveAllSnapshots(config);
10002 if (FAILED(rc)) throw rc;
10003}
10004
10005/**
10006 * Saves all snapshots of the machine into the given machine config file. Called
10007 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10008 * @param config
10009 * @return
10010 */
10011HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10012{
10013 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10014
10015 HRESULT rc = S_OK;
10016
10017 try
10018 {
10019 config.llFirstSnapshot.clear();
10020
10021 if (mData->mFirstSnapshot)
10022 {
10023 // the settings use a list for "the first snapshot"
10024 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10025
10026 // get reference to the snapshot on the list and work on that
10027 // element straight in the list to avoid excessive copying later
10028 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10029 if (FAILED(rc)) throw rc;
10030 }
10031
10032// if (mType == IsSessionMachine)
10033// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10034
10035 }
10036 catch (HRESULT err)
10037 {
10038 /* we assume that error info is set by the thrower */
10039 rc = err;
10040 }
10041 catch (...)
10042 {
10043 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10044 }
10045
10046 return rc;
10047}
10048
10049/**
10050 * Saves the VM hardware configuration. It is assumed that the
10051 * given node is empty.
10052 *
10053 * @param data Reference to the settings object for the hardware config.
10054 * @param pDbg Pointer to the settings object for the debugging config
10055 * which happens to live in mHWData.
10056 * @param pAutostart Pointer to the settings object for the autostart config
10057 * which happens to live in mHWData.
10058 */
10059HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10060 settings::Autostart *pAutostart)
10061{
10062 HRESULT rc = S_OK;
10063
10064 try
10065 {
10066 /* The hardware version attribute (optional).
10067 Automatically upgrade from 1 to current default hardware version
10068 when there is no saved state. (ugly!) */
10069 if ( mHWData->mHWVersion == "1"
10070 && mSSData->strStateFilePath.isEmpty()
10071 )
10072 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10073
10074 data.strVersion = mHWData->mHWVersion;
10075 data.uuid = mHWData->mHardwareUUID;
10076
10077 // CPU
10078 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10079 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10080 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10081 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10082 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10083 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10084 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10085 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10086 data.fPAE = !!mHWData->mPAEEnabled;
10087 data.enmLongMode = mHWData->mLongMode;
10088 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10089 data.fAPIC = !!mHWData->mAPIC;
10090 data.fX2APIC = !!mHWData->mX2APIC;
10091 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10092 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10093 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10094 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10095 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10096 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10097 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10098 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10099 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10100 data.cCPUs = mHWData->mCPUCount;
10101 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10102 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10103 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10104 data.strCpuProfile = mHWData->mCpuProfile;
10105
10106 data.llCpus.clear();
10107 if (data.fCpuHotPlug)
10108 {
10109 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10110 {
10111 if (mHWData->mCPUAttached[idx])
10112 {
10113 settings::Cpu cpu;
10114 cpu.ulId = idx;
10115 data.llCpus.push_back(cpu);
10116 }
10117 }
10118 }
10119
10120 /* Standard and Extended CPUID leafs. */
10121 data.llCpuIdLeafs.clear();
10122 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10123
10124 // memory
10125 data.ulMemorySizeMB = mHWData->mMemorySize;
10126 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10127
10128 // firmware
10129 data.firmwareType = mHWData->mFirmwareType;
10130
10131 // HID
10132 data.pointingHIDType = mHWData->mPointingHIDType;
10133 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10134
10135 // chipset
10136 data.chipsetType = mHWData->mChipsetType;
10137
10138 // iommu
10139 data.iommuType = mHWData->mIommuType;
10140
10141 // paravirt
10142 data.paravirtProvider = mHWData->mParavirtProvider;
10143 data.strParavirtDebug = mHWData->mParavirtDebug;
10144
10145 // emulated USB card reader
10146 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10147
10148 // HPET
10149 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10150
10151 // boot order
10152 data.mapBootOrder.clear();
10153 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10154 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10155
10156 /* VRDEServer settings (optional) */
10157 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10158 if (FAILED(rc)) throw rc;
10159
10160 /* BIOS settings (required) */
10161 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10162 if (FAILED(rc)) throw rc;
10163
10164 /* Recording settings (required) */
10165 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10166 if (FAILED(rc)) throw rc;
10167
10168 /* GraphicsAdapter settings (required) */
10169 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10170 if (FAILED(rc)) throw rc;
10171
10172 /* USB Controller (required) */
10173 data.usbSettings.llUSBControllers.clear();
10174 for (USBControllerList::const_iterator
10175 it = mUSBControllers->begin();
10176 it != mUSBControllers->end();
10177 ++it)
10178 {
10179 ComObjPtr<USBController> ctrl = *it;
10180 settings::USBController settingsCtrl;
10181
10182 settingsCtrl.strName = ctrl->i_getName();
10183 settingsCtrl.enmType = ctrl->i_getControllerType();
10184
10185 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10186 }
10187
10188 /* USB device filters (required) */
10189 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10190 if (FAILED(rc)) throw rc;
10191
10192 /* Network adapters (required) */
10193 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10194 data.llNetworkAdapters.clear();
10195 /* Write out only the nominal number of network adapters for this
10196 * chipset type. Since Machine::commit() hasn't been called there
10197 * may be extra NIC settings in the vector. */
10198 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10199 {
10200 settings::NetworkAdapter nic;
10201 nic.ulSlot = (uint32_t)slot;
10202 /* paranoia check... must not be NULL, but must not crash either. */
10203 if (mNetworkAdapters[slot])
10204 {
10205 if (mNetworkAdapters[slot]->i_hasDefaults())
10206 continue;
10207
10208 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10209 if (FAILED(rc)) throw rc;
10210
10211 data.llNetworkAdapters.push_back(nic);
10212 }
10213 }
10214
10215 /* Serial ports */
10216 data.llSerialPorts.clear();
10217 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10218 {
10219 if (mSerialPorts[slot]->i_hasDefaults())
10220 continue;
10221
10222 settings::SerialPort s;
10223 s.ulSlot = slot;
10224 rc = mSerialPorts[slot]->i_saveSettings(s);
10225 if (FAILED(rc)) return rc;
10226
10227 data.llSerialPorts.push_back(s);
10228 }
10229
10230 /* Parallel ports */
10231 data.llParallelPorts.clear();
10232 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10233 {
10234 if (mParallelPorts[slot]->i_hasDefaults())
10235 continue;
10236
10237 settings::ParallelPort p;
10238 p.ulSlot = slot;
10239 rc = mParallelPorts[slot]->i_saveSettings(p);
10240 if (FAILED(rc)) return rc;
10241
10242 data.llParallelPorts.push_back(p);
10243 }
10244
10245 /* Audio adapter */
10246 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10247 if (FAILED(rc)) return rc;
10248
10249 rc = i_saveStorageControllers(data.storage);
10250 if (FAILED(rc)) return rc;
10251
10252 /* Shared folders */
10253 data.llSharedFolders.clear();
10254 for (HWData::SharedFolderList::const_iterator
10255 it = mHWData->mSharedFolders.begin();
10256 it != mHWData->mSharedFolders.end();
10257 ++it)
10258 {
10259 SharedFolder *pSF = *it;
10260 AutoCaller sfCaller(pSF);
10261 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10262 settings::SharedFolder sf;
10263 sf.strName = pSF->i_getName();
10264 sf.strHostPath = pSF->i_getHostPath();
10265 sf.fWritable = !!pSF->i_isWritable();
10266 sf.fAutoMount = !!pSF->i_isAutoMounted();
10267 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10268
10269 data.llSharedFolders.push_back(sf);
10270 }
10271
10272 // clipboard
10273 data.clipboardMode = mHWData->mClipboardMode;
10274 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10275
10276 // drag'n'drop
10277 data.dndMode = mHWData->mDnDMode;
10278
10279 /* Guest */
10280 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10281
10282 // IO settings
10283 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10284 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10285
10286 /* BandwidthControl (required) */
10287 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10288 if (FAILED(rc)) throw rc;
10289
10290 /* Host PCI devices */
10291 data.pciAttachments.clear();
10292 for (HWData::PCIDeviceAssignmentList::const_iterator
10293 it = mHWData->mPCIDeviceAssignments.begin();
10294 it != mHWData->mPCIDeviceAssignments.end();
10295 ++it)
10296 {
10297 ComObjPtr<PCIDeviceAttachment> pda = *it;
10298 settings::HostPCIDeviceAttachment hpda;
10299
10300 rc = pda->i_saveSettings(hpda);
10301 if (FAILED(rc)) throw rc;
10302
10303 data.pciAttachments.push_back(hpda);
10304 }
10305
10306 // guest properties
10307 data.llGuestProperties.clear();
10308#ifdef VBOX_WITH_GUEST_PROPS
10309 for (HWData::GuestPropertyMap::const_iterator
10310 it = mHWData->mGuestProperties.begin();
10311 it != mHWData->mGuestProperties.end();
10312 ++it)
10313 {
10314 HWData::GuestProperty property = it->second;
10315
10316 /* Remove transient guest properties at shutdown unless we
10317 * are saving state. Note that restoring snapshot intentionally
10318 * keeps them, they will be removed if appropriate once the final
10319 * machine state is set (as crashes etc. need to work). */
10320 if ( ( mData->mMachineState == MachineState_PoweredOff
10321 || mData->mMachineState == MachineState_Aborted
10322 || mData->mMachineState == MachineState_Teleported)
10323 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10324 continue;
10325 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10326 prop.strName = it->first;
10327 prop.strValue = property.strValue;
10328 prop.timestamp = (uint64_t)property.mTimestamp;
10329 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10330 GuestPropWriteFlags(property.mFlags, szFlags);
10331 prop.strFlags = szFlags;
10332
10333 data.llGuestProperties.push_back(prop);
10334 }
10335
10336 /* I presume this doesn't require a backup(). */
10337 mData->mGuestPropertiesModified = FALSE;
10338#endif /* VBOX_WITH_GUEST_PROPS defined */
10339
10340 *pDbg = mHWData->mDebugging;
10341 *pAutostart = mHWData->mAutostart;
10342
10343 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10344 }
10345 catch (std::bad_alloc &)
10346 {
10347 return E_OUTOFMEMORY;
10348 }
10349
10350 AssertComRC(rc);
10351 return rc;
10352}
10353
10354/**
10355 * Saves the storage controller configuration.
10356 *
10357 * @param data storage settings.
10358 */
10359HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10360{
10361 data.llStorageControllers.clear();
10362
10363 for (StorageControllerList::const_iterator
10364 it = mStorageControllers->begin();
10365 it != mStorageControllers->end();
10366 ++it)
10367 {
10368 HRESULT rc;
10369 ComObjPtr<StorageController> pCtl = *it;
10370
10371 settings::StorageController ctl;
10372 ctl.strName = pCtl->i_getName();
10373 ctl.controllerType = pCtl->i_getControllerType();
10374 ctl.storageBus = pCtl->i_getStorageBus();
10375 ctl.ulInstance = pCtl->i_getInstance();
10376 ctl.fBootable = pCtl->i_getBootable();
10377
10378 /* Save the port count. */
10379 ULONG portCount;
10380 rc = pCtl->COMGETTER(PortCount)(&portCount);
10381 ComAssertComRCRet(rc, rc);
10382 ctl.ulPortCount = portCount;
10383
10384 /* Save fUseHostIOCache */
10385 BOOL fUseHostIOCache;
10386 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10387 ComAssertComRCRet(rc, rc);
10388 ctl.fUseHostIOCache = !!fUseHostIOCache;
10389
10390 /* save the devices now. */
10391 rc = i_saveStorageDevices(pCtl, ctl);
10392 ComAssertComRCRet(rc, rc);
10393
10394 data.llStorageControllers.push_back(ctl);
10395 }
10396
10397 return S_OK;
10398}
10399
10400/**
10401 * Saves the hard disk configuration.
10402 */
10403HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10404 settings::StorageController &data)
10405{
10406 MediumAttachmentList atts;
10407
10408 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10409 if (FAILED(rc)) return rc;
10410
10411 data.llAttachedDevices.clear();
10412 for (MediumAttachmentList::const_iterator
10413 it = atts.begin();
10414 it != atts.end();
10415 ++it)
10416 {
10417 settings::AttachedDevice dev;
10418 IMediumAttachment *iA = *it;
10419 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10420 Medium *pMedium = pAttach->i_getMedium();
10421
10422 dev.deviceType = pAttach->i_getType();
10423 dev.lPort = pAttach->i_getPort();
10424 dev.lDevice = pAttach->i_getDevice();
10425 dev.fPassThrough = pAttach->i_getPassthrough();
10426 dev.fHotPluggable = pAttach->i_getHotPluggable();
10427 if (pMedium)
10428 {
10429 if (pMedium->i_isHostDrive())
10430 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10431 else
10432 dev.uuid = pMedium->i_getId();
10433 dev.fTempEject = pAttach->i_getTempEject();
10434 dev.fNonRotational = pAttach->i_getNonRotational();
10435 dev.fDiscard = pAttach->i_getDiscard();
10436 }
10437
10438 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10439
10440 data.llAttachedDevices.push_back(dev);
10441 }
10442
10443 return S_OK;
10444}
10445
10446/**
10447 * Saves machine state settings as defined by aFlags
10448 * (SaveSTS_* values).
10449 *
10450 * @param aFlags Combination of SaveSTS_* flags.
10451 *
10452 * @note Locks objects for writing.
10453 */
10454HRESULT Machine::i_saveStateSettings(int aFlags)
10455{
10456 if (aFlags == 0)
10457 return S_OK;
10458
10459 AutoCaller autoCaller(this);
10460 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10461
10462 /* This object's write lock is also necessary to serialize file access
10463 * (prevent concurrent reads and writes) */
10464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10465
10466 HRESULT rc = S_OK;
10467
10468 Assert(mData->pMachineConfigFile);
10469
10470 try
10471 {
10472 if (aFlags & SaveSTS_CurStateModified)
10473 mData->pMachineConfigFile->fCurrentStateModified = true;
10474
10475 if (aFlags & SaveSTS_StateFilePath)
10476 {
10477 if (!mSSData->strStateFilePath.isEmpty())
10478 /* try to make the file name relative to the settings file dir */
10479 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10480 else
10481 mData->pMachineConfigFile->strStateFile.setNull();
10482 }
10483
10484 if (aFlags & SaveSTS_StateTimeStamp)
10485 {
10486 Assert( mData->mMachineState != MachineState_Aborted
10487 || mSSData->strStateFilePath.isEmpty());
10488
10489 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10490
10491 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10492/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10493 }
10494
10495 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10496 }
10497 catch (...)
10498 {
10499 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10500 }
10501
10502 return rc;
10503}
10504
10505/**
10506 * Ensures that the given medium is added to a media registry. If this machine
10507 * was created with 4.0 or later, then the machine registry is used. Otherwise
10508 * the global VirtualBox media registry is used.
10509 *
10510 * Caller must NOT hold machine lock, media tree or any medium locks!
10511 *
10512 * @param pMedium
10513 */
10514void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10515{
10516 /* Paranoia checks: do not hold machine or media tree locks. */
10517 AssertReturnVoid(!isWriteLockOnCurrentThread());
10518 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10519
10520 ComObjPtr<Medium> pBase;
10521 {
10522 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10523 pBase = pMedium->i_getBase();
10524 }
10525
10526 /* Paranoia checks: do not hold medium locks. */
10527 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10528 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10529
10530 // decide which medium registry to use now that the medium is attached:
10531 Guid uuid;
10532 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10533 if (fCanHaveOwnMediaRegistry)
10534 // machine XML is VirtualBox 4.0 or higher:
10535 uuid = i_getId(); // machine UUID
10536 else
10537 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10538
10539 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10540 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10541 if (pMedium->i_addRegistry(uuid))
10542 mParent->i_markRegistryModified(uuid);
10543
10544 /* For more complex hard disk structures it can happen that the base
10545 * medium isn't yet associated with any medium registry. Do that now. */
10546 if (pMedium != pBase)
10547 {
10548 /* Tree lock needed by Medium::addRegistry when recursing. */
10549 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10550 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10551 {
10552 treeLock.release();
10553 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10554 treeLock.acquire();
10555 }
10556 if (pBase->i_addRegistryRecursive(uuid))
10557 {
10558 treeLock.release();
10559 mParent->i_markRegistryModified(uuid);
10560 }
10561 }
10562}
10563
10564/**
10565 * Creates differencing hard disks for all normal hard disks attached to this
10566 * machine and a new set of attachments to refer to created disks.
10567 *
10568 * Used when taking a snapshot or when deleting the current state. Gets called
10569 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10570 *
10571 * This method assumes that mMediumAttachments contains the original hard disk
10572 * attachments it needs to create diffs for. On success, these attachments will
10573 * be replaced with the created diffs.
10574 *
10575 * Attachments with non-normal hard disks are left as is.
10576 *
10577 * If @a aOnline is @c false then the original hard disks that require implicit
10578 * diffs will be locked for reading. Otherwise it is assumed that they are
10579 * already locked for writing (when the VM was started). Note that in the latter
10580 * case it is responsibility of the caller to lock the newly created diffs for
10581 * writing if this method succeeds.
10582 *
10583 * @param aProgress Progress object to run (must contain at least as
10584 * many operations left as the number of hard disks
10585 * attached).
10586 * @param aWeight Weight of this operation.
10587 * @param aOnline Whether the VM was online prior to this operation.
10588 *
10589 * @note The progress object is not marked as completed, neither on success nor
10590 * on failure. This is a responsibility of the caller.
10591 *
10592 * @note Locks this object and the media tree for writing.
10593 */
10594HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10595 ULONG aWeight,
10596 bool aOnline)
10597{
10598 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10599
10600 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10601 AssertReturn(!!pProgressControl, E_INVALIDARG);
10602
10603 AutoCaller autoCaller(this);
10604 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10605
10606 AutoMultiWriteLock2 alock(this->lockHandle(),
10607 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10608
10609 /* must be in a protective state because we release the lock below */
10610 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10611 || mData->mMachineState == MachineState_OnlineSnapshotting
10612 || mData->mMachineState == MachineState_LiveSnapshotting
10613 || mData->mMachineState == MachineState_RestoringSnapshot
10614 || mData->mMachineState == MachineState_DeletingSnapshot
10615 , E_FAIL);
10616
10617 HRESULT rc = S_OK;
10618
10619 // use appropriate locked media map (online or offline)
10620 MediumLockListMap lockedMediaOffline;
10621 MediumLockListMap *lockedMediaMap;
10622 if (aOnline)
10623 lockedMediaMap = &mData->mSession.mLockedMedia;
10624 else
10625 lockedMediaMap = &lockedMediaOffline;
10626
10627 try
10628 {
10629 if (!aOnline)
10630 {
10631 /* lock all attached hard disks early to detect "in use"
10632 * situations before creating actual diffs */
10633 for (MediumAttachmentList::const_iterator
10634 it = mMediumAttachments->begin();
10635 it != mMediumAttachments->end();
10636 ++it)
10637 {
10638 MediumAttachment *pAtt = *it;
10639 if (pAtt->i_getType() == DeviceType_HardDisk)
10640 {
10641 Medium *pMedium = pAtt->i_getMedium();
10642 Assert(pMedium);
10643
10644 MediumLockList *pMediumLockList(new MediumLockList());
10645 alock.release();
10646 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10647 NULL /* pToLockWrite */,
10648 false /* fMediumLockWriteAll */,
10649 NULL,
10650 *pMediumLockList);
10651 alock.acquire();
10652 if (FAILED(rc))
10653 {
10654 delete pMediumLockList;
10655 throw rc;
10656 }
10657 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10658 if (FAILED(rc))
10659 {
10660 throw setError(rc,
10661 tr("Collecting locking information for all attached media failed"));
10662 }
10663 }
10664 }
10665
10666 /* Now lock all media. If this fails, nothing is locked. */
10667 alock.release();
10668 rc = lockedMediaMap->Lock();
10669 alock.acquire();
10670 if (FAILED(rc))
10671 {
10672 throw setError(rc,
10673 tr("Locking of attached media failed"));
10674 }
10675 }
10676
10677 /* remember the current list (note that we don't use backup() since
10678 * mMediumAttachments may be already backed up) */
10679 MediumAttachmentList atts = *mMediumAttachments.data();
10680
10681 /* start from scratch */
10682 mMediumAttachments->clear();
10683
10684 /* go through remembered attachments and create diffs for normal hard
10685 * disks and attach them */
10686 for (MediumAttachmentList::const_iterator
10687 it = atts.begin();
10688 it != atts.end();
10689 ++it)
10690 {
10691 MediumAttachment *pAtt = *it;
10692
10693 DeviceType_T devType = pAtt->i_getType();
10694 Medium *pMedium = pAtt->i_getMedium();
10695
10696 if ( devType != DeviceType_HardDisk
10697 || pMedium == NULL
10698 || pMedium->i_getType() != MediumType_Normal)
10699 {
10700 /* copy the attachment as is */
10701
10702 /** @todo the progress object created in SessionMachine::TakeSnaphot
10703 * only expects operations for hard disks. Later other
10704 * device types need to show up in the progress as well. */
10705 if (devType == DeviceType_HardDisk)
10706 {
10707 if (pMedium == NULL)
10708 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10709 aWeight); // weight
10710 else
10711 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10712 pMedium->i_getBase()->i_getName().c_str()).raw(),
10713 aWeight); // weight
10714 }
10715
10716 mMediumAttachments->push_back(pAtt);
10717 continue;
10718 }
10719
10720 /* need a diff */
10721 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10722 pMedium->i_getBase()->i_getName().c_str()).raw(),
10723 aWeight); // weight
10724
10725 Utf8Str strFullSnapshotFolder;
10726 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10727
10728 ComObjPtr<Medium> diff;
10729 diff.createObject();
10730 // store the diff in the same registry as the parent
10731 // (this cannot fail here because we can't create implicit diffs for
10732 // unregistered images)
10733 Guid uuidRegistryParent;
10734 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10735 Assert(fInRegistry); NOREF(fInRegistry);
10736 rc = diff->init(mParent,
10737 pMedium->i_getPreferredDiffFormat(),
10738 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10739 uuidRegistryParent,
10740 DeviceType_HardDisk);
10741 if (FAILED(rc)) throw rc;
10742
10743 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10744 * the push_back? Looks like we're going to release medium with the
10745 * wrong kind of lock (general issue with if we fail anywhere at all)
10746 * and an orphaned VDI in the snapshots folder. */
10747
10748 /* update the appropriate lock list */
10749 MediumLockList *pMediumLockList;
10750 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10751 AssertComRCThrowRC(rc);
10752 if (aOnline)
10753 {
10754 alock.release();
10755 /* The currently attached medium will be read-only, change
10756 * the lock type to read. */
10757 rc = pMediumLockList->Update(pMedium, false);
10758 alock.acquire();
10759 AssertComRCThrowRC(rc);
10760 }
10761
10762 /* release the locks before the potentially lengthy operation */
10763 alock.release();
10764 rc = pMedium->i_createDiffStorage(diff,
10765 pMedium->i_getPreferredDiffVariant(),
10766 pMediumLockList,
10767 NULL /* aProgress */,
10768 true /* aWait */,
10769 false /* aNotify */);
10770 alock.acquire();
10771 if (FAILED(rc)) throw rc;
10772
10773 /* actual lock list update is done in Machine::i_commitMedia */
10774
10775 rc = diff->i_addBackReference(mData->mUuid);
10776 AssertComRCThrowRC(rc);
10777
10778 /* add a new attachment */
10779 ComObjPtr<MediumAttachment> attachment;
10780 attachment.createObject();
10781 rc = attachment->init(this,
10782 diff,
10783 pAtt->i_getControllerName(),
10784 pAtt->i_getPort(),
10785 pAtt->i_getDevice(),
10786 DeviceType_HardDisk,
10787 true /* aImplicit */,
10788 false /* aPassthrough */,
10789 false /* aTempEject */,
10790 pAtt->i_getNonRotational(),
10791 pAtt->i_getDiscard(),
10792 pAtt->i_getHotPluggable(),
10793 pAtt->i_getBandwidthGroup());
10794 if (FAILED(rc)) throw rc;
10795
10796 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10797 AssertComRCThrowRC(rc);
10798 mMediumAttachments->push_back(attachment);
10799 }
10800 }
10801 catch (HRESULT aRC) { rc = aRC; }
10802
10803 /* unlock all hard disks we locked when there is no VM */
10804 if (!aOnline)
10805 {
10806 ErrorInfoKeeper eik;
10807
10808 HRESULT rc1 = lockedMediaMap->Clear();
10809 AssertComRC(rc1);
10810 }
10811
10812 return rc;
10813}
10814
10815/**
10816 * Deletes implicit differencing hard disks created either by
10817 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10818 * mMediumAttachments.
10819 *
10820 * Note that to delete hard disks created by #attachDevice() this method is
10821 * called from #i_rollbackMedia() when the changes are rolled back.
10822 *
10823 * @note Locks this object and the media tree for writing.
10824 */
10825HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10826{
10827 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10828
10829 AutoCaller autoCaller(this);
10830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10831
10832 AutoMultiWriteLock2 alock(this->lockHandle(),
10833 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10834
10835 /* We absolutely must have backed up state. */
10836 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10837
10838 /* Check if there are any implicitly created diff images. */
10839 bool fImplicitDiffs = false;
10840 for (MediumAttachmentList::const_iterator
10841 it = mMediumAttachments->begin();
10842 it != mMediumAttachments->end();
10843 ++it)
10844 {
10845 const ComObjPtr<MediumAttachment> &pAtt = *it;
10846 if (pAtt->i_isImplicit())
10847 {
10848 fImplicitDiffs = true;
10849 break;
10850 }
10851 }
10852 /* If there is nothing to do, leave early. This saves lots of image locking
10853 * effort. It also avoids a MachineStateChanged event without real reason.
10854 * This is important e.g. when loading a VM config, because there should be
10855 * no events. Otherwise API clients can become thoroughly confused for
10856 * inaccessible VMs (the code for loading VM configs uses this method for
10857 * cleanup if the config makes no sense), as they take such events as an
10858 * indication that the VM is alive, and they would force the VM config to
10859 * be reread, leading to an endless loop. */
10860 if (!fImplicitDiffs)
10861 return S_OK;
10862
10863 HRESULT rc = S_OK;
10864 MachineState_T oldState = mData->mMachineState;
10865
10866 /* will release the lock before the potentially lengthy operation,
10867 * so protect with the special state (unless already protected) */
10868 if ( oldState != MachineState_Snapshotting
10869 && oldState != MachineState_OnlineSnapshotting
10870 && oldState != MachineState_LiveSnapshotting
10871 && oldState != MachineState_RestoringSnapshot
10872 && oldState != MachineState_DeletingSnapshot
10873 && oldState != MachineState_DeletingSnapshotOnline
10874 && oldState != MachineState_DeletingSnapshotPaused
10875 )
10876 i_setMachineState(MachineState_SettingUp);
10877
10878 // use appropriate locked media map (online or offline)
10879 MediumLockListMap lockedMediaOffline;
10880 MediumLockListMap *lockedMediaMap;
10881 if (aOnline)
10882 lockedMediaMap = &mData->mSession.mLockedMedia;
10883 else
10884 lockedMediaMap = &lockedMediaOffline;
10885
10886 try
10887 {
10888 if (!aOnline)
10889 {
10890 /* lock all attached hard disks early to detect "in use"
10891 * situations before deleting actual diffs */
10892 for (MediumAttachmentList::const_iterator
10893 it = mMediumAttachments->begin();
10894 it != mMediumAttachments->end();
10895 ++it)
10896 {
10897 MediumAttachment *pAtt = *it;
10898 if (pAtt->i_getType() == DeviceType_HardDisk)
10899 {
10900 Medium *pMedium = pAtt->i_getMedium();
10901 Assert(pMedium);
10902
10903 MediumLockList *pMediumLockList(new MediumLockList());
10904 alock.release();
10905 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10906 NULL /* pToLockWrite */,
10907 false /* fMediumLockWriteAll */,
10908 NULL,
10909 *pMediumLockList);
10910 alock.acquire();
10911
10912 if (FAILED(rc))
10913 {
10914 delete pMediumLockList;
10915 throw rc;
10916 }
10917
10918 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10919 if (FAILED(rc))
10920 throw rc;
10921 }
10922 }
10923
10924 if (FAILED(rc))
10925 throw rc;
10926 } // end of offline
10927
10928 /* Lock lists are now up to date and include implicitly created media */
10929
10930 /* Go through remembered attachments and delete all implicitly created
10931 * diffs and fix up the attachment information */
10932 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10933 MediumAttachmentList implicitAtts;
10934 for (MediumAttachmentList::const_iterator
10935 it = mMediumAttachments->begin();
10936 it != mMediumAttachments->end();
10937 ++it)
10938 {
10939 ComObjPtr<MediumAttachment> pAtt = *it;
10940 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10941 if (pMedium.isNull())
10942 continue;
10943
10944 // Implicit attachments go on the list for deletion and back references are removed.
10945 if (pAtt->i_isImplicit())
10946 {
10947 /* Deassociate and mark for deletion */
10948 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10949 rc = pMedium->i_removeBackReference(mData->mUuid);
10950 if (FAILED(rc))
10951 throw rc;
10952 implicitAtts.push_back(pAtt);
10953 continue;
10954 }
10955
10956 /* Was this medium attached before? */
10957 if (!i_findAttachment(oldAtts, pMedium))
10958 {
10959 /* no: de-associate */
10960 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10961 rc = pMedium->i_removeBackReference(mData->mUuid);
10962 if (FAILED(rc))
10963 throw rc;
10964 continue;
10965 }
10966 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10967 }
10968
10969 /* If there are implicit attachments to delete, throw away the lock
10970 * map contents (which will unlock all media) since the medium
10971 * attachments will be rolled back. Below we need to completely
10972 * recreate the lock map anyway since it is infinitely complex to
10973 * do this incrementally (would need reconstructing each attachment
10974 * change, which would be extremely hairy). */
10975 if (implicitAtts.size() != 0)
10976 {
10977 ErrorInfoKeeper eik;
10978
10979 HRESULT rc1 = lockedMediaMap->Clear();
10980 AssertComRC(rc1);
10981 }
10982
10983 /* rollback hard disk changes */
10984 mMediumAttachments.rollback();
10985
10986 MultiResult mrc(S_OK);
10987
10988 // Delete unused implicit diffs.
10989 if (implicitAtts.size() != 0)
10990 {
10991 alock.release();
10992
10993 for (MediumAttachmentList::const_iterator
10994 it = implicitAtts.begin();
10995 it != implicitAtts.end();
10996 ++it)
10997 {
10998 // Remove medium associated with this attachment.
10999 ComObjPtr<MediumAttachment> pAtt = *it;
11000 Assert(pAtt);
11001 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11002 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11003 Assert(pMedium);
11004
11005 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11006 // continue on delete failure, just collect error messages
11007 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11008 pMedium->i_getLocationFull().c_str() ));
11009 mrc = rc;
11010 }
11011 // Clear the list of deleted implicit attachments now, while not
11012 // holding the lock, as it will ultimately trigger Medium::uninit()
11013 // calls which assume that the media tree lock isn't held.
11014 implicitAtts.clear();
11015
11016 alock.acquire();
11017
11018 /* if there is a VM recreate media lock map as mentioned above,
11019 * otherwise it is a waste of time and we leave things unlocked */
11020 if (aOnline)
11021 {
11022 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11023 /* must never be NULL, but better safe than sorry */
11024 if (!pMachine.isNull())
11025 {
11026 alock.release();
11027 rc = mData->mSession.mMachine->i_lockMedia();
11028 alock.acquire();
11029 if (FAILED(rc))
11030 throw rc;
11031 }
11032 }
11033 }
11034 }
11035 catch (HRESULT aRC) {rc = aRC;}
11036
11037 if (mData->mMachineState == MachineState_SettingUp)
11038 i_setMachineState(oldState);
11039
11040 /* unlock all hard disks we locked when there is no VM */
11041 if (!aOnline)
11042 {
11043 ErrorInfoKeeper eik;
11044
11045 HRESULT rc1 = lockedMediaMap->Clear();
11046 AssertComRC(rc1);
11047 }
11048
11049 return rc;
11050}
11051
11052
11053/**
11054 * Looks through the given list of media attachments for one with the given parameters
11055 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11056 * can be searched as well if needed.
11057 *
11058 * @param ll
11059 * @param aControllerName
11060 * @param aControllerPort
11061 * @param aDevice
11062 * @return
11063 */
11064MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11065 const Utf8Str &aControllerName,
11066 LONG aControllerPort,
11067 LONG aDevice)
11068{
11069 for (MediumAttachmentList::const_iterator
11070 it = ll.begin();
11071 it != ll.end();
11072 ++it)
11073 {
11074 MediumAttachment *pAttach = *it;
11075 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11076 return pAttach;
11077 }
11078
11079 return NULL;
11080}
11081
11082/**
11083 * Looks through the given list of media attachments for one with the given parameters
11084 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11085 * can be searched as well if needed.
11086 *
11087 * @param ll
11088 * @param pMedium
11089 * @return
11090 */
11091MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11092 ComObjPtr<Medium> pMedium)
11093{
11094 for (MediumAttachmentList::const_iterator
11095 it = ll.begin();
11096 it != ll.end();
11097 ++it)
11098 {
11099 MediumAttachment *pAttach = *it;
11100 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11101 if (pMediumThis == pMedium)
11102 return pAttach;
11103 }
11104
11105 return NULL;
11106}
11107
11108/**
11109 * Looks through the given list of media attachments for one with the given parameters
11110 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11111 * can be searched as well if needed.
11112 *
11113 * @param ll
11114 * @param id
11115 * @return
11116 */
11117MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11118 Guid &id)
11119{
11120 for (MediumAttachmentList::const_iterator
11121 it = ll.begin();
11122 it != ll.end();
11123 ++it)
11124 {
11125 MediumAttachment *pAttach = *it;
11126 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11127 if (pMediumThis->i_getId() == id)
11128 return pAttach;
11129 }
11130
11131 return NULL;
11132}
11133
11134/**
11135 * Main implementation for Machine::DetachDevice. This also gets called
11136 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11137 *
11138 * @param pAttach Medium attachment to detach.
11139 * @param writeLock Machine write lock which the caller must have locked once.
11140 * This may be released temporarily in here.
11141 * @param pSnapshot If NULL, then the detachment is for the current machine.
11142 * Otherwise this is for a SnapshotMachine, and this must be
11143 * its snapshot.
11144 * @return
11145 */
11146HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11147 AutoWriteLock &writeLock,
11148 Snapshot *pSnapshot)
11149{
11150 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11151 DeviceType_T mediumType = pAttach->i_getType();
11152
11153 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11154
11155 if (pAttach->i_isImplicit())
11156 {
11157 /* attempt to implicitly delete the implicitly created diff */
11158
11159 /// @todo move the implicit flag from MediumAttachment to Medium
11160 /// and forbid any hard disk operation when it is implicit. Or maybe
11161 /// a special media state for it to make it even more simple.
11162
11163 Assert(mMediumAttachments.isBackedUp());
11164
11165 /* will release the lock before the potentially lengthy operation, so
11166 * protect with the special state */
11167 MachineState_T oldState = mData->mMachineState;
11168 i_setMachineState(MachineState_SettingUp);
11169
11170 writeLock.release();
11171
11172 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11173 true /*aWait*/,
11174 false /*aNotify*/);
11175
11176 writeLock.acquire();
11177
11178 i_setMachineState(oldState);
11179
11180 if (FAILED(rc)) return rc;
11181 }
11182
11183 i_setModified(IsModified_Storage);
11184 mMediumAttachments.backup();
11185 mMediumAttachments->remove(pAttach);
11186
11187 if (!oldmedium.isNull())
11188 {
11189 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11190 if (pSnapshot)
11191 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11192 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11193 else if (mediumType != DeviceType_HardDisk)
11194 oldmedium->i_removeBackReference(mData->mUuid);
11195 }
11196
11197 return S_OK;
11198}
11199
11200/**
11201 * Goes thru all media of the given list and
11202 *
11203 * 1) calls i_detachDevice() on each of them for this machine and
11204 * 2) adds all Medium objects found in the process to the given list,
11205 * depending on cleanupMode.
11206 *
11207 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11208 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11209 * media to the list.
11210 *
11211 * This gets called from Machine::Unregister, both for the actual Machine and
11212 * the SnapshotMachine objects that might be found in the snapshots.
11213 *
11214 * Requires caller and locking. The machine lock must be passed in because it
11215 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11216 *
11217 * @param writeLock Machine lock from top-level caller; this gets passed to
11218 * i_detachDevice.
11219 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11220 * object if called for a SnapshotMachine.
11221 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11222 * added to llMedia; if Full, then all media get added;
11223 * otherwise no media get added.
11224 * @param llMedia Caller's list to receive Medium objects which got detached so
11225 * caller can close() them, depending on cleanupMode.
11226 * @return
11227 */
11228HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11229 Snapshot *pSnapshot,
11230 CleanupMode_T cleanupMode,
11231 MediaList &llMedia)
11232{
11233 Assert(isWriteLockOnCurrentThread());
11234
11235 HRESULT rc;
11236
11237 // make a temporary list because i_detachDevice invalidates iterators into
11238 // mMediumAttachments
11239 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11240
11241 for (MediumAttachmentList::iterator
11242 it = llAttachments2.begin();
11243 it != llAttachments2.end();
11244 ++it)
11245 {
11246 ComObjPtr<MediumAttachment> &pAttach = *it;
11247 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11248
11249 if (!pMedium.isNull())
11250 {
11251 AutoCaller mac(pMedium);
11252 if (FAILED(mac.rc())) return mac.rc();
11253 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11254 DeviceType_T devType = pMedium->i_getDeviceType();
11255 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11256 && devType == DeviceType_HardDisk)
11257 || (cleanupMode == CleanupMode_Full)
11258 )
11259 {
11260 llMedia.push_back(pMedium);
11261 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11262 /* Not allowed to keep this lock as below we need the parent
11263 * medium lock, and the lock order is parent to child. */
11264 lock.release();
11265 /*
11266 * Search for medias which are not attached to any machine, but
11267 * in the chain to an attached disk. Mediums are only consided
11268 * if they are:
11269 * - have only one child
11270 * - no references to any machines
11271 * - are of normal medium type
11272 */
11273 while (!pParent.isNull())
11274 {
11275 AutoCaller mac1(pParent);
11276 if (FAILED(mac1.rc())) return mac1.rc();
11277 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11278 if (pParent->i_getChildren().size() == 1)
11279 {
11280 if ( pParent->i_getMachineBackRefCount() == 0
11281 && pParent->i_getType() == MediumType_Normal
11282 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11283 llMedia.push_back(pParent);
11284 }
11285 else
11286 break;
11287 pParent = pParent->i_getParent();
11288 }
11289 }
11290 }
11291
11292 // real machine: then we need to use the proper method
11293 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11294
11295 if (FAILED(rc))
11296 return rc;
11297 }
11298
11299 return S_OK;
11300}
11301
11302/**
11303 * Perform deferred hard disk detachments.
11304 *
11305 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11306 * changed (not backed up).
11307 *
11308 * If @a aOnline is @c true then this method will also unlock the old hard
11309 * disks for which the new implicit diffs were created and will lock these new
11310 * diffs for writing.
11311 *
11312 * @param aOnline Whether the VM was online prior to this operation.
11313 *
11314 * @note Locks this object for writing!
11315 */
11316void Machine::i_commitMedia(bool aOnline /*= false*/)
11317{
11318 AutoCaller autoCaller(this);
11319 AssertComRCReturnVoid(autoCaller.rc());
11320
11321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11322
11323 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11324
11325 HRESULT rc = S_OK;
11326
11327 /* no attach/detach operations -- nothing to do */
11328 if (!mMediumAttachments.isBackedUp())
11329 return;
11330
11331 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11332 bool fMediaNeedsLocking = false;
11333
11334 /* enumerate new attachments */
11335 for (MediumAttachmentList::const_iterator
11336 it = mMediumAttachments->begin();
11337 it != mMediumAttachments->end();
11338 ++it)
11339 {
11340 MediumAttachment *pAttach = *it;
11341
11342 pAttach->i_commit();
11343
11344 Medium *pMedium = pAttach->i_getMedium();
11345 bool fImplicit = pAttach->i_isImplicit();
11346
11347 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11348 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11349 fImplicit));
11350
11351 /** @todo convert all this Machine-based voodoo to MediumAttachment
11352 * based commit logic. */
11353 if (fImplicit)
11354 {
11355 /* convert implicit attachment to normal */
11356 pAttach->i_setImplicit(false);
11357
11358 if ( aOnline
11359 && pMedium
11360 && pAttach->i_getType() == DeviceType_HardDisk
11361 )
11362 {
11363 /* update the appropriate lock list */
11364 MediumLockList *pMediumLockList;
11365 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11366 AssertComRC(rc);
11367 if (pMediumLockList)
11368 {
11369 /* unlock if there's a need to change the locking */
11370 if (!fMediaNeedsLocking)
11371 {
11372 rc = mData->mSession.mLockedMedia.Unlock();
11373 AssertComRC(rc);
11374 fMediaNeedsLocking = true;
11375 }
11376 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11377 AssertComRC(rc);
11378 rc = pMediumLockList->Append(pMedium, true);
11379 AssertComRC(rc);
11380 }
11381 }
11382
11383 continue;
11384 }
11385
11386 if (pMedium)
11387 {
11388 /* was this medium attached before? */
11389 for (MediumAttachmentList::iterator
11390 oldIt = oldAtts.begin();
11391 oldIt != oldAtts.end();
11392 ++oldIt)
11393 {
11394 MediumAttachment *pOldAttach = *oldIt;
11395 if (pOldAttach->i_getMedium() == pMedium)
11396 {
11397 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11398
11399 /* yes: remove from old to avoid de-association */
11400 oldAtts.erase(oldIt);
11401 break;
11402 }
11403 }
11404 }
11405 }
11406
11407 /* enumerate remaining old attachments and de-associate from the
11408 * current machine state */
11409 for (MediumAttachmentList::const_iterator
11410 it = oldAtts.begin();
11411 it != oldAtts.end();
11412 ++it)
11413 {
11414 MediumAttachment *pAttach = *it;
11415 Medium *pMedium = pAttach->i_getMedium();
11416
11417 /* Detach only hard disks, since DVD/floppy media is detached
11418 * instantly in MountMedium. */
11419 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11420 {
11421 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11422
11423 /* now de-associate from the current machine state */
11424 rc = pMedium->i_removeBackReference(mData->mUuid);
11425 AssertComRC(rc);
11426
11427 if (aOnline)
11428 {
11429 /* unlock since medium is not used anymore */
11430 MediumLockList *pMediumLockList;
11431 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11432 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11433 {
11434 /* this happens for online snapshots, there the attachment
11435 * is changing, but only to a diff image created under
11436 * the old one, so there is no separate lock list */
11437 Assert(!pMediumLockList);
11438 }
11439 else
11440 {
11441 AssertComRC(rc);
11442 if (pMediumLockList)
11443 {
11444 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11445 AssertComRC(rc);
11446 }
11447 }
11448 }
11449 }
11450 }
11451
11452 /* take media locks again so that the locking state is consistent */
11453 if (fMediaNeedsLocking)
11454 {
11455 Assert(aOnline);
11456 rc = mData->mSession.mLockedMedia.Lock();
11457 AssertComRC(rc);
11458 }
11459
11460 /* commit the hard disk changes */
11461 mMediumAttachments.commit();
11462
11463 if (i_isSessionMachine())
11464 {
11465 /*
11466 * Update the parent machine to point to the new owner.
11467 * This is necessary because the stored parent will point to the
11468 * session machine otherwise and cause crashes or errors later
11469 * when the session machine gets invalid.
11470 */
11471 /** @todo Change the MediumAttachment class to behave like any other
11472 * class in this regard by creating peer MediumAttachment
11473 * objects for session machines and share the data with the peer
11474 * machine.
11475 */
11476 for (MediumAttachmentList::const_iterator
11477 it = mMediumAttachments->begin();
11478 it != mMediumAttachments->end();
11479 ++it)
11480 (*it)->i_updateParentMachine(mPeer);
11481
11482 /* attach new data to the primary machine and reshare it */
11483 mPeer->mMediumAttachments.attach(mMediumAttachments);
11484 }
11485
11486 return;
11487}
11488
11489/**
11490 * Perform deferred deletion of implicitly created diffs.
11491 *
11492 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11493 * changed (not backed up).
11494 *
11495 * @note Locks this object for writing!
11496 */
11497void Machine::i_rollbackMedia()
11498{
11499 AutoCaller autoCaller(this);
11500 AssertComRCReturnVoid(autoCaller.rc());
11501
11502 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11503 LogFlowThisFunc(("Entering rollbackMedia\n"));
11504
11505 HRESULT rc = S_OK;
11506
11507 /* no attach/detach operations -- nothing to do */
11508 if (!mMediumAttachments.isBackedUp())
11509 return;
11510
11511 /* enumerate new attachments */
11512 for (MediumAttachmentList::const_iterator
11513 it = mMediumAttachments->begin();
11514 it != mMediumAttachments->end();
11515 ++it)
11516 {
11517 MediumAttachment *pAttach = *it;
11518 /* Fix up the backrefs for DVD/floppy media. */
11519 if (pAttach->i_getType() != DeviceType_HardDisk)
11520 {
11521 Medium *pMedium = pAttach->i_getMedium();
11522 if (pMedium)
11523 {
11524 rc = pMedium->i_removeBackReference(mData->mUuid);
11525 AssertComRC(rc);
11526 }
11527 }
11528
11529 (*it)->i_rollback();
11530
11531 pAttach = *it;
11532 /* Fix up the backrefs for DVD/floppy media. */
11533 if (pAttach->i_getType() != DeviceType_HardDisk)
11534 {
11535 Medium *pMedium = pAttach->i_getMedium();
11536 if (pMedium)
11537 {
11538 rc = pMedium->i_addBackReference(mData->mUuid);
11539 AssertComRC(rc);
11540 }
11541 }
11542 }
11543
11544 /** @todo convert all this Machine-based voodoo to MediumAttachment
11545 * based rollback logic. */
11546 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11547
11548 return;
11549}
11550
11551/**
11552 * Returns true if the settings file is located in the directory named exactly
11553 * as the machine; this means, among other things, that the machine directory
11554 * should be auto-renamed.
11555 *
11556 * @param aSettingsDir if not NULL, the full machine settings file directory
11557 * name will be assigned there.
11558 *
11559 * @note Doesn't lock anything.
11560 * @note Not thread safe (must be called from this object's lock).
11561 */
11562bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11563{
11564 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11565 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11566 if (aSettingsDir)
11567 *aSettingsDir = strMachineDirName;
11568 strMachineDirName.stripPath(); // vmname
11569 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11570 strConfigFileOnly.stripPath() // vmname.vbox
11571 .stripSuffix(); // vmname
11572 /** @todo hack, make somehow use of ComposeMachineFilename */
11573 if (mUserData->s.fDirectoryIncludesUUID)
11574 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11575
11576 AssertReturn(!strMachineDirName.isEmpty(), false);
11577 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11578
11579 return strMachineDirName == strConfigFileOnly;
11580}
11581
11582/**
11583 * Discards all changes to machine settings.
11584 *
11585 * @param aNotify Whether to notify the direct session about changes or not.
11586 *
11587 * @note Locks objects for writing!
11588 */
11589void Machine::i_rollback(bool aNotify)
11590{
11591 AutoCaller autoCaller(this);
11592 AssertComRCReturn(autoCaller.rc(), (void)0);
11593
11594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11595
11596 if (!mStorageControllers.isNull())
11597 {
11598 if (mStorageControllers.isBackedUp())
11599 {
11600 /* unitialize all new devices (absent in the backed up list). */
11601 StorageControllerList *backedList = mStorageControllers.backedUpData();
11602 for (StorageControllerList::const_iterator
11603 it = mStorageControllers->begin();
11604 it != mStorageControllers->end();
11605 ++it)
11606 {
11607 if ( std::find(backedList->begin(), backedList->end(), *it)
11608 == backedList->end()
11609 )
11610 {
11611 (*it)->uninit();
11612 }
11613 }
11614
11615 /* restore the list */
11616 mStorageControllers.rollback();
11617 }
11618
11619 /* rollback any changes to devices after restoring the list */
11620 if (mData->flModifications & IsModified_Storage)
11621 {
11622 for (StorageControllerList::const_iterator
11623 it = mStorageControllers->begin();
11624 it != mStorageControllers->end();
11625 ++it)
11626 {
11627 (*it)->i_rollback();
11628 }
11629 }
11630 }
11631
11632 if (!mUSBControllers.isNull())
11633 {
11634 if (mUSBControllers.isBackedUp())
11635 {
11636 /* unitialize all new devices (absent in the backed up list). */
11637 USBControllerList *backedList = mUSBControllers.backedUpData();
11638 for (USBControllerList::const_iterator
11639 it = mUSBControllers->begin();
11640 it != mUSBControllers->end();
11641 ++it)
11642 {
11643 if ( std::find(backedList->begin(), backedList->end(), *it)
11644 == backedList->end()
11645 )
11646 {
11647 (*it)->uninit();
11648 }
11649 }
11650
11651 /* restore the list */
11652 mUSBControllers.rollback();
11653 }
11654
11655 /* rollback any changes to devices after restoring the list */
11656 if (mData->flModifications & IsModified_USB)
11657 {
11658 for (USBControllerList::const_iterator
11659 it = mUSBControllers->begin();
11660 it != mUSBControllers->end();
11661 ++it)
11662 {
11663 (*it)->i_rollback();
11664 }
11665 }
11666 }
11667
11668 mUserData.rollback();
11669
11670 mHWData.rollback();
11671
11672 if (mData->flModifications & IsModified_Storage)
11673 i_rollbackMedia();
11674
11675 if (mBIOSSettings)
11676 mBIOSSettings->i_rollback();
11677
11678 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11679 mRecordingSettings->i_rollback();
11680
11681 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11682 mGraphicsAdapter->i_rollback();
11683
11684 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11685 mVRDEServer->i_rollback();
11686
11687 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11688 mAudioAdapter->i_rollback();
11689
11690 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11691 mUSBDeviceFilters->i_rollback();
11692
11693 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11694 mBandwidthControl->i_rollback();
11695
11696 if (!mHWData.isNull())
11697 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11698 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11699 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11700 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11701
11702 if (mData->flModifications & IsModified_NetworkAdapters)
11703 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11704 if ( mNetworkAdapters[slot]
11705 && mNetworkAdapters[slot]->i_isModified())
11706 {
11707 mNetworkAdapters[slot]->i_rollback();
11708 networkAdapters[slot] = mNetworkAdapters[slot];
11709 }
11710
11711 if (mData->flModifications & IsModified_SerialPorts)
11712 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11713 if ( mSerialPorts[slot]
11714 && mSerialPorts[slot]->i_isModified())
11715 {
11716 mSerialPorts[slot]->i_rollback();
11717 serialPorts[slot] = mSerialPorts[slot];
11718 }
11719
11720 if (mData->flModifications & IsModified_ParallelPorts)
11721 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11722 if ( mParallelPorts[slot]
11723 && mParallelPorts[slot]->i_isModified())
11724 {
11725 mParallelPorts[slot]->i_rollback();
11726 parallelPorts[slot] = mParallelPorts[slot];
11727 }
11728
11729 if (aNotify)
11730 {
11731 /* inform the direct session about changes */
11732
11733 ComObjPtr<Machine> that = this;
11734 uint32_t flModifications = mData->flModifications;
11735 alock.release();
11736
11737 if (flModifications & IsModified_SharedFolders)
11738 that->i_onSharedFolderChange();
11739
11740 if (flModifications & IsModified_VRDEServer)
11741 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11742 if (flModifications & IsModified_USB)
11743 that->i_onUSBControllerChange();
11744
11745 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11746 if (networkAdapters[slot])
11747 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11748 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11749 if (serialPorts[slot])
11750 that->i_onSerialPortChange(serialPorts[slot]);
11751 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11752 if (parallelPorts[slot])
11753 that->i_onParallelPortChange(parallelPorts[slot]);
11754
11755 if (flModifications & IsModified_Storage)
11756 {
11757 for (StorageControllerList::const_iterator
11758 it = mStorageControllers->begin();
11759 it != mStorageControllers->end();
11760 ++it)
11761 {
11762 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11763 }
11764 }
11765
11766
11767#if 0
11768 if (flModifications & IsModified_BandwidthControl)
11769 that->onBandwidthControlChange();
11770#endif
11771 }
11772}
11773
11774/**
11775 * Commits all the changes to machine settings.
11776 *
11777 * Note that this operation is supposed to never fail.
11778 *
11779 * @note Locks this object and children for writing.
11780 */
11781void Machine::i_commit()
11782{
11783 AutoCaller autoCaller(this);
11784 AssertComRCReturnVoid(autoCaller.rc());
11785
11786 AutoCaller peerCaller(mPeer);
11787 AssertComRCReturnVoid(peerCaller.rc());
11788
11789 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11790
11791 /*
11792 * use safe commit to ensure Snapshot machines (that share mUserData)
11793 * will still refer to a valid memory location
11794 */
11795 mUserData.commitCopy();
11796
11797 mHWData.commit();
11798
11799 if (mMediumAttachments.isBackedUp())
11800 i_commitMedia(Global::IsOnline(mData->mMachineState));
11801
11802 mBIOSSettings->i_commit();
11803 mRecordingSettings->i_commit();
11804 mGraphicsAdapter->i_commit();
11805 mVRDEServer->i_commit();
11806 mAudioAdapter->i_commit();
11807 mUSBDeviceFilters->i_commit();
11808 mBandwidthControl->i_commit();
11809
11810 /* Since mNetworkAdapters is a list which might have been changed (resized)
11811 * without using the Backupable<> template we need to handle the copying
11812 * of the list entries manually, including the creation of peers for the
11813 * new objects. */
11814 bool commitNetworkAdapters = false;
11815 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11816 if (mPeer)
11817 {
11818 /* commit everything, even the ones which will go away */
11819 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11820 mNetworkAdapters[slot]->i_commit();
11821 /* copy over the new entries, creating a peer and uninit the original */
11822 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11823 for (size_t slot = 0; slot < newSize; slot++)
11824 {
11825 /* look if this adapter has a peer device */
11826 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11827 if (!peer)
11828 {
11829 /* no peer means the adapter is a newly created one;
11830 * create a peer owning data this data share it with */
11831 peer.createObject();
11832 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11833 }
11834 mPeer->mNetworkAdapters[slot] = peer;
11835 }
11836 /* uninit any no longer needed network adapters */
11837 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11838 mNetworkAdapters[slot]->uninit();
11839 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11840 {
11841 if (mPeer->mNetworkAdapters[slot])
11842 mPeer->mNetworkAdapters[slot]->uninit();
11843 }
11844 /* Keep the original network adapter count until this point, so that
11845 * discarding a chipset type change will not lose settings. */
11846 mNetworkAdapters.resize(newSize);
11847 mPeer->mNetworkAdapters.resize(newSize);
11848 }
11849 else
11850 {
11851 /* we have no peer (our parent is the newly created machine);
11852 * just commit changes to the network adapters */
11853 commitNetworkAdapters = true;
11854 }
11855 if (commitNetworkAdapters)
11856 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11857 mNetworkAdapters[slot]->i_commit();
11858
11859 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11860 mSerialPorts[slot]->i_commit();
11861 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11862 mParallelPorts[slot]->i_commit();
11863
11864 bool commitStorageControllers = false;
11865
11866 if (mStorageControllers.isBackedUp())
11867 {
11868 mStorageControllers.commit();
11869
11870 if (mPeer)
11871 {
11872 /* Commit all changes to new controllers (this will reshare data with
11873 * peers for those who have peers) */
11874 StorageControllerList *newList = new StorageControllerList();
11875 for (StorageControllerList::const_iterator
11876 it = mStorageControllers->begin();
11877 it != mStorageControllers->end();
11878 ++it)
11879 {
11880 (*it)->i_commit();
11881
11882 /* look if this controller has a peer device */
11883 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11884 if (!peer)
11885 {
11886 /* no peer means the device is a newly created one;
11887 * create a peer owning data this device share it with */
11888 peer.createObject();
11889 peer->init(mPeer, *it, true /* aReshare */);
11890 }
11891 else
11892 {
11893 /* remove peer from the old list */
11894 mPeer->mStorageControllers->remove(peer);
11895 }
11896 /* and add it to the new list */
11897 newList->push_back(peer);
11898 }
11899
11900 /* uninit old peer's controllers that are left */
11901 for (StorageControllerList::const_iterator
11902 it = mPeer->mStorageControllers->begin();
11903 it != mPeer->mStorageControllers->end();
11904 ++it)
11905 {
11906 (*it)->uninit();
11907 }
11908
11909 /* attach new list of controllers to our peer */
11910 mPeer->mStorageControllers.attach(newList);
11911 }
11912 else
11913 {
11914 /* we have no peer (our parent is the newly created machine);
11915 * just commit changes to devices */
11916 commitStorageControllers = true;
11917 }
11918 }
11919 else
11920 {
11921 /* the list of controllers itself is not changed,
11922 * just commit changes to controllers themselves */
11923 commitStorageControllers = true;
11924 }
11925
11926 if (commitStorageControllers)
11927 {
11928 for (StorageControllerList::const_iterator
11929 it = mStorageControllers->begin();
11930 it != mStorageControllers->end();
11931 ++it)
11932 {
11933 (*it)->i_commit();
11934 }
11935 }
11936
11937 bool commitUSBControllers = false;
11938
11939 if (mUSBControllers.isBackedUp())
11940 {
11941 mUSBControllers.commit();
11942
11943 if (mPeer)
11944 {
11945 /* Commit all changes to new controllers (this will reshare data with
11946 * peers for those who have peers) */
11947 USBControllerList *newList = new USBControllerList();
11948 for (USBControllerList::const_iterator
11949 it = mUSBControllers->begin();
11950 it != mUSBControllers->end();
11951 ++it)
11952 {
11953 (*it)->i_commit();
11954
11955 /* look if this controller has a peer device */
11956 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11957 if (!peer)
11958 {
11959 /* no peer means the device is a newly created one;
11960 * create a peer owning data this device share it with */
11961 peer.createObject();
11962 peer->init(mPeer, *it, true /* aReshare */);
11963 }
11964 else
11965 {
11966 /* remove peer from the old list */
11967 mPeer->mUSBControllers->remove(peer);
11968 }
11969 /* and add it to the new list */
11970 newList->push_back(peer);
11971 }
11972
11973 /* uninit old peer's controllers that are left */
11974 for (USBControllerList::const_iterator
11975 it = mPeer->mUSBControllers->begin();
11976 it != mPeer->mUSBControllers->end();
11977 ++it)
11978 {
11979 (*it)->uninit();
11980 }
11981
11982 /* attach new list of controllers to our peer */
11983 mPeer->mUSBControllers.attach(newList);
11984 }
11985 else
11986 {
11987 /* we have no peer (our parent is the newly created machine);
11988 * just commit changes to devices */
11989 commitUSBControllers = true;
11990 }
11991 }
11992 else
11993 {
11994 /* the list of controllers itself is not changed,
11995 * just commit changes to controllers themselves */
11996 commitUSBControllers = true;
11997 }
11998
11999 if (commitUSBControllers)
12000 {
12001 for (USBControllerList::const_iterator
12002 it = mUSBControllers->begin();
12003 it != mUSBControllers->end();
12004 ++it)
12005 {
12006 (*it)->i_commit();
12007 }
12008 }
12009
12010 if (i_isSessionMachine())
12011 {
12012 /* attach new data to the primary machine and reshare it */
12013 mPeer->mUserData.attach(mUserData);
12014 mPeer->mHWData.attach(mHWData);
12015 /* mmMediumAttachments is reshared by fixupMedia */
12016 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12017 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12018 }
12019}
12020
12021/**
12022 * Copies all the hardware data from the given machine.
12023 *
12024 * Currently, only called when the VM is being restored from a snapshot. In
12025 * particular, this implies that the VM is not running during this method's
12026 * call.
12027 *
12028 * @note This method must be called from under this object's lock.
12029 *
12030 * @note This method doesn't call #i_commit(), so all data remains backed up and
12031 * unsaved.
12032 */
12033void Machine::i_copyFrom(Machine *aThat)
12034{
12035 AssertReturnVoid(!i_isSnapshotMachine());
12036 AssertReturnVoid(aThat->i_isSnapshotMachine());
12037
12038 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12039
12040 mHWData.assignCopy(aThat->mHWData);
12041
12042 // create copies of all shared folders (mHWData after attaching a copy
12043 // contains just references to original objects)
12044 for (HWData::SharedFolderList::iterator
12045 it = mHWData->mSharedFolders.begin();
12046 it != mHWData->mSharedFolders.end();
12047 ++it)
12048 {
12049 ComObjPtr<SharedFolder> folder;
12050 folder.createObject();
12051 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12052 AssertComRC(rc);
12053 *it = folder;
12054 }
12055
12056 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12057 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12058 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12059 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12060 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12061 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12062 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12063
12064 /* create private copies of all controllers */
12065 mStorageControllers.backup();
12066 mStorageControllers->clear();
12067 for (StorageControllerList::const_iterator
12068 it = aThat->mStorageControllers->begin();
12069 it != aThat->mStorageControllers->end();
12070 ++it)
12071 {
12072 ComObjPtr<StorageController> ctrl;
12073 ctrl.createObject();
12074 ctrl->initCopy(this, *it);
12075 mStorageControllers->push_back(ctrl);
12076 }
12077
12078 /* create private copies of all USB controllers */
12079 mUSBControllers.backup();
12080 mUSBControllers->clear();
12081 for (USBControllerList::const_iterator
12082 it = aThat->mUSBControllers->begin();
12083 it != aThat->mUSBControllers->end();
12084 ++it)
12085 {
12086 ComObjPtr<USBController> ctrl;
12087 ctrl.createObject();
12088 ctrl->initCopy(this, *it);
12089 mUSBControllers->push_back(ctrl);
12090 }
12091
12092 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12093 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12094 {
12095 if (mNetworkAdapters[slot].isNotNull())
12096 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12097 else
12098 {
12099 unconst(mNetworkAdapters[slot]).createObject();
12100 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12101 }
12102 }
12103 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12104 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12105 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12106 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12107}
12108
12109/**
12110 * Returns whether the given storage controller is hotplug capable.
12111 *
12112 * @returns true if the controller supports hotplugging
12113 * false otherwise.
12114 * @param enmCtrlType The controller type to check for.
12115 */
12116bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12117{
12118 ComPtr<ISystemProperties> systemProperties;
12119 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12120 if (FAILED(rc))
12121 return false;
12122
12123 BOOL aHotplugCapable = FALSE;
12124 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12125
12126 return RT_BOOL(aHotplugCapable);
12127}
12128
12129#ifdef VBOX_WITH_RESOURCE_USAGE_API
12130
12131void Machine::i_getDiskList(MediaList &list)
12132{
12133 for (MediumAttachmentList::const_iterator
12134 it = mMediumAttachments->begin();
12135 it != mMediumAttachments->end();
12136 ++it)
12137 {
12138 MediumAttachment *pAttach = *it;
12139 /* just in case */
12140 AssertContinue(pAttach);
12141
12142 AutoCaller localAutoCallerA(pAttach);
12143 if (FAILED(localAutoCallerA.rc())) continue;
12144
12145 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12146
12147 if (pAttach->i_getType() == DeviceType_HardDisk)
12148 list.push_back(pAttach->i_getMedium());
12149 }
12150}
12151
12152void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12153{
12154 AssertReturnVoid(isWriteLockOnCurrentThread());
12155 AssertPtrReturnVoid(aCollector);
12156
12157 pm::CollectorHAL *hal = aCollector->getHAL();
12158 /* Create sub metrics */
12159 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12160 "Percentage of processor time spent in user mode by the VM process.");
12161 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12162 "Percentage of processor time spent in kernel mode by the VM process.");
12163 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12164 "Size of resident portion of VM process in memory.");
12165 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12166 "Actual size of all VM disks combined.");
12167 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12168 "Network receive rate.");
12169 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12170 "Network transmit rate.");
12171 /* Create and register base metrics */
12172 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12173 cpuLoadUser, cpuLoadKernel);
12174 aCollector->registerBaseMetric(cpuLoad);
12175 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12176 ramUsageUsed);
12177 aCollector->registerBaseMetric(ramUsage);
12178 MediaList disks;
12179 i_getDiskList(disks);
12180 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12181 diskUsageUsed);
12182 aCollector->registerBaseMetric(diskUsage);
12183
12184 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12185 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12186 new pm::AggregateAvg()));
12187 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12188 new pm::AggregateMin()));
12189 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12190 new pm::AggregateMax()));
12191 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12192 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12193 new pm::AggregateAvg()));
12194 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12195 new pm::AggregateMin()));
12196 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12197 new pm::AggregateMax()));
12198
12199 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12200 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12201 new pm::AggregateAvg()));
12202 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12203 new pm::AggregateMin()));
12204 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12205 new pm::AggregateMax()));
12206
12207 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12208 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12209 new pm::AggregateAvg()));
12210 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12211 new pm::AggregateMin()));
12212 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12213 new pm::AggregateMax()));
12214
12215
12216 /* Guest metrics collector */
12217 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12218 aCollector->registerGuest(mCollectorGuest);
12219 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12220
12221 /* Create sub metrics */
12222 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12223 "Percentage of processor time spent in user mode as seen by the guest.");
12224 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12225 "Percentage of processor time spent in kernel mode as seen by the guest.");
12226 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12227 "Percentage of processor time spent idling as seen by the guest.");
12228
12229 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12230 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12231 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12232 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12233 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12234 pm::SubMetric *guestMemCache = new pm::SubMetric(
12235 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12236
12237 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12238 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12239
12240 /* Create and register base metrics */
12241 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12242 machineNetRx, machineNetTx);
12243 aCollector->registerBaseMetric(machineNetRate);
12244
12245 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12246 guestLoadUser, guestLoadKernel, guestLoadIdle);
12247 aCollector->registerBaseMetric(guestCpuLoad);
12248
12249 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12250 guestMemTotal, guestMemFree,
12251 guestMemBalloon, guestMemShared,
12252 guestMemCache, guestPagedTotal);
12253 aCollector->registerBaseMetric(guestCpuMem);
12254
12255 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12256 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12257 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12258 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12259
12260 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12261 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12262 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12263 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12264
12265 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12266 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12268 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12269
12270 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12271 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12273 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12274
12275 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12276 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12284
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12289
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12296 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12301 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12306 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12309}
12310
12311void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12312{
12313 AssertReturnVoid(isWriteLockOnCurrentThread());
12314
12315 if (aCollector)
12316 {
12317 aCollector->unregisterMetricsFor(aMachine);
12318 aCollector->unregisterBaseMetricsFor(aMachine);
12319 }
12320}
12321
12322#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12323
12324
12325////////////////////////////////////////////////////////////////////////////////
12326
12327DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12328
12329HRESULT SessionMachine::FinalConstruct()
12330{
12331 LogFlowThisFunc(("\n"));
12332
12333 mClientToken = NULL;
12334
12335 return BaseFinalConstruct();
12336}
12337
12338void SessionMachine::FinalRelease()
12339{
12340 LogFlowThisFunc(("\n"));
12341
12342 Assert(!mClientToken);
12343 /* paranoia, should not hang around any more */
12344 if (mClientToken)
12345 {
12346 delete mClientToken;
12347 mClientToken = NULL;
12348 }
12349
12350 uninit(Uninit::Unexpected);
12351
12352 BaseFinalRelease();
12353}
12354
12355/**
12356 * @note Must be called only by Machine::LockMachine() from its own write lock.
12357 */
12358HRESULT SessionMachine::init(Machine *aMachine)
12359{
12360 LogFlowThisFuncEnter();
12361 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12362
12363 AssertReturn(aMachine, E_INVALIDARG);
12364
12365 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12366
12367 /* Enclose the state transition NotReady->InInit->Ready */
12368 AutoInitSpan autoInitSpan(this);
12369 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12370
12371 HRESULT rc = S_OK;
12372
12373 RT_ZERO(mAuthLibCtx);
12374
12375 /* create the machine client token */
12376 try
12377 {
12378 mClientToken = new ClientToken(aMachine, this);
12379 if (!mClientToken->isReady())
12380 {
12381 delete mClientToken;
12382 mClientToken = NULL;
12383 rc = E_FAIL;
12384 }
12385 }
12386 catch (std::bad_alloc &)
12387 {
12388 rc = E_OUTOFMEMORY;
12389 }
12390 if (FAILED(rc))
12391 return rc;
12392
12393 /* memorize the peer Machine */
12394 unconst(mPeer) = aMachine;
12395 /* share the parent pointer */
12396 unconst(mParent) = aMachine->mParent;
12397
12398 /* take the pointers to data to share */
12399 mData.share(aMachine->mData);
12400 mSSData.share(aMachine->mSSData);
12401
12402 mUserData.share(aMachine->mUserData);
12403 mHWData.share(aMachine->mHWData);
12404 mMediumAttachments.share(aMachine->mMediumAttachments);
12405
12406 mStorageControllers.allocate();
12407 for (StorageControllerList::const_iterator
12408 it = aMachine->mStorageControllers->begin();
12409 it != aMachine->mStorageControllers->end();
12410 ++it)
12411 {
12412 ComObjPtr<StorageController> ctl;
12413 ctl.createObject();
12414 ctl->init(this, *it);
12415 mStorageControllers->push_back(ctl);
12416 }
12417
12418 mUSBControllers.allocate();
12419 for (USBControllerList::const_iterator
12420 it = aMachine->mUSBControllers->begin();
12421 it != aMachine->mUSBControllers->end();
12422 ++it)
12423 {
12424 ComObjPtr<USBController> ctl;
12425 ctl.createObject();
12426 ctl->init(this, *it);
12427 mUSBControllers->push_back(ctl);
12428 }
12429
12430 unconst(mBIOSSettings).createObject();
12431 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12432 unconst(mRecordingSettings).createObject();
12433 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12434 /* create another GraphicsAdapter object that will be mutable */
12435 unconst(mGraphicsAdapter).createObject();
12436 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12437 /* create another VRDEServer object that will be mutable */
12438 unconst(mVRDEServer).createObject();
12439 mVRDEServer->init(this, aMachine->mVRDEServer);
12440 /* create another audio adapter object that will be mutable */
12441 unconst(mAudioAdapter).createObject();
12442 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12443 /* create a list of serial ports that will be mutable */
12444 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12445 {
12446 unconst(mSerialPorts[slot]).createObject();
12447 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12448 }
12449 /* create a list of parallel ports that will be mutable */
12450 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12451 {
12452 unconst(mParallelPorts[slot]).createObject();
12453 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12454 }
12455
12456 /* create another USB device filters object that will be mutable */
12457 unconst(mUSBDeviceFilters).createObject();
12458 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12459
12460 /* create a list of network adapters that will be mutable */
12461 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12462 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12463 {
12464 unconst(mNetworkAdapters[slot]).createObject();
12465 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12466 }
12467
12468 /* create another bandwidth control object that will be mutable */
12469 unconst(mBandwidthControl).createObject();
12470 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12471
12472 /* default is to delete saved state on Saved -> PoweredOff transition */
12473 mRemoveSavedState = true;
12474
12475 /* Confirm a successful initialization when it's the case */
12476 autoInitSpan.setSucceeded();
12477
12478 miNATNetworksStarted = 0;
12479
12480 LogFlowThisFuncLeave();
12481 return rc;
12482}
12483
12484/**
12485 * Uninitializes this session object. If the reason is other than
12486 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12487 * or the client watcher code.
12488 *
12489 * @param aReason uninitialization reason
12490 *
12491 * @note Locks mParent + this object for writing.
12492 */
12493void SessionMachine::uninit(Uninit::Reason aReason)
12494{
12495 LogFlowThisFuncEnter();
12496 LogFlowThisFunc(("reason=%d\n", aReason));
12497
12498 /*
12499 * Strongly reference ourselves to prevent this object deletion after
12500 * mData->mSession.mMachine.setNull() below (which can release the last
12501 * reference and call the destructor). Important: this must be done before
12502 * accessing any members (and before AutoUninitSpan that does it as well).
12503 * This self reference will be released as the very last step on return.
12504 */
12505 ComObjPtr<SessionMachine> selfRef;
12506 if (aReason != Uninit::Unexpected)
12507 selfRef = this;
12508
12509 /* Enclose the state transition Ready->InUninit->NotReady */
12510 AutoUninitSpan autoUninitSpan(this);
12511 if (autoUninitSpan.uninitDone())
12512 {
12513 LogFlowThisFunc(("Already uninitialized\n"));
12514 LogFlowThisFuncLeave();
12515 return;
12516 }
12517
12518 if (autoUninitSpan.initFailed())
12519 {
12520 /* We've been called by init() because it's failed. It's not really
12521 * necessary (nor it's safe) to perform the regular uninit sequence
12522 * below, the following is enough.
12523 */
12524 LogFlowThisFunc(("Initialization failed.\n"));
12525 /* destroy the machine client token */
12526 if (mClientToken)
12527 {
12528 delete mClientToken;
12529 mClientToken = NULL;
12530 }
12531 uninitDataAndChildObjects();
12532 mData.free();
12533 unconst(mParent) = NULL;
12534 unconst(mPeer) = NULL;
12535 LogFlowThisFuncLeave();
12536 return;
12537 }
12538
12539 MachineState_T lastState;
12540 {
12541 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12542 lastState = mData->mMachineState;
12543 }
12544 NOREF(lastState);
12545
12546#ifdef VBOX_WITH_USB
12547 // release all captured USB devices, but do this before requesting the locks below
12548 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12549 {
12550 /* Console::captureUSBDevices() is called in the VM process only after
12551 * setting the machine state to Starting or Restoring.
12552 * Console::detachAllUSBDevices() will be called upon successful
12553 * termination. So, we need to release USB devices only if there was
12554 * an abnormal termination of a running VM.
12555 *
12556 * This is identical to SessionMachine::DetachAllUSBDevices except
12557 * for the aAbnormal argument. */
12558 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12559 AssertComRC(rc);
12560 NOREF(rc);
12561
12562 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12563 if (service)
12564 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12565 }
12566#endif /* VBOX_WITH_USB */
12567
12568 // we need to lock this object in uninit() because the lock is shared
12569 // with mPeer (as well as data we modify below). mParent lock is needed
12570 // by several calls to it.
12571 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12572
12573#ifdef VBOX_WITH_RESOURCE_USAGE_API
12574 /*
12575 * It is safe to call Machine::i_unregisterMetrics() here because
12576 * PerformanceCollector::samplerCallback no longer accesses guest methods
12577 * holding the lock.
12578 */
12579 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12580 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12581 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12582 if (mCollectorGuest)
12583 {
12584 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12585 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12586 mCollectorGuest = NULL;
12587 }
12588#endif
12589
12590 if (aReason == Uninit::Abnormal)
12591 {
12592 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12593
12594 /* reset the state to Aborted */
12595 if (mData->mMachineState != MachineState_Aborted)
12596 i_setMachineState(MachineState_Aborted);
12597 }
12598
12599 // any machine settings modified?
12600 if (mData->flModifications)
12601 {
12602 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12603 i_rollback(false /* aNotify */);
12604 }
12605
12606 mData->mSession.mPID = NIL_RTPROCESS;
12607
12608 if (aReason == Uninit::Unexpected)
12609 {
12610 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12611 * client watcher thread to update the set of machines that have open
12612 * sessions. */
12613 mParent->i_updateClientWatcher();
12614 }
12615
12616 /* uninitialize all remote controls */
12617 if (mData->mSession.mRemoteControls.size())
12618 {
12619 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12620 mData->mSession.mRemoteControls.size()));
12621
12622 /* Always restart a the beginning, since the iterator is invalidated
12623 * by using erase(). */
12624 for (Data::Session::RemoteControlList::iterator
12625 it = mData->mSession.mRemoteControls.begin();
12626 it != mData->mSession.mRemoteControls.end();
12627 it = mData->mSession.mRemoteControls.begin())
12628 {
12629 ComPtr<IInternalSessionControl> pControl = *it;
12630 mData->mSession.mRemoteControls.erase(it);
12631 multilock.release();
12632 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12633 HRESULT rc = pControl->Uninitialize();
12634 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12635 if (FAILED(rc))
12636 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12637 multilock.acquire();
12638 }
12639 mData->mSession.mRemoteControls.clear();
12640 }
12641
12642 /* Remove all references to the NAT network service. The service will stop
12643 * if all references (also from other VMs) are removed. */
12644 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12645 {
12646 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12647 {
12648 BOOL enabled;
12649 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12650 if ( FAILED(hrc)
12651 || !enabled)
12652 continue;
12653
12654 NetworkAttachmentType_T type;
12655 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12656 if ( SUCCEEDED(hrc)
12657 && type == NetworkAttachmentType_NATNetwork)
12658 {
12659 Bstr name;
12660 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12661 if (SUCCEEDED(hrc))
12662 {
12663 multilock.release();
12664 Utf8Str strName(name);
12665 LogRel(("VM '%s' stops using NAT network '%s'\n",
12666 mUserData->s.strName.c_str(), strName.c_str()));
12667 mParent->i_natNetworkRefDec(strName);
12668 multilock.acquire();
12669 }
12670 }
12671 }
12672 }
12673
12674 /*
12675 * An expected uninitialization can come only from #i_checkForDeath().
12676 * Otherwise it means that something's gone really wrong (for example,
12677 * the Session implementation has released the VirtualBox reference
12678 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12679 * etc). However, it's also possible, that the client releases the IPC
12680 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12681 * but the VirtualBox release event comes first to the server process.
12682 * This case is practically possible, so we should not assert on an
12683 * unexpected uninit, just log a warning.
12684 */
12685
12686 if (aReason == Uninit::Unexpected)
12687 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12688
12689 if (aReason != Uninit::Normal)
12690 {
12691 mData->mSession.mDirectControl.setNull();
12692 }
12693 else
12694 {
12695 /* this must be null here (see #OnSessionEnd()) */
12696 Assert(mData->mSession.mDirectControl.isNull());
12697 Assert(mData->mSession.mState == SessionState_Unlocking);
12698 Assert(!mData->mSession.mProgress.isNull());
12699 }
12700 if (mData->mSession.mProgress)
12701 {
12702 if (aReason == Uninit::Normal)
12703 mData->mSession.mProgress->i_notifyComplete(S_OK);
12704 else
12705 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12706 COM_IIDOF(ISession),
12707 getComponentName(),
12708 tr("The VM session was aborted"));
12709 mData->mSession.mProgress.setNull();
12710 }
12711
12712 if (mConsoleTaskData.mProgress)
12713 {
12714 Assert(aReason == Uninit::Abnormal);
12715 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12716 COM_IIDOF(ISession),
12717 getComponentName(),
12718 tr("The VM session was aborted"));
12719 mConsoleTaskData.mProgress.setNull();
12720 }
12721
12722 /* remove the association between the peer machine and this session machine */
12723 Assert( (SessionMachine*)mData->mSession.mMachine == this
12724 || aReason == Uninit::Unexpected);
12725
12726 /* reset the rest of session data */
12727 mData->mSession.mLockType = LockType_Null;
12728 mData->mSession.mMachine.setNull();
12729 mData->mSession.mState = SessionState_Unlocked;
12730 mData->mSession.mName.setNull();
12731
12732 /* destroy the machine client token before leaving the exclusive lock */
12733 if (mClientToken)
12734 {
12735 delete mClientToken;
12736 mClientToken = NULL;
12737 }
12738
12739 /* fire an event */
12740 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12741
12742 uninitDataAndChildObjects();
12743
12744 /* free the essential data structure last */
12745 mData.free();
12746
12747 /* release the exclusive lock before setting the below two to NULL */
12748 multilock.release();
12749
12750 unconst(mParent) = NULL;
12751 unconst(mPeer) = NULL;
12752
12753 AuthLibUnload(&mAuthLibCtx);
12754
12755 LogFlowThisFuncLeave();
12756}
12757
12758// util::Lockable interface
12759////////////////////////////////////////////////////////////////////////////////
12760
12761/**
12762 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12763 * with the primary Machine instance (mPeer).
12764 */
12765RWLockHandle *SessionMachine::lockHandle() const
12766{
12767 AssertReturn(mPeer != NULL, NULL);
12768 return mPeer->lockHandle();
12769}
12770
12771// IInternalMachineControl methods
12772////////////////////////////////////////////////////////////////////////////////
12773
12774/**
12775 * Passes collected guest statistics to performance collector object
12776 */
12777HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12778 ULONG aCpuKernel, ULONG aCpuIdle,
12779 ULONG aMemTotal, ULONG aMemFree,
12780 ULONG aMemBalloon, ULONG aMemShared,
12781 ULONG aMemCache, ULONG aPageTotal,
12782 ULONG aAllocVMM, ULONG aFreeVMM,
12783 ULONG aBalloonedVMM, ULONG aSharedVMM,
12784 ULONG aVmNetRx, ULONG aVmNetTx)
12785{
12786#ifdef VBOX_WITH_RESOURCE_USAGE_API
12787 if (mCollectorGuest)
12788 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12789 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12790 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12791 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12792
12793 return S_OK;
12794#else
12795 NOREF(aValidStats);
12796 NOREF(aCpuUser);
12797 NOREF(aCpuKernel);
12798 NOREF(aCpuIdle);
12799 NOREF(aMemTotal);
12800 NOREF(aMemFree);
12801 NOREF(aMemBalloon);
12802 NOREF(aMemShared);
12803 NOREF(aMemCache);
12804 NOREF(aPageTotal);
12805 NOREF(aAllocVMM);
12806 NOREF(aFreeVMM);
12807 NOREF(aBalloonedVMM);
12808 NOREF(aSharedVMM);
12809 NOREF(aVmNetRx);
12810 NOREF(aVmNetTx);
12811 return E_NOTIMPL;
12812#endif
12813}
12814
12815////////////////////////////////////////////////////////////////////////////////
12816//
12817// SessionMachine task records
12818//
12819////////////////////////////////////////////////////////////////////////////////
12820
12821/**
12822 * Task record for saving the machine state.
12823 */
12824class SessionMachine::SaveStateTask
12825 : public Machine::Task
12826{
12827public:
12828 SaveStateTask(SessionMachine *m,
12829 Progress *p,
12830 const Utf8Str &t,
12831 Reason_T enmReason,
12832 const Utf8Str &strStateFilePath)
12833 : Task(m, p, t),
12834 m_enmReason(enmReason),
12835 m_strStateFilePath(strStateFilePath)
12836 {}
12837
12838private:
12839 void handler()
12840 {
12841 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12842 }
12843
12844 Reason_T m_enmReason;
12845 Utf8Str m_strStateFilePath;
12846
12847 friend class SessionMachine;
12848};
12849
12850/**
12851 * Task thread implementation for SessionMachine::SaveState(), called from
12852 * SessionMachine::taskHandler().
12853 *
12854 * @note Locks this object for writing.
12855 *
12856 * @param task
12857 * @return
12858 */
12859void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12860{
12861 LogFlowThisFuncEnter();
12862
12863 AutoCaller autoCaller(this);
12864 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12865 if (FAILED(autoCaller.rc()))
12866 {
12867 /* we might have been uninitialized because the session was accidentally
12868 * closed by the client, so don't assert */
12869 HRESULT rc = setError(E_FAIL,
12870 tr("The session has been accidentally closed"));
12871 task.m_pProgress->i_notifyComplete(rc);
12872 LogFlowThisFuncLeave();
12873 return;
12874 }
12875
12876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12877
12878 HRESULT rc = S_OK;
12879
12880 try
12881 {
12882 ComPtr<IInternalSessionControl> directControl;
12883 if (mData->mSession.mLockType == LockType_VM)
12884 directControl = mData->mSession.mDirectControl;
12885 if (directControl.isNull())
12886 throw setError(VBOX_E_INVALID_VM_STATE,
12887 tr("Trying to save state without a running VM"));
12888 alock.release();
12889 BOOL fSuspendedBySave;
12890 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12891 Assert(!fSuspendedBySave);
12892 alock.acquire();
12893
12894 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12895 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12896 throw E_FAIL);
12897
12898 if (SUCCEEDED(rc))
12899 {
12900 mSSData->strStateFilePath = task.m_strStateFilePath;
12901
12902 /* save all VM settings */
12903 rc = i_saveSettings(NULL);
12904 // no need to check whether VirtualBox.xml needs saving also since
12905 // we can't have a name change pending at this point
12906 }
12907 else
12908 {
12909 // On failure, set the state to the state we had at the beginning.
12910 i_setMachineState(task.m_machineStateBackup);
12911 i_updateMachineStateOnClient();
12912
12913 // Delete the saved state file (might have been already created).
12914 // No need to check whether this is shared with a snapshot here
12915 // because we certainly created a fresh saved state file here.
12916 RTFileDelete(task.m_strStateFilePath.c_str());
12917 }
12918 }
12919 catch (HRESULT aRC) { rc = aRC; }
12920
12921 task.m_pProgress->i_notifyComplete(rc);
12922
12923 LogFlowThisFuncLeave();
12924}
12925
12926/**
12927 * @note Locks this object for writing.
12928 */
12929HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12930{
12931 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12932}
12933
12934HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12935{
12936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12937
12938 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12939 if (FAILED(rc)) return rc;
12940
12941 if ( mData->mMachineState != MachineState_Running
12942 && mData->mMachineState != MachineState_Paused
12943 )
12944 return setError(VBOX_E_INVALID_VM_STATE,
12945 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12946 Global::stringifyMachineState(mData->mMachineState));
12947
12948 ComObjPtr<Progress> pProgress;
12949 pProgress.createObject();
12950 rc = pProgress->init(i_getVirtualBox(),
12951 static_cast<IMachine *>(this) /* aInitiator */,
12952 tr("Saving the execution state of the virtual machine"),
12953 FALSE /* aCancelable */);
12954 if (FAILED(rc))
12955 return rc;
12956
12957 Utf8Str strStateFilePath;
12958 i_composeSavedStateFilename(strStateFilePath);
12959
12960 /* create and start the task on a separate thread (note that it will not
12961 * start working until we release alock) */
12962 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12963 rc = pTask->createThread();
12964 if (FAILED(rc))
12965 return rc;
12966
12967 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12968 i_setMachineState(MachineState_Saving);
12969 i_updateMachineStateOnClient();
12970
12971 pProgress.queryInterfaceTo(aProgress.asOutParam());
12972
12973 return S_OK;
12974}
12975
12976/**
12977 * @note Locks this object for writing.
12978 */
12979HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12980{
12981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12982
12983 HRESULT rc = i_checkStateDependency(MutableStateDep);
12984 if (FAILED(rc)) return rc;
12985
12986 if ( mData->mMachineState != MachineState_PoweredOff
12987 && mData->mMachineState != MachineState_Teleported
12988 && mData->mMachineState != MachineState_Aborted
12989 )
12990 return setError(VBOX_E_INVALID_VM_STATE,
12991 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12992 Global::stringifyMachineState(mData->mMachineState));
12993
12994 com::Utf8Str stateFilePathFull;
12995 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12996 if (RT_FAILURE(vrc))
12997 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12998 tr("Invalid saved state file path '%s' (%Rrc)"),
12999 aSavedStateFile.c_str(),
13000 vrc);
13001
13002 mSSData->strStateFilePath = stateFilePathFull;
13003
13004 /* The below i_setMachineState() will detect the state transition and will
13005 * update the settings file */
13006
13007 return i_setMachineState(MachineState_Saved);
13008}
13009
13010/**
13011 * @note Locks this object for writing.
13012 */
13013HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13014{
13015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13016
13017 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13018 if (FAILED(rc)) return rc;
13019
13020 if (mData->mMachineState != MachineState_Saved)
13021 return setError(VBOX_E_INVALID_VM_STATE,
13022 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13023 Global::stringifyMachineState(mData->mMachineState));
13024
13025 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13026
13027 /*
13028 * Saved -> PoweredOff transition will be detected in the SessionMachine
13029 * and properly handled.
13030 */
13031 rc = i_setMachineState(MachineState_PoweredOff);
13032 return rc;
13033}
13034
13035
13036/**
13037 * @note Locks the same as #i_setMachineState() does.
13038 */
13039HRESULT SessionMachine::updateState(MachineState_T aState)
13040{
13041 return i_setMachineState(aState);
13042}
13043
13044/**
13045 * @note Locks this object for writing.
13046 */
13047HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13048{
13049 IProgress *pProgress(aProgress);
13050
13051 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13052
13053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13054
13055 if (mData->mSession.mState != SessionState_Locked)
13056 return VBOX_E_INVALID_OBJECT_STATE;
13057
13058 if (!mData->mSession.mProgress.isNull())
13059 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13060
13061 /* If we didn't reference the NAT network service yet, add a reference to
13062 * force a start */
13063 if (miNATNetworksStarted < 1)
13064 {
13065 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13066 {
13067 BOOL enabled;
13068 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13069 if ( FAILED(hrc)
13070 || !enabled)
13071 continue;
13072
13073 NetworkAttachmentType_T type;
13074 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13075 if ( SUCCEEDED(hrc)
13076 && type == NetworkAttachmentType_NATNetwork)
13077 {
13078 Bstr name;
13079 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13080 if (SUCCEEDED(hrc))
13081 {
13082 Utf8Str strName(name);
13083 LogRel(("VM '%s' starts using NAT network '%s'\n",
13084 mUserData->s.strName.c_str(), strName.c_str()));
13085 mPeer->lockHandle()->unlockWrite();
13086 mParent->i_natNetworkRefInc(strName);
13087#ifdef RT_LOCK_STRICT
13088 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13089#else
13090 mPeer->lockHandle()->lockWrite();
13091#endif
13092 }
13093 }
13094 }
13095 miNATNetworksStarted++;
13096 }
13097
13098 LogFlowThisFunc(("returns S_OK.\n"));
13099 return S_OK;
13100}
13101
13102/**
13103 * @note Locks this object for writing.
13104 */
13105HRESULT SessionMachine::endPowerUp(LONG aResult)
13106{
13107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13108
13109 if (mData->mSession.mState != SessionState_Locked)
13110 return VBOX_E_INVALID_OBJECT_STATE;
13111
13112 /* Finalize the LaunchVMProcess progress object. */
13113 if (mData->mSession.mProgress)
13114 {
13115 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13116 mData->mSession.mProgress.setNull();
13117 }
13118
13119 if (SUCCEEDED((HRESULT)aResult))
13120 {
13121#ifdef VBOX_WITH_RESOURCE_USAGE_API
13122 /* The VM has been powered up successfully, so it makes sense
13123 * now to offer the performance metrics for a running machine
13124 * object. Doing it earlier wouldn't be safe. */
13125 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13126 mData->mSession.mPID);
13127#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13128 }
13129
13130 return S_OK;
13131}
13132
13133/**
13134 * @note Locks this object for writing.
13135 */
13136HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13137{
13138 LogFlowThisFuncEnter();
13139
13140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13141
13142 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13143 E_FAIL);
13144
13145 /* create a progress object to track operation completion */
13146 ComObjPtr<Progress> pProgress;
13147 pProgress.createObject();
13148 pProgress->init(i_getVirtualBox(),
13149 static_cast<IMachine *>(this) /* aInitiator */,
13150 tr("Stopping the virtual machine"),
13151 FALSE /* aCancelable */);
13152
13153 /* fill in the console task data */
13154 mConsoleTaskData.mLastState = mData->mMachineState;
13155 mConsoleTaskData.mProgress = pProgress;
13156
13157 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13158 i_setMachineState(MachineState_Stopping);
13159
13160 pProgress.queryInterfaceTo(aProgress.asOutParam());
13161
13162 return S_OK;
13163}
13164
13165/**
13166 * @note Locks this object for writing.
13167 */
13168HRESULT SessionMachine::endPoweringDown(LONG aResult,
13169 const com::Utf8Str &aErrMsg)
13170{
13171 HRESULT const hrcResult = (HRESULT)aResult;
13172 LogFlowThisFuncEnter();
13173
13174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13175
13176 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13177 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13178 && mConsoleTaskData.mLastState != MachineState_Null,
13179 E_FAIL);
13180
13181 /*
13182 * On failure, set the state to the state we had when BeginPoweringDown()
13183 * was called (this is expected by Console::PowerDown() and the associated
13184 * task). On success the VM process already changed the state to
13185 * MachineState_PoweredOff, so no need to do anything.
13186 */
13187 if (FAILED(hrcResult))
13188 i_setMachineState(mConsoleTaskData.mLastState);
13189
13190 /* notify the progress object about operation completion */
13191 Assert(mConsoleTaskData.mProgress);
13192 if (SUCCEEDED(hrcResult))
13193 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13194 else
13195 {
13196 if (aErrMsg.length())
13197 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13198 COM_IIDOF(ISession),
13199 getComponentName(),
13200 aErrMsg.c_str());
13201 else
13202 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13203 }
13204
13205 /* clear out the temporary saved state data */
13206 mConsoleTaskData.mLastState = MachineState_Null;
13207 mConsoleTaskData.mProgress.setNull();
13208
13209 LogFlowThisFuncLeave();
13210 return S_OK;
13211}
13212
13213
13214/**
13215 * Goes through the USB filters of the given machine to see if the given
13216 * device matches any filter or not.
13217 *
13218 * @note Locks the same as USBController::hasMatchingFilter() does.
13219 */
13220HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13221 BOOL *aMatched,
13222 ULONG *aMaskedInterfaces)
13223{
13224 LogFlowThisFunc(("\n"));
13225
13226#ifdef VBOX_WITH_USB
13227 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13228#else
13229 NOREF(aDevice);
13230 NOREF(aMaskedInterfaces);
13231 *aMatched = FALSE;
13232#endif
13233
13234 return S_OK;
13235}
13236
13237/**
13238 * @note Locks the same as Host::captureUSBDevice() does.
13239 */
13240HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13241{
13242 LogFlowThisFunc(("\n"));
13243
13244#ifdef VBOX_WITH_USB
13245 /* if captureDeviceForVM() fails, it must have set extended error info */
13246 clearError();
13247 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13248 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13249 return rc;
13250
13251 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13252 AssertReturn(service, E_FAIL);
13253 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13254#else
13255 RT_NOREF(aId, aCaptureFilename);
13256 return E_NOTIMPL;
13257#endif
13258}
13259
13260/**
13261 * @note Locks the same as Host::detachUSBDevice() does.
13262 */
13263HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13264 BOOL aDone)
13265{
13266 LogFlowThisFunc(("\n"));
13267
13268#ifdef VBOX_WITH_USB
13269 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13270 AssertReturn(service, E_FAIL);
13271 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13272#else
13273 NOREF(aId);
13274 NOREF(aDone);
13275 return E_NOTIMPL;
13276#endif
13277}
13278
13279/**
13280 * Inserts all machine filters to the USB proxy service and then calls
13281 * Host::autoCaptureUSBDevices().
13282 *
13283 * Called by Console from the VM process upon VM startup.
13284 *
13285 * @note Locks what called methods lock.
13286 */
13287HRESULT SessionMachine::autoCaptureUSBDevices()
13288{
13289 LogFlowThisFunc(("\n"));
13290
13291#ifdef VBOX_WITH_USB
13292 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13293 AssertComRC(rc);
13294 NOREF(rc);
13295
13296 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13297 AssertReturn(service, E_FAIL);
13298 return service->autoCaptureDevicesForVM(this);
13299#else
13300 return S_OK;
13301#endif
13302}
13303
13304/**
13305 * Removes all machine filters from the USB proxy service and then calls
13306 * Host::detachAllUSBDevices().
13307 *
13308 * Called by Console from the VM process upon normal VM termination or by
13309 * SessionMachine::uninit() upon abnormal VM termination (from under the
13310 * Machine/SessionMachine lock).
13311 *
13312 * @note Locks what called methods lock.
13313 */
13314HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13315{
13316 LogFlowThisFunc(("\n"));
13317
13318#ifdef VBOX_WITH_USB
13319 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13320 AssertComRC(rc);
13321 NOREF(rc);
13322
13323 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13324 AssertReturn(service, E_FAIL);
13325 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13326#else
13327 NOREF(aDone);
13328 return S_OK;
13329#endif
13330}
13331
13332/**
13333 * @note Locks this object for writing.
13334 */
13335HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13336 ComPtr<IProgress> &aProgress)
13337{
13338 LogFlowThisFuncEnter();
13339
13340 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13341 /*
13342 * We don't assert below because it might happen that a non-direct session
13343 * informs us it is closed right after we've been uninitialized -- it's ok.
13344 */
13345
13346 /* get IInternalSessionControl interface */
13347 ComPtr<IInternalSessionControl> control(aSession);
13348
13349 ComAssertRet(!control.isNull(), E_INVALIDARG);
13350
13351 /* Creating a Progress object requires the VirtualBox lock, and
13352 * thus locking it here is required by the lock order rules. */
13353 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13354
13355 if (control == mData->mSession.mDirectControl)
13356 {
13357 /* The direct session is being normally closed by the client process
13358 * ----------------------------------------------------------------- */
13359
13360 /* go to the closing state (essential for all open*Session() calls and
13361 * for #i_checkForDeath()) */
13362 Assert(mData->mSession.mState == SessionState_Locked);
13363 mData->mSession.mState = SessionState_Unlocking;
13364
13365 /* set direct control to NULL to release the remote instance */
13366 mData->mSession.mDirectControl.setNull();
13367 LogFlowThisFunc(("Direct control is set to NULL\n"));
13368
13369 if (mData->mSession.mProgress)
13370 {
13371 /* finalize the progress, someone might wait if a frontend
13372 * closes the session before powering on the VM. */
13373 mData->mSession.mProgress->notifyComplete(E_FAIL,
13374 COM_IIDOF(ISession),
13375 getComponentName(),
13376 tr("The VM session was closed before any attempt to power it on"));
13377 mData->mSession.mProgress.setNull();
13378 }
13379
13380 /* Create the progress object the client will use to wait until
13381 * #i_checkForDeath() is called to uninitialize this session object after
13382 * it releases the IPC semaphore.
13383 * Note! Because we're "reusing" mProgress here, this must be a proxy
13384 * object just like for LaunchVMProcess. */
13385 Assert(mData->mSession.mProgress.isNull());
13386 ComObjPtr<ProgressProxy> progress;
13387 progress.createObject();
13388 ComPtr<IUnknown> pPeer(mPeer);
13389 progress->init(mParent, pPeer,
13390 Bstr(tr("Closing session")).raw(),
13391 FALSE /* aCancelable */);
13392 progress.queryInterfaceTo(aProgress.asOutParam());
13393 mData->mSession.mProgress = progress;
13394 }
13395 else
13396 {
13397 /* the remote session is being normally closed */
13398 bool found = false;
13399 for (Data::Session::RemoteControlList::iterator
13400 it = mData->mSession.mRemoteControls.begin();
13401 it != mData->mSession.mRemoteControls.end();
13402 ++it)
13403 {
13404 if (control == *it)
13405 {
13406 found = true;
13407 // This MUST be erase(it), not remove(*it) as the latter
13408 // triggers a very nasty use after free due to the place where
13409 // the value "lives".
13410 mData->mSession.mRemoteControls.erase(it);
13411 break;
13412 }
13413 }
13414 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13415 E_INVALIDARG);
13416 }
13417
13418 /* signal the client watcher thread, because the client is going away */
13419 mParent->i_updateClientWatcher();
13420
13421 LogFlowThisFuncLeave();
13422 return S_OK;
13423}
13424
13425HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13426 std::vector<com::Utf8Str> &aValues,
13427 std::vector<LONG64> &aTimestamps,
13428 std::vector<com::Utf8Str> &aFlags)
13429{
13430 LogFlowThisFunc(("\n"));
13431
13432#ifdef VBOX_WITH_GUEST_PROPS
13433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13434
13435 size_t cEntries = mHWData->mGuestProperties.size();
13436 aNames.resize(cEntries);
13437 aValues.resize(cEntries);
13438 aTimestamps.resize(cEntries);
13439 aFlags.resize(cEntries);
13440
13441 size_t i = 0;
13442 for (HWData::GuestPropertyMap::const_iterator
13443 it = mHWData->mGuestProperties.begin();
13444 it != mHWData->mGuestProperties.end();
13445 ++it, ++i)
13446 {
13447 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13448 aNames[i] = it->first;
13449 aValues[i] = it->second.strValue;
13450 aTimestamps[i] = it->second.mTimestamp;
13451
13452 /* If it is NULL, keep it NULL. */
13453 if (it->second.mFlags)
13454 {
13455 GuestPropWriteFlags(it->second.mFlags, szFlags);
13456 aFlags[i] = szFlags;
13457 }
13458 else
13459 aFlags[i] = "";
13460 }
13461 return S_OK;
13462#else
13463 ReturnComNotImplemented();
13464#endif
13465}
13466
13467HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13468 const com::Utf8Str &aValue,
13469 LONG64 aTimestamp,
13470 const com::Utf8Str &aFlags)
13471{
13472 LogFlowThisFunc(("\n"));
13473
13474#ifdef VBOX_WITH_GUEST_PROPS
13475 try
13476 {
13477 /*
13478 * Convert input up front.
13479 */
13480 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13481 if (aFlags.length())
13482 {
13483 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13484 AssertRCReturn(vrc, E_INVALIDARG);
13485 }
13486
13487 /*
13488 * Now grab the object lock, validate the state and do the update.
13489 */
13490
13491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13492
13493 if (!Global::IsOnline(mData->mMachineState))
13494 {
13495 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13496 VBOX_E_INVALID_VM_STATE);
13497 }
13498
13499 i_setModified(IsModified_MachineData);
13500 mHWData.backup();
13501
13502 bool fDelete = !aValue.length();
13503 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13504 if (it != mHWData->mGuestProperties.end())
13505 {
13506 if (!fDelete)
13507 {
13508 it->second.strValue = aValue;
13509 it->second.mTimestamp = aTimestamp;
13510 it->second.mFlags = fFlags;
13511 }
13512 else
13513 mHWData->mGuestProperties.erase(it);
13514
13515 mData->mGuestPropertiesModified = TRUE;
13516 }
13517 else if (!fDelete)
13518 {
13519 HWData::GuestProperty prop;
13520 prop.strValue = aValue;
13521 prop.mTimestamp = aTimestamp;
13522 prop.mFlags = fFlags;
13523
13524 mHWData->mGuestProperties[aName] = prop;
13525 mData->mGuestPropertiesModified = TRUE;
13526 }
13527
13528 alock.release();
13529
13530 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13531 }
13532 catch (...)
13533 {
13534 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13535 }
13536 return S_OK;
13537#else
13538 ReturnComNotImplemented();
13539#endif
13540}
13541
13542
13543HRESULT SessionMachine::lockMedia()
13544{
13545 AutoMultiWriteLock2 alock(this->lockHandle(),
13546 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13547
13548 AssertReturn( mData->mMachineState == MachineState_Starting
13549 || mData->mMachineState == MachineState_Restoring
13550 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13551
13552 clearError();
13553 alock.release();
13554 return i_lockMedia();
13555}
13556
13557HRESULT SessionMachine::unlockMedia()
13558{
13559 HRESULT hrc = i_unlockMedia();
13560 return hrc;
13561}
13562
13563HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13564 ComPtr<IMediumAttachment> &aNewAttachment)
13565{
13566 // request the host lock first, since might be calling Host methods for getting host drives;
13567 // next, protect the media tree all the while we're in here, as well as our member variables
13568 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13569 this->lockHandle(),
13570 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13571
13572 IMediumAttachment *iAttach = aAttachment;
13573 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13574
13575 Utf8Str ctrlName;
13576 LONG lPort;
13577 LONG lDevice;
13578 bool fTempEject;
13579 {
13580 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13581
13582 /* Need to query the details first, as the IMediumAttachment reference
13583 * might be to the original settings, which we are going to change. */
13584 ctrlName = pAttach->i_getControllerName();
13585 lPort = pAttach->i_getPort();
13586 lDevice = pAttach->i_getDevice();
13587 fTempEject = pAttach->i_getTempEject();
13588 }
13589
13590 if (!fTempEject)
13591 {
13592 /* Remember previously mounted medium. The medium before taking the
13593 * backup is not necessarily the same thing. */
13594 ComObjPtr<Medium> oldmedium;
13595 oldmedium = pAttach->i_getMedium();
13596
13597 i_setModified(IsModified_Storage);
13598 mMediumAttachments.backup();
13599
13600 // The backup operation makes the pAttach reference point to the
13601 // old settings. Re-get the correct reference.
13602 pAttach = i_findAttachment(*mMediumAttachments.data(),
13603 ctrlName,
13604 lPort,
13605 lDevice);
13606
13607 {
13608 AutoCaller autoAttachCaller(this);
13609 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13610
13611 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13612 if (!oldmedium.isNull())
13613 oldmedium->i_removeBackReference(mData->mUuid);
13614
13615 pAttach->i_updateMedium(NULL);
13616 pAttach->i_updateEjected();
13617 }
13618
13619 i_setModified(IsModified_Storage);
13620 }
13621 else
13622 {
13623 {
13624 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13625 pAttach->i_updateEjected();
13626 }
13627 }
13628
13629 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13630
13631 return S_OK;
13632}
13633
13634HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13635 com::Utf8Str &aResult)
13636{
13637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13638
13639 HRESULT hr = S_OK;
13640
13641 if (!mAuthLibCtx.hAuthLibrary)
13642 {
13643 /* Load the external authentication library. */
13644 Bstr authLibrary;
13645 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13646
13647 Utf8Str filename = authLibrary;
13648
13649 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13650 if (RT_FAILURE(vrc))
13651 hr = setErrorBoth(E_FAIL, vrc,
13652 tr("Could not load the external authentication library '%s' (%Rrc)"),
13653 filename.c_str(), vrc);
13654 }
13655
13656 /* The auth library might need the machine lock. */
13657 alock.release();
13658
13659 if (FAILED(hr))
13660 return hr;
13661
13662 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13663 {
13664 enum VRDEAuthParams
13665 {
13666 parmUuid = 1,
13667 parmGuestJudgement,
13668 parmUser,
13669 parmPassword,
13670 parmDomain,
13671 parmClientId
13672 };
13673
13674 AuthResult result = AuthResultAccessDenied;
13675
13676 Guid uuid(aAuthParams[parmUuid]);
13677 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13678 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13679
13680 result = AuthLibAuthenticate(&mAuthLibCtx,
13681 uuid.raw(), guestJudgement,
13682 aAuthParams[parmUser].c_str(),
13683 aAuthParams[parmPassword].c_str(),
13684 aAuthParams[parmDomain].c_str(),
13685 u32ClientId);
13686
13687 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13688 size_t cbPassword = aAuthParams[parmPassword].length();
13689 if (cbPassword)
13690 {
13691 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13692 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13693 }
13694
13695 if (result == AuthResultAccessGranted)
13696 aResult = "granted";
13697 else
13698 aResult = "denied";
13699
13700 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13701 aAuthParams[parmUser].c_str(), aResult.c_str()));
13702 }
13703 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13704 {
13705 enum VRDEAuthDisconnectParams
13706 {
13707 parmUuid = 1,
13708 parmClientId
13709 };
13710
13711 Guid uuid(aAuthParams[parmUuid]);
13712 uint32_t u32ClientId = 0;
13713 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13714 }
13715 else
13716 {
13717 hr = E_INVALIDARG;
13718 }
13719
13720 return hr;
13721}
13722
13723// public methods only for internal purposes
13724/////////////////////////////////////////////////////////////////////////////
13725
13726#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13727/**
13728 * Called from the client watcher thread to check for expected or unexpected
13729 * death of the client process that has a direct session to this machine.
13730 *
13731 * On Win32 and on OS/2, this method is called only when we've got the
13732 * mutex (i.e. the client has either died or terminated normally) so it always
13733 * returns @c true (the client is terminated, the session machine is
13734 * uninitialized).
13735 *
13736 * On other platforms, the method returns @c true if the client process has
13737 * terminated normally or abnormally and the session machine was uninitialized,
13738 * and @c false if the client process is still alive.
13739 *
13740 * @note Locks this object for writing.
13741 */
13742bool SessionMachine::i_checkForDeath()
13743{
13744 Uninit::Reason reason;
13745 bool terminated = false;
13746
13747 /* Enclose autoCaller with a block because calling uninit() from under it
13748 * will deadlock. */
13749 {
13750 AutoCaller autoCaller(this);
13751 if (!autoCaller.isOk())
13752 {
13753 /* return true if not ready, to cause the client watcher to exclude
13754 * the corresponding session from watching */
13755 LogFlowThisFunc(("Already uninitialized!\n"));
13756 return true;
13757 }
13758
13759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13760
13761 /* Determine the reason of death: if the session state is Closing here,
13762 * everything is fine. Otherwise it means that the client did not call
13763 * OnSessionEnd() before it released the IPC semaphore. This may happen
13764 * either because the client process has abnormally terminated, or
13765 * because it simply forgot to call ISession::Close() before exiting. We
13766 * threat the latter also as an abnormal termination (see
13767 * Session::uninit() for details). */
13768 reason = mData->mSession.mState == SessionState_Unlocking ?
13769 Uninit::Normal :
13770 Uninit::Abnormal;
13771
13772 if (mClientToken)
13773 terminated = mClientToken->release();
13774 } /* AutoCaller block */
13775
13776 if (terminated)
13777 uninit(reason);
13778
13779 return terminated;
13780}
13781
13782void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13783{
13784 LogFlowThisFunc(("\n"));
13785
13786 strTokenId.setNull();
13787
13788 AutoCaller autoCaller(this);
13789 AssertComRCReturnVoid(autoCaller.rc());
13790
13791 Assert(mClientToken);
13792 if (mClientToken)
13793 mClientToken->getId(strTokenId);
13794}
13795#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13796IToken *SessionMachine::i_getToken()
13797{
13798 LogFlowThisFunc(("\n"));
13799
13800 AutoCaller autoCaller(this);
13801 AssertComRCReturn(autoCaller.rc(), NULL);
13802
13803 Assert(mClientToken);
13804 if (mClientToken)
13805 return mClientToken->getToken();
13806 else
13807 return NULL;
13808}
13809#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13810
13811Machine::ClientToken *SessionMachine::i_getClientToken()
13812{
13813 LogFlowThisFunc(("\n"));
13814
13815 AutoCaller autoCaller(this);
13816 AssertComRCReturn(autoCaller.rc(), NULL);
13817
13818 return mClientToken;
13819}
13820
13821
13822/**
13823 * @note Locks this object for reading.
13824 */
13825HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13831
13832 ComPtr<IInternalSessionControl> directControl;
13833 {
13834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13835 if (mData->mSession.mLockType == LockType_VM)
13836 directControl = mData->mSession.mDirectControl;
13837 }
13838
13839 /* ignore notifications sent after #OnSessionEnd() is called */
13840 if (!directControl)
13841 return S_OK;
13842
13843 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13844}
13845
13846/**
13847 * @note Locks this object for reading.
13848 */
13849HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13850 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13851 const Utf8Str &aGuestIp, LONG aGuestPort)
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 AutoCaller autoCaller(this);
13856 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13857
13858 ComPtr<IInternalSessionControl> directControl;
13859 {
13860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13861 if (mData->mSession.mLockType == LockType_VM)
13862 directControl = mData->mSession.mDirectControl;
13863 }
13864
13865 /* ignore notifications sent after #OnSessionEnd() is called */
13866 if (!directControl)
13867 return S_OK;
13868 /*
13869 * instead acting like callback we ask IVirtualBox deliver corresponding event
13870 */
13871
13872 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13873 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13874 return S_OK;
13875}
13876
13877/**
13878 * @note Locks this object for reading.
13879 */
13880HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13881{
13882 LogFlowThisFunc(("\n"));
13883
13884 AutoCaller autoCaller(this);
13885 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13886
13887 ComPtr<IInternalSessionControl> directControl;
13888 {
13889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13890 if (mData->mSession.mLockType == LockType_VM)
13891 directControl = mData->mSession.mDirectControl;
13892 }
13893
13894 /* ignore notifications sent after #OnSessionEnd() is called */
13895 if (!directControl)
13896 return S_OK;
13897
13898 return directControl->OnAudioAdapterChange(audioAdapter);
13899}
13900
13901/**
13902 * @note Locks this object for reading.
13903 */
13904HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13905{
13906 LogFlowThisFunc(("\n"));
13907
13908 AutoCaller autoCaller(this);
13909 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13910
13911 ComPtr<IInternalSessionControl> directControl;
13912 {
13913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13914 if (mData->mSession.mLockType == LockType_VM)
13915 directControl = mData->mSession.mDirectControl;
13916 }
13917
13918 /* ignore notifications sent after #OnSessionEnd() is called */
13919 if (!directControl)
13920 return S_OK;
13921
13922 return directControl->OnSerialPortChange(serialPort);
13923}
13924
13925/**
13926 * @note Locks this object for reading.
13927 */
13928HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13929{
13930 LogFlowThisFunc(("\n"));
13931
13932 AutoCaller autoCaller(this);
13933 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13934
13935 ComPtr<IInternalSessionControl> directControl;
13936 {
13937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13938 if (mData->mSession.mLockType == LockType_VM)
13939 directControl = mData->mSession.mDirectControl;
13940 }
13941
13942 /* ignore notifications sent after #OnSessionEnd() is called */
13943 if (!directControl)
13944 return S_OK;
13945
13946 return directControl->OnParallelPortChange(parallelPort);
13947}
13948
13949/**
13950 * @note Locks this object for reading.
13951 */
13952HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13953{
13954 LogFlowThisFunc(("\n"));
13955
13956 AutoCaller autoCaller(this);
13957 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13958
13959 ComPtr<IInternalSessionControl> directControl;
13960 {
13961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13962 if (mData->mSession.mLockType == LockType_VM)
13963 directControl = mData->mSession.mDirectControl;
13964 }
13965
13966 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13967
13968 /* ignore notifications sent after #OnSessionEnd() is called */
13969 if (!directControl)
13970 return S_OK;
13971
13972 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13973}
13974
13975/**
13976 * @note Locks this object for reading.
13977 */
13978HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13979{
13980 LogFlowThisFunc(("\n"));
13981
13982 AutoCaller autoCaller(this);
13983 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13984
13985 ComPtr<IInternalSessionControl> directControl;
13986 {
13987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13988 if (mData->mSession.mLockType == LockType_VM)
13989 directControl = mData->mSession.mDirectControl;
13990 }
13991
13992 mParent->i_onMediumChanged(aAttachment);
13993
13994 /* ignore notifications sent after #OnSessionEnd() is called */
13995 if (!directControl)
13996 return S_OK;
13997
13998 return directControl->OnMediumChange(aAttachment, aForce);
13999}
14000
14001HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005 AutoCaller autoCaller(this);
14006 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14007
14008 ComPtr<IInternalSessionControl> directControl;
14009 {
14010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14011 if (mData->mSession.mLockType == LockType_VM)
14012 directControl = mData->mSession.mDirectControl;
14013 }
14014
14015 /* ignore notifications sent after #OnSessionEnd() is called */
14016 if (!directControl)
14017 return S_OK;
14018
14019 return directControl->OnVMProcessPriorityChange(aPriority);
14020}
14021
14022/**
14023 * @note Locks this object for reading.
14024 */
14025HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14026{
14027 LogFlowThisFunc(("\n"));
14028
14029 AutoCaller autoCaller(this);
14030 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14031
14032 ComPtr<IInternalSessionControl> directControl;
14033 {
14034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14035 if (mData->mSession.mLockType == LockType_VM)
14036 directControl = mData->mSession.mDirectControl;
14037 }
14038
14039 /* ignore notifications sent after #OnSessionEnd() is called */
14040 if (!directControl)
14041 return S_OK;
14042
14043 return directControl->OnCPUChange(aCPU, aRemove);
14044}
14045
14046HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14047{
14048 LogFlowThisFunc(("\n"));
14049
14050 AutoCaller autoCaller(this);
14051 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14052
14053 ComPtr<IInternalSessionControl> directControl;
14054 {
14055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14056 if (mData->mSession.mLockType == LockType_VM)
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14065}
14066
14067/**
14068 * @note Locks this object for reading.
14069 */
14070HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14071{
14072 LogFlowThisFunc(("\n"));
14073
14074 AutoCaller autoCaller(this);
14075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14076
14077 ComPtr<IInternalSessionControl> directControl;
14078 {
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080 if (mData->mSession.mLockType == LockType_VM)
14081 directControl = mData->mSession.mDirectControl;
14082 }
14083
14084 /* ignore notifications sent after #OnSessionEnd() is called */
14085 if (!directControl)
14086 return S_OK;
14087
14088 return directControl->OnVRDEServerChange(aRestart);
14089}
14090
14091/**
14092 * @note Locks this object for reading.
14093 */
14094HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14095{
14096 LogFlowThisFunc(("\n"));
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14100
14101 ComPtr<IInternalSessionControl> directControl;
14102 {
14103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14104 if (mData->mSession.mLockType == LockType_VM)
14105 directControl = mData->mSession.mDirectControl;
14106 }
14107
14108 /* ignore notifications sent after #OnSessionEnd() is called */
14109 if (!directControl)
14110 return S_OK;
14111
14112 return directControl->OnRecordingChange(aEnable);
14113}
14114
14115/**
14116 * @note Locks this object for reading.
14117 */
14118HRESULT SessionMachine::i_onUSBControllerChange()
14119{
14120 LogFlowThisFunc(("\n"));
14121
14122 AutoCaller autoCaller(this);
14123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14124
14125 ComPtr<IInternalSessionControl> directControl;
14126 {
14127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14128 if (mData->mSession.mLockType == LockType_VM)
14129 directControl = mData->mSession.mDirectControl;
14130 }
14131
14132 /* ignore notifications sent after #OnSessionEnd() is called */
14133 if (!directControl)
14134 return S_OK;
14135
14136 return directControl->OnUSBControllerChange();
14137}
14138
14139/**
14140 * @note Locks this object for reading.
14141 */
14142HRESULT SessionMachine::i_onSharedFolderChange()
14143{
14144 LogFlowThisFunc(("\n"));
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturnRC(autoCaller.rc());
14148
14149 ComPtr<IInternalSessionControl> directControl;
14150 {
14151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14152 if (mData->mSession.mLockType == LockType_VM)
14153 directControl = mData->mSession.mDirectControl;
14154 }
14155
14156 /* ignore notifications sent after #OnSessionEnd() is called */
14157 if (!directControl)
14158 return S_OK;
14159
14160 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14161}
14162
14163/**
14164 * @note Locks this object for reading.
14165 */
14166HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14167{
14168 LogFlowThisFunc(("\n"));
14169
14170 AutoCaller autoCaller(this);
14171 AssertComRCReturnRC(autoCaller.rc());
14172
14173 ComPtr<IInternalSessionControl> directControl;
14174 {
14175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14176 if (mData->mSession.mLockType == LockType_VM)
14177 directControl = mData->mSession.mDirectControl;
14178 }
14179
14180 /* ignore notifications sent after #OnSessionEnd() is called */
14181 if (!directControl)
14182 return S_OK;
14183
14184 return directControl->OnClipboardModeChange(aClipboardMode);
14185}
14186
14187/**
14188 * @note Locks this object for reading.
14189 */
14190HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14191{
14192 LogFlowThisFunc(("\n"));
14193
14194 AutoCaller autoCaller(this);
14195 AssertComRCReturnRC(autoCaller.rc());
14196
14197 ComPtr<IInternalSessionControl> directControl;
14198 {
14199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14200 if (mData->mSession.mLockType == LockType_VM)
14201 directControl = mData->mSession.mDirectControl;
14202 }
14203
14204 /* ignore notifications sent after #OnSessionEnd() is called */
14205 if (!directControl)
14206 return S_OK;
14207
14208 return directControl->OnClipboardFileTransferModeChange(aEnable);
14209}
14210
14211/**
14212 * @note Locks this object for reading.
14213 */
14214HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14215{
14216 LogFlowThisFunc(("\n"));
14217
14218 AutoCaller autoCaller(this);
14219 AssertComRCReturnRC(autoCaller.rc());
14220
14221 ComPtr<IInternalSessionControl> directControl;
14222 {
14223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14224 if (mData->mSession.mLockType == LockType_VM)
14225 directControl = mData->mSession.mDirectControl;
14226 }
14227
14228 /* ignore notifications sent after #OnSessionEnd() is called */
14229 if (!directControl)
14230 return S_OK;
14231
14232 return directControl->OnDnDModeChange(aDnDMode);
14233}
14234
14235/**
14236 * @note Locks this object for reading.
14237 */
14238HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14239{
14240 LogFlowThisFunc(("\n"));
14241
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14244
14245 ComPtr<IInternalSessionControl> directControl;
14246 {
14247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14248 if (mData->mSession.mLockType == LockType_VM)
14249 directControl = mData->mSession.mDirectControl;
14250 }
14251
14252 /* ignore notifications sent after #OnSessionEnd() is called */
14253 if (!directControl)
14254 return S_OK;
14255
14256 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14257}
14258
14259/**
14260 * @note Locks this object for reading.
14261 */
14262HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14263{
14264 LogFlowThisFunc(("\n"));
14265
14266 AutoCaller autoCaller(this);
14267 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14268
14269 ComPtr<IInternalSessionControl> directControl;
14270 {
14271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14272 if (mData->mSession.mLockType == LockType_VM)
14273 directControl = mData->mSession.mDirectControl;
14274 }
14275
14276 /* ignore notifications sent after #OnSessionEnd() is called */
14277 if (!directControl)
14278 return S_OK;
14279
14280 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14281}
14282
14283/**
14284 * Returns @c true if this machine's USB controller reports it has a matching
14285 * filter for the given USB device and @c false otherwise.
14286 *
14287 * @note locks this object for reading.
14288 */
14289bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14290{
14291 AutoCaller autoCaller(this);
14292 /* silently return if not ready -- this method may be called after the
14293 * direct machine session has been called */
14294 if (!autoCaller.isOk())
14295 return false;
14296
14297#ifdef VBOX_WITH_USB
14298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14299
14300 switch (mData->mMachineState)
14301 {
14302 case MachineState_Starting:
14303 case MachineState_Restoring:
14304 case MachineState_TeleportingIn:
14305 case MachineState_Paused:
14306 case MachineState_Running:
14307 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14308 * elsewhere... */
14309 alock.release();
14310 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14311 default: break;
14312 }
14313#else
14314 NOREF(aDevice);
14315 NOREF(aMaskedIfs);
14316#endif
14317 return false;
14318}
14319
14320/**
14321 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14322 */
14323HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14324 IVirtualBoxErrorInfo *aError,
14325 ULONG aMaskedIfs,
14326 const com::Utf8Str &aCaptureFilename)
14327{
14328 LogFlowThisFunc(("\n"));
14329
14330 AutoCaller autoCaller(this);
14331
14332 /* This notification may happen after the machine object has been
14333 * uninitialized (the session was closed), so don't assert. */
14334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14335
14336 ComPtr<IInternalSessionControl> directControl;
14337 {
14338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14339 if (mData->mSession.mLockType == LockType_VM)
14340 directControl = mData->mSession.mDirectControl;
14341 }
14342
14343 /* fail on notifications sent after #OnSessionEnd() is called, it is
14344 * expected by the caller */
14345 if (!directControl)
14346 return E_FAIL;
14347
14348 /* No locks should be held at this point. */
14349 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14350 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14351
14352 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14353}
14354
14355/**
14356 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14357 */
14358HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14359 IVirtualBoxErrorInfo *aError)
14360{
14361 LogFlowThisFunc(("\n"));
14362
14363 AutoCaller autoCaller(this);
14364
14365 /* This notification may happen after the machine object has been
14366 * uninitialized (the session was closed), so don't assert. */
14367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14368
14369 ComPtr<IInternalSessionControl> directControl;
14370 {
14371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14372 if (mData->mSession.mLockType == LockType_VM)
14373 directControl = mData->mSession.mDirectControl;
14374 }
14375
14376 /* fail on notifications sent after #OnSessionEnd() is called, it is
14377 * expected by the caller */
14378 if (!directControl)
14379 return E_FAIL;
14380
14381 /* No locks should be held at this point. */
14382 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14383 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14384
14385 return directControl->OnUSBDeviceDetach(aId, aError);
14386}
14387
14388// protected methods
14389/////////////////////////////////////////////////////////////////////////////
14390
14391/**
14392 * Deletes the given file if it is no longer in use by either the current machine state
14393 * (if the machine is "saved") or any of the machine's snapshots.
14394 *
14395 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14396 * but is different for each SnapshotMachine. When calling this, the order of calling this
14397 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14398 * is therefore critical. I know, it's all rather messy.
14399 *
14400 * @param strStateFile
14401 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14402 * the test for whether the saved state file is in use.
14403 */
14404void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14405 Snapshot *pSnapshotToIgnore)
14406{
14407 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14408 if ( (strStateFile.isNotEmpty())
14409 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14410 )
14411 // ... and it must also not be shared with other snapshots
14412 if ( !mData->mFirstSnapshot
14413 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14414 // this checks the SnapshotMachine's state file paths
14415 )
14416 RTFileDelete(strStateFile.c_str());
14417}
14418
14419/**
14420 * Locks the attached media.
14421 *
14422 * All attached hard disks are locked for writing and DVD/floppy are locked for
14423 * reading. Parents of attached hard disks (if any) are locked for reading.
14424 *
14425 * This method also performs accessibility check of all media it locks: if some
14426 * media is inaccessible, the method will return a failure and a bunch of
14427 * extended error info objects per each inaccessible medium.
14428 *
14429 * Note that this method is atomic: if it returns a success, all media are
14430 * locked as described above; on failure no media is locked at all (all
14431 * succeeded individual locks will be undone).
14432 *
14433 * The caller is responsible for doing the necessary state sanity checks.
14434 *
14435 * The locks made by this method must be undone by calling #unlockMedia() when
14436 * no more needed.
14437 */
14438HRESULT SessionMachine::i_lockMedia()
14439{
14440 AutoCaller autoCaller(this);
14441 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14442
14443 AutoMultiWriteLock2 alock(this->lockHandle(),
14444 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14445
14446 /* bail out if trying to lock things with already set up locking */
14447 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14448
14449 MultiResult mrc(S_OK);
14450
14451 /* Collect locking information for all medium objects attached to the VM. */
14452 for (MediumAttachmentList::const_iterator
14453 it = mMediumAttachments->begin();
14454 it != mMediumAttachments->end();
14455 ++it)
14456 {
14457 MediumAttachment *pAtt = *it;
14458 DeviceType_T devType = pAtt->i_getType();
14459 Medium *pMedium = pAtt->i_getMedium();
14460
14461 MediumLockList *pMediumLockList(new MediumLockList());
14462 // There can be attachments without a medium (floppy/dvd), and thus
14463 // it's impossible to create a medium lock list. It still makes sense
14464 // to have the empty medium lock list in the map in case a medium is
14465 // attached later.
14466 if (pMedium != NULL)
14467 {
14468 MediumType_T mediumType = pMedium->i_getType();
14469 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14470 || mediumType == MediumType_Shareable;
14471 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14472
14473 alock.release();
14474 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14475 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14476 false /* fMediumLockWriteAll */,
14477 NULL,
14478 *pMediumLockList);
14479 alock.acquire();
14480 if (FAILED(mrc))
14481 {
14482 delete pMediumLockList;
14483 mData->mSession.mLockedMedia.Clear();
14484 break;
14485 }
14486 }
14487
14488 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14489 if (FAILED(rc))
14490 {
14491 mData->mSession.mLockedMedia.Clear();
14492 mrc = setError(rc,
14493 tr("Collecting locking information for all attached media failed"));
14494 break;
14495 }
14496 }
14497
14498 if (SUCCEEDED(mrc))
14499 {
14500 /* Now lock all media. If this fails, nothing is locked. */
14501 alock.release();
14502 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14503 alock.acquire();
14504 if (FAILED(rc))
14505 {
14506 mrc = setError(rc,
14507 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14508 }
14509 }
14510
14511 return mrc;
14512}
14513
14514/**
14515 * Undoes the locks made by by #lockMedia().
14516 */
14517HRESULT SessionMachine::i_unlockMedia()
14518{
14519 AutoCaller autoCaller(this);
14520 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14521
14522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14523
14524 /* we may be holding important error info on the current thread;
14525 * preserve it */
14526 ErrorInfoKeeper eik;
14527
14528 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14529 AssertComRC(rc);
14530 return rc;
14531}
14532
14533/**
14534 * Helper to change the machine state (reimplementation).
14535 *
14536 * @note Locks this object for writing.
14537 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14538 * it can cause crashes in random places due to unexpectedly committing
14539 * the current settings. The caller is responsible for that. The call
14540 * to saveStateSettings is fine, because this method does not commit.
14541 */
14542HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14543{
14544 LogFlowThisFuncEnter();
14545 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14546
14547 AutoCaller autoCaller(this);
14548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14549
14550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14551
14552 MachineState_T oldMachineState = mData->mMachineState;
14553
14554 AssertMsgReturn(oldMachineState != aMachineState,
14555 ("oldMachineState=%s, aMachineState=%s\n",
14556 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14557 E_FAIL);
14558
14559 HRESULT rc = S_OK;
14560
14561 int stsFlags = 0;
14562 bool deleteSavedState = false;
14563
14564 /* detect some state transitions */
14565
14566 if ( ( oldMachineState == MachineState_Saved
14567 && aMachineState == MachineState_Restoring)
14568 || ( ( oldMachineState == MachineState_PoweredOff
14569 || oldMachineState == MachineState_Teleported
14570 || oldMachineState == MachineState_Aborted
14571 )
14572 && ( aMachineState == MachineState_TeleportingIn
14573 || aMachineState == MachineState_Starting
14574 )
14575 )
14576 )
14577 {
14578 /* The EMT thread is about to start */
14579
14580 /* Nothing to do here for now... */
14581
14582 /// @todo NEWMEDIA don't let mDVDDrive and other children
14583 /// change anything when in the Starting/Restoring state
14584 }
14585 else if ( ( oldMachineState == MachineState_Running
14586 || oldMachineState == MachineState_Paused
14587 || oldMachineState == MachineState_Teleporting
14588 || oldMachineState == MachineState_OnlineSnapshotting
14589 || oldMachineState == MachineState_LiveSnapshotting
14590 || oldMachineState == MachineState_Stuck
14591 || oldMachineState == MachineState_Starting
14592 || oldMachineState == MachineState_Stopping
14593 || oldMachineState == MachineState_Saving
14594 || oldMachineState == MachineState_Restoring
14595 || oldMachineState == MachineState_TeleportingPausedVM
14596 || oldMachineState == MachineState_TeleportingIn
14597 )
14598 && ( aMachineState == MachineState_PoweredOff
14599 || aMachineState == MachineState_Saved
14600 || aMachineState == MachineState_Teleported
14601 || aMachineState == MachineState_Aborted
14602 )
14603 )
14604 {
14605 /* The EMT thread has just stopped, unlock attached media. Note that as
14606 * opposed to locking that is done from Console, we do unlocking here
14607 * because the VM process may have aborted before having a chance to
14608 * properly unlock all media it locked. */
14609
14610 unlockMedia();
14611 }
14612
14613 if (oldMachineState == MachineState_Restoring)
14614 {
14615 if (aMachineState != MachineState_Saved)
14616 {
14617 /*
14618 * delete the saved state file once the machine has finished
14619 * restoring from it (note that Console sets the state from
14620 * Restoring to Saved if the VM couldn't restore successfully,
14621 * to give the user an ability to fix an error and retry --
14622 * we keep the saved state file in this case)
14623 */
14624 deleteSavedState = true;
14625 }
14626 }
14627 else if ( oldMachineState == MachineState_Saved
14628 && ( aMachineState == MachineState_PoweredOff
14629 || aMachineState == MachineState_Aborted
14630 || aMachineState == MachineState_Teleported
14631 )
14632 )
14633 {
14634 /*
14635 * delete the saved state after SessionMachine::ForgetSavedState() is called
14636 * or if the VM process (owning a direct VM session) crashed while the
14637 * VM was Saved
14638 */
14639
14640 /// @todo (dmik)
14641 // Not sure that deleting the saved state file just because of the
14642 // client death before it attempted to restore the VM is a good
14643 // thing. But when it crashes we need to go to the Aborted state
14644 // which cannot have the saved state file associated... The only
14645 // way to fix this is to make the Aborted condition not a VM state
14646 // but a bool flag: i.e., when a crash occurs, set it to true and
14647 // change the state to PoweredOff or Saved depending on the
14648 // saved state presence.
14649
14650 deleteSavedState = true;
14651 mData->mCurrentStateModified = TRUE;
14652 stsFlags |= SaveSTS_CurStateModified;
14653 }
14654
14655 if ( aMachineState == MachineState_Starting
14656 || aMachineState == MachineState_Restoring
14657 || aMachineState == MachineState_TeleportingIn
14658 )
14659 {
14660 /* set the current state modified flag to indicate that the current
14661 * state is no more identical to the state in the
14662 * current snapshot */
14663 if (!mData->mCurrentSnapshot.isNull())
14664 {
14665 mData->mCurrentStateModified = TRUE;
14666 stsFlags |= SaveSTS_CurStateModified;
14667 }
14668 }
14669
14670 if (deleteSavedState)
14671 {
14672 if (mRemoveSavedState)
14673 {
14674 Assert(!mSSData->strStateFilePath.isEmpty());
14675
14676 // it is safe to delete the saved state file if ...
14677 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14678 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14679 // ... none of the snapshots share the saved state file
14680 )
14681 RTFileDelete(mSSData->strStateFilePath.c_str());
14682 }
14683
14684 mSSData->strStateFilePath.setNull();
14685 stsFlags |= SaveSTS_StateFilePath;
14686 }
14687
14688 /* redirect to the underlying peer machine */
14689 mPeer->i_setMachineState(aMachineState);
14690
14691 if ( oldMachineState != MachineState_RestoringSnapshot
14692 && ( aMachineState == MachineState_PoweredOff
14693 || aMachineState == MachineState_Teleported
14694 || aMachineState == MachineState_Aborted
14695 || aMachineState == MachineState_Saved))
14696 {
14697 /* the machine has stopped execution
14698 * (or the saved state file was adopted) */
14699 stsFlags |= SaveSTS_StateTimeStamp;
14700 }
14701
14702 if ( ( oldMachineState == MachineState_PoweredOff
14703 || oldMachineState == MachineState_Aborted
14704 || oldMachineState == MachineState_Teleported
14705 )
14706 && aMachineState == MachineState_Saved)
14707 {
14708 /* the saved state file was adopted */
14709 Assert(!mSSData->strStateFilePath.isEmpty());
14710 stsFlags |= SaveSTS_StateFilePath;
14711 }
14712
14713#ifdef VBOX_WITH_GUEST_PROPS
14714 if ( aMachineState == MachineState_PoweredOff
14715 || aMachineState == MachineState_Aborted
14716 || aMachineState == MachineState_Teleported)
14717 {
14718 /* Make sure any transient guest properties get removed from the
14719 * property store on shutdown. */
14720 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14721
14722 /* remove it from the settings representation */
14723 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14724 for (settings::GuestPropertiesList::iterator
14725 it = llGuestProperties.begin();
14726 it != llGuestProperties.end();
14727 /*nothing*/)
14728 {
14729 const settings::GuestProperty &prop = *it;
14730 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14731 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14732 {
14733 it = llGuestProperties.erase(it);
14734 fNeedsSaving = true;
14735 }
14736 else
14737 {
14738 ++it;
14739 }
14740 }
14741
14742 /* Additionally remove it from the HWData representation. Required to
14743 * keep everything in sync, as this is what the API keeps using. */
14744 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14745 for (HWData::GuestPropertyMap::iterator
14746 it = llHWGuestProperties.begin();
14747 it != llHWGuestProperties.end();
14748 /*nothing*/)
14749 {
14750 uint32_t fFlags = it->second.mFlags;
14751 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14752 {
14753 /* iterator where we need to continue after the erase call
14754 * (C++03 is a fact still, and it doesn't return the iterator
14755 * which would allow continuing) */
14756 HWData::GuestPropertyMap::iterator it2 = it;
14757 ++it2;
14758 llHWGuestProperties.erase(it);
14759 it = it2;
14760 fNeedsSaving = true;
14761 }
14762 else
14763 {
14764 ++it;
14765 }
14766 }
14767
14768 if (fNeedsSaving)
14769 {
14770 mData->mCurrentStateModified = TRUE;
14771 stsFlags |= SaveSTS_CurStateModified;
14772 }
14773 }
14774#endif /* VBOX_WITH_GUEST_PROPS */
14775
14776 rc = i_saveStateSettings(stsFlags);
14777
14778 if ( ( oldMachineState != MachineState_PoweredOff
14779 && oldMachineState != MachineState_Aborted
14780 && oldMachineState != MachineState_Teleported
14781 )
14782 && ( aMachineState == MachineState_PoweredOff
14783 || aMachineState == MachineState_Aborted
14784 || aMachineState == MachineState_Teleported
14785 )
14786 )
14787 {
14788 /* we've been shut down for any reason */
14789 /* no special action so far */
14790 }
14791
14792 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14793 LogFlowThisFuncLeave();
14794 return rc;
14795}
14796
14797/**
14798 * Sends the current machine state value to the VM process.
14799 *
14800 * @note Locks this object for reading, then calls a client process.
14801 */
14802HRESULT SessionMachine::i_updateMachineStateOnClient()
14803{
14804 AutoCaller autoCaller(this);
14805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14806
14807 ComPtr<IInternalSessionControl> directControl;
14808 {
14809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14810 AssertReturn(!!mData, E_FAIL);
14811 if (mData->mSession.mLockType == LockType_VM)
14812 directControl = mData->mSession.mDirectControl;
14813
14814 /* directControl may be already set to NULL here in #OnSessionEnd()
14815 * called too early by the direct session process while there is still
14816 * some operation (like deleting the snapshot) in progress. The client
14817 * process in this case is waiting inside Session::close() for the
14818 * "end session" process object to complete, while #uninit() called by
14819 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14820 * operation to complete. For now, we accept this inconsistent behavior
14821 * and simply do nothing here. */
14822
14823 if (mData->mSession.mState == SessionState_Unlocking)
14824 return S_OK;
14825 }
14826
14827 /* ignore notifications sent after #OnSessionEnd() is called */
14828 if (!directControl)
14829 return S_OK;
14830
14831 return directControl->UpdateMachineState(mData->mMachineState);
14832}
14833
14834
14835/*static*/
14836HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14837{
14838 va_list args;
14839 va_start(args, pcszMsg);
14840 HRESULT rc = setErrorInternal(aResultCode,
14841 getStaticClassIID(),
14842 getStaticComponentName(),
14843 Utf8Str(pcszMsg, args),
14844 false /* aWarning */,
14845 true /* aLogIt */);
14846 va_end(args);
14847 return rc;
14848}
14849
14850
14851HRESULT Machine::updateState(MachineState_T aState)
14852{
14853 NOREF(aState);
14854 ReturnComNotImplemented();
14855}
14856
14857HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14858{
14859 NOREF(aProgress);
14860 ReturnComNotImplemented();
14861}
14862
14863HRESULT Machine::endPowerUp(LONG aResult)
14864{
14865 NOREF(aResult);
14866 ReturnComNotImplemented();
14867}
14868
14869HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14870{
14871 NOREF(aProgress);
14872 ReturnComNotImplemented();
14873}
14874
14875HRESULT Machine::endPoweringDown(LONG aResult,
14876 const com::Utf8Str &aErrMsg)
14877{
14878 NOREF(aResult);
14879 NOREF(aErrMsg);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14884 BOOL *aMatched,
14885 ULONG *aMaskedInterfaces)
14886{
14887 NOREF(aDevice);
14888 NOREF(aMatched);
14889 NOREF(aMaskedInterfaces);
14890 ReturnComNotImplemented();
14891
14892}
14893
14894HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14895{
14896 NOREF(aId); NOREF(aCaptureFilename);
14897 ReturnComNotImplemented();
14898}
14899
14900HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14901 BOOL aDone)
14902{
14903 NOREF(aId);
14904 NOREF(aDone);
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::autoCaptureUSBDevices()
14909{
14910 ReturnComNotImplemented();
14911}
14912
14913HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14914{
14915 NOREF(aDone);
14916 ReturnComNotImplemented();
14917}
14918
14919HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14920 ComPtr<IProgress> &aProgress)
14921{
14922 NOREF(aSession);
14923 NOREF(aProgress);
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::finishOnlineMergeMedium()
14928{
14929 ReturnComNotImplemented();
14930}
14931
14932HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14933 std::vector<com::Utf8Str> &aValues,
14934 std::vector<LONG64> &aTimestamps,
14935 std::vector<com::Utf8Str> &aFlags)
14936{
14937 NOREF(aNames);
14938 NOREF(aValues);
14939 NOREF(aTimestamps);
14940 NOREF(aFlags);
14941 ReturnComNotImplemented();
14942}
14943
14944HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14945 const com::Utf8Str &aValue,
14946 LONG64 aTimestamp,
14947 const com::Utf8Str &aFlags)
14948{
14949 NOREF(aName);
14950 NOREF(aValue);
14951 NOREF(aTimestamp);
14952 NOREF(aFlags);
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::lockMedia()
14957{
14958 ReturnComNotImplemented();
14959}
14960
14961HRESULT Machine::unlockMedia()
14962{
14963 ReturnComNotImplemented();
14964}
14965
14966HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14967 ComPtr<IMediumAttachment> &aNewAttachment)
14968{
14969 NOREF(aAttachment);
14970 NOREF(aNewAttachment);
14971 ReturnComNotImplemented();
14972}
14973
14974HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14975 ULONG aCpuUser,
14976 ULONG aCpuKernel,
14977 ULONG aCpuIdle,
14978 ULONG aMemTotal,
14979 ULONG aMemFree,
14980 ULONG aMemBalloon,
14981 ULONG aMemShared,
14982 ULONG aMemCache,
14983 ULONG aPagedTotal,
14984 ULONG aMemAllocTotal,
14985 ULONG aMemFreeTotal,
14986 ULONG aMemBalloonTotal,
14987 ULONG aMemSharedTotal,
14988 ULONG aVmNetRx,
14989 ULONG aVmNetTx)
14990{
14991 NOREF(aValidStats);
14992 NOREF(aCpuUser);
14993 NOREF(aCpuKernel);
14994 NOREF(aCpuIdle);
14995 NOREF(aMemTotal);
14996 NOREF(aMemFree);
14997 NOREF(aMemBalloon);
14998 NOREF(aMemShared);
14999 NOREF(aMemCache);
15000 NOREF(aPagedTotal);
15001 NOREF(aMemAllocTotal);
15002 NOREF(aMemFreeTotal);
15003 NOREF(aMemBalloonTotal);
15004 NOREF(aMemSharedTotal);
15005 NOREF(aVmNetRx);
15006 NOREF(aVmNetTx);
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15011 com::Utf8Str &aResult)
15012{
15013 NOREF(aAuthParams);
15014 NOREF(aResult);
15015 ReturnComNotImplemented();
15016}
15017
15018com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15019{
15020 com::Utf8Str strControllerName = "Unknown";
15021 switch (aBusType)
15022 {
15023 case StorageBus_IDE:
15024 {
15025 strControllerName = "IDE";
15026 break;
15027 }
15028 case StorageBus_SATA:
15029 {
15030 strControllerName = "SATA";
15031 break;
15032 }
15033 case StorageBus_SCSI:
15034 {
15035 strControllerName = "SCSI";
15036 break;
15037 }
15038 case StorageBus_Floppy:
15039 {
15040 strControllerName = "Floppy";
15041 break;
15042 }
15043 case StorageBus_SAS:
15044 {
15045 strControllerName = "SAS";
15046 break;
15047 }
15048 case StorageBus_USB:
15049 {
15050 strControllerName = "USB";
15051 break;
15052 }
15053 default:
15054 break;
15055 }
15056 return strControllerName;
15057}
15058
15059HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15060{
15061 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15062
15063 AutoCaller autoCaller(this);
15064 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15065
15066 HRESULT rc = S_OK;
15067
15068 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15069 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15070 rc = getUSBDeviceFilters(usbDeviceFilters);
15071 if (FAILED(rc)) return rc;
15072
15073 NOREF(aFlags);
15074 com::Utf8Str osTypeId;
15075 ComObjPtr<GuestOSType> osType = NULL;
15076
15077 /* Get the guest os type as a string from the VB. */
15078 rc = getOSTypeId(osTypeId);
15079 if (FAILED(rc)) return rc;
15080
15081 /* Get the os type obj that coresponds, can be used to get
15082 * the defaults for this guest OS. */
15083 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15084 if (FAILED(rc)) return rc;
15085
15086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15087
15088 /* Let the OS type select 64-bit ness. */
15089 mHWData->mLongMode = osType->i_is64Bit()
15090 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15091
15092 /* Let the OS type enable the X2APIC */
15093 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15094
15095 /* This one covers IOAPICEnabled. */
15096 mBIOSSettings->i_applyDefaults(osType);
15097
15098 /* Initialize default record settings. */
15099 mRecordingSettings->i_applyDefaults();
15100
15101 /* Initialize default BIOS settings here */
15102 /* Hardware virtualization must be ON by default */
15103 mHWData->mAPIC = true;
15104 mHWData->mHWVirtExEnabled = true;
15105
15106 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15107 if (FAILED(rc)) return rc;
15108
15109 /* Graphics stuff. */
15110 GraphicsControllerType_T graphicsController;
15111 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15112 if (FAILED(rc)) return rc;
15113
15114 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15115 if (FAILED(rc)) return rc;
15116
15117 ULONG vramSize;
15118 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15119 if (FAILED(rc)) return rc;
15120
15121 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15122 if (FAILED(rc)) return rc;
15123
15124 BOOL fAccelerate2DVideoEnabled;
15125 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15126 if (FAILED(rc)) return rc;
15127
15128 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15129 if (FAILED(rc)) return rc;
15130
15131 BOOL fAccelerate3DEnabled;
15132 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15133 if (FAILED(rc)) return rc;
15134
15135 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15136 if (FAILED(rc)) return rc;
15137
15138 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15139 if (FAILED(rc)) return rc;
15140
15141 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15142 if (FAILED(rc)) return rc;
15143
15144 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15145 if (FAILED(rc)) return rc;
15146
15147 BOOL mRTCUseUTC;
15148 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15149 if (FAILED(rc)) return rc;
15150
15151 setRTCUseUTC(mRTCUseUTC);
15152 if (FAILED(rc)) return rc;
15153
15154 /* the setter does more than just the assignment, so use it */
15155 ChipsetType_T enmChipsetType;
15156 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15157 if (FAILED(rc)) return rc;
15158
15159 rc = COMSETTER(ChipsetType)(enmChipsetType);
15160 if (FAILED(rc)) return rc;
15161
15162 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15163 if (FAILED(rc)) return rc;
15164
15165 /* Apply IOMMU defaults. */
15166 IommuType_T enmIommuType;
15167 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15168 if (FAILED(rc)) return rc;
15169
15170 rc = COMSETTER(IommuType)(enmIommuType);
15171 if (FAILED(rc)) return rc;
15172
15173 /* Apply network adapters defaults */
15174 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15175 mNetworkAdapters[slot]->i_applyDefaults(osType);
15176
15177 /* Apply serial port defaults */
15178 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15179 mSerialPorts[slot]->i_applyDefaults(osType);
15180
15181 /* Apply parallel port defaults - not OS dependent*/
15182 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15183 mParallelPorts[slot]->i_applyDefaults();
15184
15185 /* Audio stuff. */
15186 AudioControllerType_T audioController;
15187 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15191 if (FAILED(rc)) return rc;
15192
15193 AudioCodecType_T audioCodec;
15194 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15195 if (FAILED(rc)) return rc;
15196
15197 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15198 if (FAILED(rc)) return rc;
15199
15200 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15201 if (FAILED(rc)) return rc;
15202
15203 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15204 if (FAILED(rc)) return rc;
15205
15206 /* Storage Controllers */
15207 StorageControllerType_T hdStorageControllerType;
15208 StorageBus_T hdStorageBusType;
15209 StorageControllerType_T dvdStorageControllerType;
15210 StorageBus_T dvdStorageBusType;
15211 BOOL recommendedFloppy;
15212 ComPtr<IStorageController> floppyController;
15213 ComPtr<IStorageController> hdController;
15214 ComPtr<IStorageController> dvdController;
15215 Utf8Str strFloppyName, strDVDName, strHDName;
15216
15217 /* GUI auto generates controller names using bus type. Do the same*/
15218 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15219
15220 /* Floppy recommended? add one. */
15221 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15222 if (FAILED(rc)) return rc;
15223 if (recommendedFloppy)
15224 {
15225 rc = addStorageController(strFloppyName,
15226 StorageBus_Floppy,
15227 floppyController);
15228 if (FAILED(rc)) return rc;
15229 }
15230
15231 /* Setup one DVD storage controller. */
15232 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15233 if (FAILED(rc)) return rc;
15234
15235 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15236 if (FAILED(rc)) return rc;
15237
15238 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15239
15240 rc = addStorageController(strDVDName,
15241 dvdStorageBusType,
15242 dvdController);
15243 if (FAILED(rc)) return rc;
15244
15245 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15246 if (FAILED(rc)) return rc;
15247
15248 /* Setup one HDD storage controller. */
15249 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15250 if (FAILED(rc)) return rc;
15251
15252 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15253 if (FAILED(rc)) return rc;
15254
15255 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15256
15257 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15258 {
15259 rc = addStorageController(strHDName,
15260 hdStorageBusType,
15261 hdController);
15262 if (FAILED(rc)) return rc;
15263
15264 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15265 if (FAILED(rc)) return rc;
15266 }
15267 else
15268 {
15269 /* The HD controller is the same as DVD: */
15270 hdController = dvdController;
15271 }
15272
15273 /* Limit the AHCI port count if it's used because windows has trouble with
15274 * too many ports and other guest (OS X in particular) may take extra long
15275 * boot: */
15276
15277 // pParent = static_cast<Medium*>(aP)
15278 IStorageController *temp = hdController;
15279 ComObjPtr<StorageController> storageController;
15280 storageController = static_cast<StorageController *>(temp);
15281
15282 // tempHDController = aHDController;
15283 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15284 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15285 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15286 storageController->COMSETTER(PortCount)(1);
15287
15288 /* USB stuff */
15289
15290 bool ohciEnabled = false;
15291
15292 ComPtr<IUSBController> usbController;
15293 BOOL recommendedUSB3;
15294 BOOL recommendedUSB;
15295 BOOL usbProxyAvailable;
15296
15297 getUSBProxyAvailable(&usbProxyAvailable);
15298 if (FAILED(rc)) return rc;
15299
15300 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15301 if (FAILED(rc)) return rc;
15302 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15303 if (FAILED(rc)) return rc;
15304
15305 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15306 {
15307#ifdef VBOX_WITH_EXTPACK
15308 /* USB 3.0 is only available if the proper ExtPack is installed. */
15309 ExtPackManager *aManager = mParent->i_getExtPackManager();
15310 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15311 {
15312 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15313 if (FAILED(rc)) return rc;
15314
15315 /* xHci includes OHCI */
15316 ohciEnabled = true;
15317 }
15318#endif
15319 }
15320 if ( !ohciEnabled
15321 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15322 {
15323 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15324 if (FAILED(rc)) return rc;
15325 ohciEnabled = true;
15326
15327#ifdef VBOX_WITH_EXTPACK
15328 /* USB 2.0 is only available if the proper ExtPack is installed.
15329 * Note. Configuring EHCI here and providing messages about
15330 * the missing extpack isn't exactly clean, but it is a
15331 * necessary evil to patch over legacy compatability issues
15332 * introduced by the new distribution model. */
15333 ExtPackManager *manager = mParent->i_getExtPackManager();
15334 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15335 {
15336 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15337 if (FAILED(rc)) return rc;
15338 }
15339#endif
15340 }
15341
15342 /* Set recommended human interface device types: */
15343 BOOL recommendedUSBHID;
15344 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15345 if (FAILED(rc)) return rc;
15346
15347 if (recommendedUSBHID)
15348 {
15349 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15350 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15351 if (!ohciEnabled && !usbDeviceFilters.isNull())
15352 {
15353 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15354 if (FAILED(rc)) return rc;
15355 }
15356 }
15357
15358 BOOL recommendedUSBTablet;
15359 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15360 if (FAILED(rc)) return rc;
15361
15362 if (recommendedUSBTablet)
15363 {
15364 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15365 if (!ohciEnabled && !usbDeviceFilters.isNull())
15366 {
15367 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15368 if (FAILED(rc)) return rc;
15369 }
15370 }
15371 return S_OK;
15372}
15373
15374/* This isn't handled entirely by the wrapper generator yet. */
15375#ifdef VBOX_WITH_XPCOM
15376NS_DECL_CLASSINFO(SessionMachine)
15377NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15378
15379NS_DECL_CLASSINFO(SnapshotMachine)
15380NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15381#endif
Note: See TracBrowser for help on using the repository browser.

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