VirtualBox

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

Last change on this file since 88539 was 88365, checked in by vboxsync, 4 years ago

Intel IOMMU: bugref:9967 Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.6 KB
Line 
1/* $Id: MachineImpl.cpp 88365 2021-04-05 06:28:17Z 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 == "Windows2012"
1379 || mUserData->s.strOsType == "Windows2012_64"
1380 || mUserData->s.strOsType == "Windows2008"
1381 || mUserData->s.strOsType == "Windows2008_64")
1382 {
1383 *aParavirtProvider = ParavirtProvider_HyperV;
1384 }
1385 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1386 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1387 || mUserData->s.strOsType == "Linux"
1388 || mUserData->s.strOsType == "Linux_64"
1389 || mUserData->s.strOsType == "ArchLinux"
1390 || mUserData->s.strOsType == "ArchLinux_64"
1391 || mUserData->s.strOsType == "Debian"
1392 || mUserData->s.strOsType == "Debian_64"
1393 || mUserData->s.strOsType == "Fedora"
1394 || mUserData->s.strOsType == "Fedora_64"
1395 || mUserData->s.strOsType == "Gentoo"
1396 || mUserData->s.strOsType == "Gentoo_64"
1397 || mUserData->s.strOsType == "Mandriva"
1398 || mUserData->s.strOsType == "Mandriva_64"
1399 || mUserData->s.strOsType == "OpenSUSE"
1400 || mUserData->s.strOsType == "OpenSUSE_64"
1401 || mUserData->s.strOsType == "Oracle"
1402 || mUserData->s.strOsType == "Oracle_64"
1403 || mUserData->s.strOsType == "RedHat"
1404 || mUserData->s.strOsType == "RedHat_64"
1405 || mUserData->s.strOsType == "Turbolinux"
1406 || mUserData->s.strOsType == "Turbolinux_64"
1407 || mUserData->s.strOsType == "Ubuntu"
1408 || mUserData->s.strOsType == "Ubuntu_64"
1409 || mUserData->s.strOsType == "Xandros"
1410 || mUserData->s.strOsType == "Xandros_64")
1411 {
1412 *aParavirtProvider = ParavirtProvider_KVM;
1413 }
1414 else
1415 *aParavirtProvider = ParavirtProvider_None;
1416 break;
1417 }
1418
1419 default: AssertFailedBreak(); /* Shut up MSC. */
1420 }
1421 break;
1422 }
1423 }
1424
1425 Assert( *aParavirtProvider == ParavirtProvider_None
1426 || *aParavirtProvider == ParavirtProvider_Minimal
1427 || *aParavirtProvider == ParavirtProvider_HyperV
1428 || *aParavirtProvider == ParavirtProvider_KVM);
1429 return S_OK;
1430}
1431
1432HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1433{
1434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1435
1436 aHardwareVersion = mHWData->mHWVersion;
1437
1438 return S_OK;
1439}
1440
1441HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1442{
1443 /* check known version */
1444 Utf8Str hwVersion = aHardwareVersion;
1445 if ( hwVersion.compare("1") != 0
1446 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1447 return setError(E_INVALIDARG,
1448 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1449
1450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1451
1452 HRESULT rc = i_checkStateDependency(MutableStateDep);
1453 if (FAILED(rc)) return rc;
1454
1455 i_setModified(IsModified_MachineData);
1456 mHWData.backup();
1457 mHWData->mHWVersion = aHardwareVersion;
1458
1459 return S_OK;
1460}
1461
1462HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1463{
1464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1465
1466 if (!mHWData->mHardwareUUID.isZero())
1467 aHardwareUUID = mHWData->mHardwareUUID;
1468 else
1469 aHardwareUUID = mData->mUuid;
1470
1471 return S_OK;
1472}
1473
1474HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1475{
1476 if (!aHardwareUUID.isValid())
1477 return E_INVALIDARG;
1478
1479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 HRESULT rc = i_checkStateDependency(MutableStateDep);
1482 if (FAILED(rc)) return rc;
1483
1484 i_setModified(IsModified_MachineData);
1485 mHWData.backup();
1486 if (aHardwareUUID == mData->mUuid)
1487 mHWData->mHardwareUUID.clear();
1488 else
1489 mHWData->mHardwareUUID = aHardwareUUID;
1490
1491 return S_OK;
1492}
1493
1494HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1495{
1496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1497
1498 *aMemorySize = mHWData->mMemorySize;
1499
1500 return S_OK;
1501}
1502
1503HRESULT Machine::setMemorySize(ULONG aMemorySize)
1504{
1505 /* check RAM limits */
1506 if ( aMemorySize < MM_RAM_MIN_IN_MB
1507 || aMemorySize > MM_RAM_MAX_IN_MB
1508 )
1509 return setError(E_INVALIDARG,
1510 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1511 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1512
1513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1514
1515 HRESULT rc = i_checkStateDependency(MutableStateDep);
1516 if (FAILED(rc)) return rc;
1517
1518 i_setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 mHWData->mMemorySize = aMemorySize;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUCount = mHWData->mCPUCount;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUCount(ULONG aCPUCount)
1535{
1536 /* check CPU limits */
1537 if ( aCPUCount < SchemaDefs::MinCPUCount
1538 || aCPUCount > SchemaDefs::MaxCPUCount
1539 )
1540 return setError(E_INVALIDARG,
1541 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1542 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1543
1544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1547 if (mHWData->mCPUHotPlugEnabled)
1548 {
1549 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1550 {
1551 if (mHWData->mCPUAttached[idx])
1552 return setError(E_INVALIDARG,
1553 tr("There is still a CPU attached to socket %lu."
1554 "Detach the CPU before removing the socket"),
1555 aCPUCount, idx+1);
1556 }
1557 }
1558
1559 HRESULT rc = i_checkStateDependency(MutableStateDep);
1560 if (FAILED(rc)) return rc;
1561
1562 i_setModified(IsModified_MachineData);
1563 mHWData.backup();
1564 mHWData->mCPUCount = aCPUCount;
1565
1566 return S_OK;
1567}
1568
1569HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1570{
1571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1572
1573 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1574
1575 return S_OK;
1576}
1577
1578HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1579{
1580 HRESULT rc = S_OK;
1581
1582 /* check throttle limits */
1583 if ( aCPUExecutionCap < 1
1584 || aCPUExecutionCap > 100
1585 )
1586 return setError(E_INVALIDARG,
1587 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1588 aCPUExecutionCap, 1, 100);
1589
1590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1591
1592 alock.release();
1593 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1594 alock.acquire();
1595 if (FAILED(rc)) return rc;
1596
1597 i_setModified(IsModified_MachineData);
1598 mHWData.backup();
1599 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1600
1601 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1602 if (Global::IsOnline(mData->mMachineState))
1603 i_saveSettings(NULL);
1604
1605 return S_OK;
1606}
1607
1608HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1609{
1610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1611
1612 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1613
1614 return S_OK;
1615}
1616
1617HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1618{
1619 HRESULT rc = S_OK;
1620
1621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1622
1623 rc = i_checkStateDependency(MutableStateDep);
1624 if (FAILED(rc)) return rc;
1625
1626 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1627 {
1628 if (aCPUHotPlugEnabled)
1629 {
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632
1633 /* Add the amount of CPUs currently attached */
1634 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1635 mHWData->mCPUAttached[i] = true;
1636 }
1637 else
1638 {
1639 /*
1640 * We can disable hotplug only if the amount of maximum CPUs is equal
1641 * to the amount of attached CPUs
1642 */
1643 unsigned cCpusAttached = 0;
1644 unsigned iHighestId = 0;
1645
1646 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1647 {
1648 if (mHWData->mCPUAttached[i])
1649 {
1650 cCpusAttached++;
1651 iHighestId = i;
1652 }
1653 }
1654
1655 if ( (cCpusAttached != mHWData->mCPUCount)
1656 || (iHighestId >= mHWData->mCPUCount))
1657 return setError(E_INVALIDARG,
1658 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1659
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 }
1663 }
1664
1665 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1666
1667 return rc;
1668}
1669
1670HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1671{
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1675
1676 return S_OK;
1677}
1678
1679HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1680{
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1684 if (SUCCEEDED(hrc))
1685 {
1686 i_setModified(IsModified_MachineData);
1687 mHWData.backup();
1688 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1689 }
1690 return hrc;
1691}
1692
1693HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1694{
1695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1696 aCPUProfile = mHWData->mCpuProfile;
1697 return S_OK;
1698}
1699
1700HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1701{
1702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1703 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1704 if (SUCCEEDED(hrc))
1705 {
1706 i_setModified(IsModified_MachineData);
1707 mHWData.backup();
1708 /* Empty equals 'host'. */
1709 if (aCPUProfile.isNotEmpty())
1710 mHWData->mCpuProfile = aCPUProfile;
1711 else
1712 mHWData->mCpuProfile = "host";
1713 }
1714 return hrc;
1715}
1716
1717HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1718{
1719#ifdef VBOX_WITH_USB_CARDREADER
1720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1723
1724 return S_OK;
1725#else
1726 NOREF(aEmulatedUSBCardReaderEnabled);
1727 return E_NOTIMPL;
1728#endif
1729}
1730
1731HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1732{
1733#ifdef VBOX_WITH_USB_CARDREADER
1734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1737 if (FAILED(rc)) return rc;
1738
1739 i_setModified(IsModified_MachineData);
1740 mHWData.backup();
1741 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1742
1743 return S_OK;
1744#else
1745 NOREF(aEmulatedUSBCardReaderEnabled);
1746 return E_NOTIMPL;
1747#endif
1748}
1749
1750HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1751{
1752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 *aHPETEnabled = mHWData->mHPETEnabled;
1755
1756 return S_OK;
1757}
1758
1759HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1760{
1761 HRESULT rc = S_OK;
1762
1763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 rc = i_checkStateDependency(MutableStateDep);
1766 if (FAILED(rc)) return rc;
1767
1768 i_setModified(IsModified_MachineData);
1769 mHWData.backup();
1770
1771 mHWData->mHPETEnabled = aHPETEnabled;
1772
1773 return rc;
1774}
1775
1776/** @todo this method should not be public */
1777HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1778{
1779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1780
1781 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1782
1783 return S_OK;
1784}
1785
1786/**
1787 * Set the memory balloon size.
1788 *
1789 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1790 * we have to make sure that we never call IGuest from here.
1791 */
1792HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1793{
1794 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1795#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1796 /* check limits */
1797 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1798 return setError(E_INVALIDARG,
1799 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1800 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1801
1802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 i_setModified(IsModified_MachineData);
1805 mHWData.backup();
1806 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1807
1808 return S_OK;
1809#else
1810 NOREF(aMemoryBalloonSize);
1811 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1812#endif
1813}
1814
1815HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1816{
1817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1818
1819 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1820 return S_OK;
1821}
1822
1823HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1824{
1825#ifdef VBOX_WITH_PAGE_SHARING
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1829 i_setModified(IsModified_MachineData);
1830 mHWData.backup();
1831 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1832 return S_OK;
1833#else
1834 NOREF(aPageFusionEnabled);
1835 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1836#endif
1837}
1838
1839HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1840{
1841 /* mBIOSSettings is constant during life time, no need to lock */
1842 aBIOSSettings = mBIOSSettings;
1843
1844 return S_OK;
1845}
1846
1847HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1848{
1849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 aRecordingSettings = mRecordingSettings;
1852
1853 return S_OK;
1854}
1855
1856HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1857{
1858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1859
1860 aGraphicsAdapter = mGraphicsAdapter;
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1866{
1867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 switch (aProperty)
1870 {
1871 case CPUPropertyType_PAE:
1872 *aValue = mHWData->mPAEEnabled;
1873 break;
1874
1875 case CPUPropertyType_LongMode:
1876 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1877 *aValue = TRUE;
1878 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1879 *aValue = FALSE;
1880#if HC_ARCH_BITS == 64
1881 else
1882 *aValue = TRUE;
1883#else
1884 else
1885 {
1886 *aValue = FALSE;
1887
1888 ComObjPtr<GuestOSType> pGuestOSType;
1889 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1890 pGuestOSType);
1891 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1892 {
1893 if (pGuestOSType->i_is64Bit())
1894 {
1895 ComObjPtr<Host> pHost = mParent->i_host();
1896 alock.release();
1897
1898 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1899 if (FAILED(hrc2))
1900 *aValue = FALSE;
1901 }
1902 }
1903 }
1904#endif
1905 break;
1906
1907 case CPUPropertyType_TripleFaultReset:
1908 *aValue = mHWData->mTripleFaultReset;
1909 break;
1910
1911 case CPUPropertyType_APIC:
1912 *aValue = mHWData->mAPIC;
1913 break;
1914
1915 case CPUPropertyType_X2APIC:
1916 *aValue = mHWData->mX2APIC;
1917 break;
1918
1919 case CPUPropertyType_IBPBOnVMExit:
1920 *aValue = mHWData->mIBPBOnVMExit;
1921 break;
1922
1923 case CPUPropertyType_IBPBOnVMEntry:
1924 *aValue = mHWData->mIBPBOnVMEntry;
1925 break;
1926
1927 case CPUPropertyType_SpecCtrl:
1928 *aValue = mHWData->mSpecCtrl;
1929 break;
1930
1931 case CPUPropertyType_SpecCtrlByHost:
1932 *aValue = mHWData->mSpecCtrlByHost;
1933 break;
1934
1935 case CPUPropertyType_HWVirt:
1936 *aValue = mHWData->mNestedHWVirt;
1937 break;
1938
1939 case CPUPropertyType_L1DFlushOnEMTScheduling:
1940 *aValue = mHWData->mL1DFlushOnSched;
1941 break;
1942
1943 case CPUPropertyType_L1DFlushOnVMEntry:
1944 *aValue = mHWData->mL1DFlushOnVMEntry;
1945 break;
1946
1947 case CPUPropertyType_MDSClearOnEMTScheduling:
1948 *aValue = mHWData->mMDSClearOnSched;
1949 break;
1950
1951 case CPUPropertyType_MDSClearOnVMEntry:
1952 *aValue = mHWData->mMDSClearOnVMEntry;
1953 break;
1954
1955 default:
1956 return E_INVALIDARG;
1957 }
1958 return S_OK;
1959}
1960
1961HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1962{
1963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 HRESULT rc = i_checkStateDependency(MutableStateDep);
1966 if (FAILED(rc)) return rc;
1967
1968 switch (aProperty)
1969 {
1970 case CPUPropertyType_PAE:
1971 i_setModified(IsModified_MachineData);
1972 mHWData.backup();
1973 mHWData->mPAEEnabled = !!aValue;
1974 break;
1975
1976 case CPUPropertyType_LongMode:
1977 i_setModified(IsModified_MachineData);
1978 mHWData.backup();
1979 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1980 break;
1981
1982 case CPUPropertyType_TripleFaultReset:
1983 i_setModified(IsModified_MachineData);
1984 mHWData.backup();
1985 mHWData->mTripleFaultReset = !!aValue;
1986 break;
1987
1988 case CPUPropertyType_APIC:
1989 if (mHWData->mX2APIC)
1990 aValue = TRUE;
1991 i_setModified(IsModified_MachineData);
1992 mHWData.backup();
1993 mHWData->mAPIC = !!aValue;
1994 break;
1995
1996 case CPUPropertyType_X2APIC:
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mX2APIC = !!aValue;
2000 if (aValue)
2001 mHWData->mAPIC = !!aValue;
2002 break;
2003
2004 case CPUPropertyType_IBPBOnVMExit:
2005 i_setModified(IsModified_MachineData);
2006 mHWData.backup();
2007 mHWData->mIBPBOnVMExit = !!aValue;
2008 break;
2009
2010 case CPUPropertyType_IBPBOnVMEntry:
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mIBPBOnVMEntry = !!aValue;
2014 break;
2015
2016 case CPUPropertyType_SpecCtrl:
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mSpecCtrl = !!aValue;
2020 break;
2021
2022 case CPUPropertyType_SpecCtrlByHost:
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mSpecCtrlByHost = !!aValue;
2026 break;
2027
2028 case CPUPropertyType_HWVirt:
2029 i_setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mNestedHWVirt = !!aValue;
2032 break;
2033
2034 case CPUPropertyType_L1DFlushOnEMTScheduling:
2035 i_setModified(IsModified_MachineData);
2036 mHWData.backup();
2037 mHWData->mL1DFlushOnSched = !!aValue;
2038 break;
2039
2040 case CPUPropertyType_L1DFlushOnVMEntry:
2041 i_setModified(IsModified_MachineData);
2042 mHWData.backup();
2043 mHWData->mL1DFlushOnVMEntry = !!aValue;
2044 break;
2045
2046 case CPUPropertyType_MDSClearOnEMTScheduling:
2047 i_setModified(IsModified_MachineData);
2048 mHWData.backup();
2049 mHWData->mMDSClearOnSched = !!aValue;
2050 break;
2051
2052 case CPUPropertyType_MDSClearOnVMEntry:
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mMDSClearOnVMEntry = !!aValue;
2056 break;
2057
2058 default:
2059 return E_INVALIDARG;
2060 }
2061 return S_OK;
2062}
2063
2064HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2065 ULONG *aValEcx, ULONG *aValEdx)
2066{
2067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2068 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2069 {
2070 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2071 it != mHWData->mCpuIdLeafList.end();
2072 ++it)
2073 {
2074 if (aOrdinal == 0)
2075 {
2076 const settings::CpuIdLeaf &rLeaf= *it;
2077 *aIdx = rLeaf.idx;
2078 *aSubIdx = rLeaf.idxSub;
2079 *aValEax = rLeaf.uEax;
2080 *aValEbx = rLeaf.uEbx;
2081 *aValEcx = rLeaf.uEcx;
2082 *aValEdx = rLeaf.uEdx;
2083 return S_OK;
2084 }
2085 aOrdinal--;
2086 }
2087 }
2088 return E_INVALIDARG;
2089}
2090
2091HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2092{
2093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2094
2095 /*
2096 * Search the list.
2097 */
2098 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2099 {
2100 const settings::CpuIdLeaf &rLeaf= *it;
2101 if ( rLeaf.idx == aIdx
2102 && ( aSubIdx == UINT32_MAX
2103 || rLeaf.idxSub == aSubIdx) )
2104 {
2105 *aValEax = rLeaf.uEax;
2106 *aValEbx = rLeaf.uEbx;
2107 *aValEcx = rLeaf.uEcx;
2108 *aValEdx = rLeaf.uEdx;
2109 return S_OK;
2110 }
2111 }
2112
2113 return E_INVALIDARG;
2114}
2115
2116
2117HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2118{
2119 /*
2120 * Validate input before taking locks and checking state.
2121 */
2122 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2123 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2124 if ( aIdx >= UINT32_C(0x20)
2125 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2126 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2127 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2128
2129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2130 HRESULT rc = i_checkStateDependency(MutableStateDep);
2131 if (FAILED(rc)) return rc;
2132
2133 /*
2134 * Impose a maximum number of leaves.
2135 */
2136 if (mHWData->mCpuIdLeafList.size() > 256)
2137 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2138
2139 /*
2140 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2141 */
2142 i_setModified(IsModified_MachineData);
2143 mHWData.backup();
2144
2145 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2146 {
2147 settings::CpuIdLeaf &rLeaf= *it;
2148 if ( rLeaf.idx == aIdx
2149 && ( aSubIdx == UINT32_MAX
2150 || rLeaf.idxSub == aSubIdx) )
2151 it = mHWData->mCpuIdLeafList.erase(it);
2152 else
2153 ++it;
2154 }
2155
2156 settings::CpuIdLeaf NewLeaf;
2157 NewLeaf.idx = aIdx;
2158 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2159 NewLeaf.uEax = aValEax;
2160 NewLeaf.uEbx = aValEbx;
2161 NewLeaf.uEcx = aValEcx;
2162 NewLeaf.uEdx = aValEdx;
2163 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2164 return S_OK;
2165}
2166
2167HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2168{
2169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2170
2171 HRESULT rc = i_checkStateDependency(MutableStateDep);
2172 if (FAILED(rc)) return rc;
2173
2174 /*
2175 * Do the removal.
2176 */
2177 bool fModified = mHWData.isBackedUp();
2178 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2179 {
2180 settings::CpuIdLeaf &rLeaf= *it;
2181 if ( rLeaf.idx == aIdx
2182 && ( aSubIdx == UINT32_MAX
2183 || rLeaf.idxSub == aSubIdx) )
2184 {
2185 if (!fModified)
2186 {
2187 fModified = true;
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190 // Start from the beginning, since mHWData.backup() creates
2191 // a new list, causing iterator mixup. This makes sure that
2192 // the settings are not unnecessarily marked as modified,
2193 // at the price of extra list walking.
2194 it = mHWData->mCpuIdLeafList.begin();
2195 }
2196 else
2197 it = mHWData->mCpuIdLeafList.erase(it);
2198 }
2199 else
2200 ++it;
2201 }
2202
2203 return S_OK;
2204}
2205
2206HRESULT Machine::removeAllCPUIDLeaves()
2207{
2208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 HRESULT rc = i_checkStateDependency(MutableStateDep);
2211 if (FAILED(rc)) return rc;
2212
2213 if (mHWData->mCpuIdLeafList.size() > 0)
2214 {
2215 i_setModified(IsModified_MachineData);
2216 mHWData.backup();
2217
2218 mHWData->mCpuIdLeafList.clear();
2219 }
2220
2221 return S_OK;
2222}
2223HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2224{
2225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2226
2227 switch(aProperty)
2228 {
2229 case HWVirtExPropertyType_Enabled:
2230 *aValue = mHWData->mHWVirtExEnabled;
2231 break;
2232
2233 case HWVirtExPropertyType_VPID:
2234 *aValue = mHWData->mHWVirtExVPIDEnabled;
2235 break;
2236
2237 case HWVirtExPropertyType_NestedPaging:
2238 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2239 break;
2240
2241 case HWVirtExPropertyType_UnrestrictedExecution:
2242 *aValue = mHWData->mHWVirtExUXEnabled;
2243 break;
2244
2245 case HWVirtExPropertyType_LargePages:
2246 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2247#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2248 *aValue = FALSE;
2249#endif
2250 break;
2251
2252 case HWVirtExPropertyType_Force:
2253 *aValue = mHWData->mHWVirtExForceEnabled;
2254 break;
2255
2256 case HWVirtExPropertyType_UseNativeApi:
2257 *aValue = mHWData->mHWVirtExUseNativeApi;
2258 break;
2259
2260 case HWVirtExPropertyType_VirtVmsaveVmload:
2261 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2262 break;
2263
2264 default:
2265 return E_INVALIDARG;
2266 }
2267 return S_OK;
2268}
2269
2270HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2271{
2272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 HRESULT rc = i_checkStateDependency(MutableStateDep);
2275 if (FAILED(rc)) return rc;
2276
2277 switch (aProperty)
2278 {
2279 case HWVirtExPropertyType_Enabled:
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mHWVirtExEnabled = !!aValue;
2283 break;
2284
2285 case HWVirtExPropertyType_VPID:
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2289 break;
2290
2291 case HWVirtExPropertyType_NestedPaging:
2292 i_setModified(IsModified_MachineData);
2293 mHWData.backup();
2294 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2295 break;
2296
2297 case HWVirtExPropertyType_UnrestrictedExecution:
2298 i_setModified(IsModified_MachineData);
2299 mHWData.backup();
2300 mHWData->mHWVirtExUXEnabled = !!aValue;
2301 break;
2302
2303 case HWVirtExPropertyType_LargePages:
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2307 break;
2308
2309 case HWVirtExPropertyType_Force:
2310 i_setModified(IsModified_MachineData);
2311 mHWData.backup();
2312 mHWData->mHWVirtExForceEnabled = !!aValue;
2313 break;
2314
2315 case HWVirtExPropertyType_UseNativeApi:
2316 i_setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mHWVirtExUseNativeApi = !!aValue;
2319 break;
2320
2321 case HWVirtExPropertyType_VirtVmsaveVmload:
2322 i_setModified(IsModified_MachineData);
2323 mHWData.backup();
2324 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2325 break;
2326
2327 default:
2328 return E_INVALIDARG;
2329 }
2330
2331 return S_OK;
2332}
2333
2334HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2335{
2336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2337
2338 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2339
2340 return S_OK;
2341}
2342
2343HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2344{
2345 /** @todo (r=dmik):
2346 * 1. Allow to change the name of the snapshot folder containing snapshots
2347 * 2. Rename the folder on disk instead of just changing the property
2348 * value (to be smart and not to leave garbage). Note that it cannot be
2349 * done here because the change may be rolled back. Thus, the right
2350 * place is #saveSettings().
2351 */
2352
2353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 HRESULT rc = i_checkStateDependency(MutableStateDep);
2356 if (FAILED(rc)) return rc;
2357
2358 if (!mData->mCurrentSnapshot.isNull())
2359 return setError(E_FAIL,
2360 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2361
2362 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2363
2364 if (strSnapshotFolder.isEmpty())
2365 strSnapshotFolder = "Snapshots";
2366 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2367 if (RT_FAILURE(vrc))
2368 return setErrorBoth(E_FAIL, vrc,
2369 tr("Invalid snapshot folder '%s' (%Rrc)"),
2370 strSnapshotFolder.c_str(), vrc);
2371
2372 i_setModified(IsModified_MachineData);
2373 mUserData.backup();
2374
2375 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2376
2377 return S_OK;
2378}
2379
2380HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2381{
2382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 aMediumAttachments.resize(mMediumAttachments->size());
2385 size_t i = 0;
2386 for (MediumAttachmentList::const_iterator
2387 it = mMediumAttachments->begin();
2388 it != mMediumAttachments->end();
2389 ++it, ++i)
2390 aMediumAttachments[i] = *it;
2391
2392 return S_OK;
2393}
2394
2395HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2396{
2397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2398
2399 Assert(!!mVRDEServer);
2400
2401 aVRDEServer = mVRDEServer;
2402
2403 return S_OK;
2404}
2405
2406HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2407{
2408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2409
2410 aAudioAdapter = mAudioAdapter;
2411
2412 return S_OK;
2413}
2414
2415HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2416{
2417#ifdef VBOX_WITH_VUSB
2418 clearError();
2419 MultiResult rc(S_OK);
2420
2421# ifdef VBOX_WITH_USB
2422 rc = mParent->i_host()->i_checkUSBProxyService();
2423 if (FAILED(rc)) return rc;
2424# endif
2425
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 aUSBControllers.resize(mUSBControllers->size());
2429 size_t i = 0;
2430 for (USBControllerList::const_iterator
2431 it = mUSBControllers->begin();
2432 it != mUSBControllers->end();
2433 ++it, ++i)
2434 aUSBControllers[i] = *it;
2435
2436 return S_OK;
2437#else
2438 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2439 * extended error info to indicate that USB is simply not available
2440 * (w/o treating it as a failure), for example, as in OSE */
2441 NOREF(aUSBControllers);
2442 ReturnComNotImplemented();
2443#endif /* VBOX_WITH_VUSB */
2444}
2445
2446HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2447{
2448#ifdef VBOX_WITH_VUSB
2449 clearError();
2450 MultiResult rc(S_OK);
2451
2452# ifdef VBOX_WITH_USB
2453 rc = mParent->i_host()->i_checkUSBProxyService();
2454 if (FAILED(rc)) return rc;
2455# endif
2456
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 aUSBDeviceFilters = mUSBDeviceFilters;
2460 return rc;
2461#else
2462 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2463 * extended error info to indicate that USB is simply not available
2464 * (w/o treating it as a failure), for example, as in OSE */
2465 NOREF(aUSBDeviceFilters);
2466 ReturnComNotImplemented();
2467#endif /* VBOX_WITH_VUSB */
2468}
2469
2470HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2471{
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 aSettingsFilePath = mData->m_strConfigFileFull;
2475
2476 return S_OK;
2477}
2478
2479HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2480{
2481 RT_NOREF(aSettingsFilePath);
2482 ReturnComNotImplemented();
2483}
2484
2485HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2486{
2487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2490 if (FAILED(rc)) return rc;
2491
2492 if (!mData->pMachineConfigFile->fileExists())
2493 // this is a new machine, and no config file exists yet:
2494 *aSettingsModified = TRUE;
2495 else
2496 *aSettingsModified = (mData->flModifications != 0);
2497
2498 return S_OK;
2499}
2500
2501HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2502{
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 *aSessionState = mData->mSession.mState;
2506
2507 return S_OK;
2508}
2509
2510HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2511{
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 aSessionName = mData->mSession.mName;
2515
2516 return S_OK;
2517}
2518
2519HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2520{
2521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2522
2523 *aSessionPID = mData->mSession.mPID;
2524
2525 return S_OK;
2526}
2527
2528HRESULT Machine::getState(MachineState_T *aState)
2529{
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 *aState = mData->mMachineState;
2533 Assert(mData->mMachineState != MachineState_Null);
2534
2535 return S_OK;
2536}
2537
2538HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2539{
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2543
2544 return S_OK;
2545}
2546
2547HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2548{
2549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2550
2551 aStateFilePath = mSSData->strStateFilePath;
2552
2553 return S_OK;
2554}
2555
2556HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2557{
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 i_getLogFolder(aLogFolder);
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 aCurrentSnapshot = mData->mCurrentSnapshot;
2570
2571 return S_OK;
2572}
2573
2574HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2579 ? 0
2580 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2586{
2587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589 /* Note: for machines with no snapshots, we always return FALSE
2590 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2591 * reasons :) */
2592
2593 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2594 ? FALSE
2595 : mData->mCurrentStateModified;
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 aSharedFolders.resize(mHWData->mSharedFolders.size());
2605 size_t i = 0;
2606 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2607 it = mHWData->mSharedFolders.begin();
2608 it != mHWData->mSharedFolders.end();
2609 ++it, ++i)
2610 aSharedFolders[i] = *it;
2611
2612 return S_OK;
2613}
2614
2615HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2616{
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 *aClipboardMode = mHWData->mClipboardMode;
2620
2621 return S_OK;
2622}
2623
2624HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2625{
2626 HRESULT rc = S_OK;
2627
2628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2629
2630 alock.release();
2631 rc = i_onClipboardModeChange(aClipboardMode);
2632 alock.acquire();
2633 if (FAILED(rc)) return rc;
2634
2635 i_setModified(IsModified_MachineData);
2636 mHWData.backup();
2637 mHWData->mClipboardMode = aClipboardMode;
2638
2639 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2640 if (Global::IsOnline(mData->mMachineState))
2641 i_saveSettings(NULL);
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2656{
2657 HRESULT rc = S_OK;
2658
2659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 alock.release();
2662 rc = i_onClipboardFileTransferModeChange(aEnabled);
2663 alock.acquire();
2664 if (FAILED(rc)) return rc;
2665
2666 i_setModified(IsModified_MachineData);
2667 mHWData.backup();
2668 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2669
2670 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2671 if (Global::IsOnline(mData->mMachineState))
2672 i_saveSettings(NULL);
2673
2674 return S_OK;
2675}
2676
2677HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2678{
2679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 *aDnDMode = mHWData->mDnDMode;
2682
2683 return S_OK;
2684}
2685
2686HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2687{
2688 HRESULT rc = S_OK;
2689
2690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 alock.release();
2693 rc = i_onDnDModeChange(aDnDMode);
2694
2695 alock.acquire();
2696 if (FAILED(rc)) return rc;
2697
2698 i_setModified(IsModified_MachineData);
2699 mHWData.backup();
2700 mHWData->mDnDMode = aDnDMode;
2701
2702 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2703 if (Global::IsOnline(mData->mMachineState))
2704 i_saveSettings(NULL);
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 aStorageControllers.resize(mStorageControllers->size());
2714 size_t i = 0;
2715 for (StorageControllerList::const_iterator
2716 it = mStorageControllers->begin();
2717 it != mStorageControllers->end();
2718 ++it, ++i)
2719 aStorageControllers[i] = *it;
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2725{
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *aEnabled = mUserData->s.fTeleporterEnabled;
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2734{
2735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 /* Only allow it to be set to true when PoweredOff or Aborted.
2738 (Clearing it is always permitted.) */
2739 if ( aTeleporterEnabled
2740 && mData->mRegistered
2741 && ( !i_isSessionMachine()
2742 || ( mData->mMachineState != MachineState_PoweredOff
2743 && mData->mMachineState != MachineState_Teleported
2744 && mData->mMachineState != MachineState_Aborted
2745 )
2746 )
2747 )
2748 return setError(VBOX_E_INVALID_VM_STATE,
2749 tr("The machine is not powered off (state is %s)"),
2750 Global::stringifyMachineState(mData->mMachineState));
2751
2752 i_setModified(IsModified_MachineData);
2753 mUserData.backup();
2754 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2769{
2770 if (aTeleporterPort >= _64K)
2771 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2772
2773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2776 if (FAILED(rc)) return rc;
2777
2778 i_setModified(IsModified_MachineData);
2779 mUserData.backup();
2780 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2786{
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2790
2791 return S_OK;
2792}
2793
2794HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2795{
2796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2799 if (FAILED(rc)) return rc;
2800
2801 i_setModified(IsModified_MachineData);
2802 mUserData.backup();
2803 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2817{
2818 /*
2819 * Hash the password first.
2820 */
2821 com::Utf8Str aT = aTeleporterPassword;
2822
2823 if (!aT.isEmpty())
2824 {
2825 if (VBoxIsPasswordHashed(&aT))
2826 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2827 VBoxHashPassword(&aT);
2828 }
2829
2830 /*
2831 * Do the update.
2832 */
2833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2834 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2835 if (SUCCEEDED(hrc))
2836 {
2837 i_setModified(IsModified_MachineData);
2838 mUserData.backup();
2839 mUserData->s.strTeleporterPassword = aT;
2840 }
2841
2842 return hrc;
2843}
2844
2845HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 /* Only allow it to be set to true when PoweredOff or Aborted.
2859 (Clearing it is always permitted.) */
2860 if ( aRTCUseUTC
2861 && mData->mRegistered
2862 && ( !i_isSessionMachine()
2863 || ( mData->mMachineState != MachineState_PoweredOff
2864 && mData->mMachineState != MachineState_Teleported
2865 && mData->mMachineState != MachineState_Aborted
2866 )
2867 )
2868 )
2869 return setError(VBOX_E_INVALID_VM_STATE,
2870 tr("The machine is not powered off (state is %s)"),
2871 Global::stringifyMachineState(mData->mMachineState));
2872
2873 i_setModified(IsModified_MachineData);
2874 mUserData.backup();
2875 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2876
2877 return S_OK;
2878}
2879
2880HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2881{
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2885
2886 return S_OK;
2887}
2888
2889HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2890{
2891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 HRESULT rc = i_checkStateDependency(MutableStateDep);
2894 if (FAILED(rc)) return rc;
2895
2896 i_setModified(IsModified_MachineData);
2897 mHWData.backup();
2898 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2904{
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 *aIOCacheSize = mHWData->mIOCacheSize;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2913{
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 HRESULT rc = i_checkStateDependency(MutableStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 i_setModified(IsModified_MachineData);
2920 mHWData.backup();
2921 mHWData->mIOCacheSize = aIOCacheSize;
2922
2923 return S_OK;
2924}
2925
2926
2927/**
2928 * @note Locks objects!
2929 */
2930HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2931 LockType_T aLockType)
2932{
2933 /* check the session state */
2934 SessionState_T state;
2935 HRESULT rc = aSession->COMGETTER(State)(&state);
2936 if (FAILED(rc)) return rc;
2937
2938 if (state != SessionState_Unlocked)
2939 return setError(VBOX_E_INVALID_OBJECT_STATE,
2940 tr("The given session is busy"));
2941
2942 // get the client's IInternalSessionControl interface
2943 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2944 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2945 E_INVALIDARG);
2946
2947 // session name (only used in some code paths)
2948 Utf8Str strSessionName;
2949
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 if (!mData->mRegistered)
2953 return setError(E_UNEXPECTED,
2954 tr("The machine '%s' is not registered"),
2955 mUserData->s.strName.c_str());
2956
2957 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2958
2959 SessionState_T oldState = mData->mSession.mState;
2960 /* Hack: in case the session is closing and there is a progress object
2961 * which allows waiting for the session to be closed, take the opportunity
2962 * and do a limited wait (max. 1 second). This helps a lot when the system
2963 * is busy and thus session closing can take a little while. */
2964 if ( mData->mSession.mState == SessionState_Unlocking
2965 && mData->mSession.mProgress)
2966 {
2967 alock.release();
2968 mData->mSession.mProgress->WaitForCompletion(1000);
2969 alock.acquire();
2970 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2971 }
2972
2973 // try again now
2974 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2975 // (i.e. session machine exists)
2976 && (aLockType == LockType_Shared) // caller wants a shared link to the
2977 // existing session that holds the write lock:
2978 )
2979 {
2980 // OK, share the session... we are now dealing with three processes:
2981 // 1) VBoxSVC (where this code runs);
2982 // 2) process C: the caller's client process (who wants a shared session);
2983 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2984
2985 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2986 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2987 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2988 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2989 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2990
2991 /*
2992 * Release the lock before calling the client process. It's safe here
2993 * since the only thing to do after we get the lock again is to add
2994 * the remote control to the list (which doesn't directly influence
2995 * anything).
2996 */
2997 alock.release();
2998
2999 // get the console of the session holding the write lock (this is a remote call)
3000 ComPtr<IConsole> pConsoleW;
3001 if (mData->mSession.mLockType == LockType_VM)
3002 {
3003 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3004 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3005 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3006 if (FAILED(rc))
3007 // the failure may occur w/o any error info (from RPC), so provide one
3008 return setError(VBOX_E_VM_ERROR,
3009 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3010 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3011 }
3012
3013 // share the session machine and W's console with the caller's session
3014 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3015 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3016 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3017
3018 if (FAILED(rc))
3019 // the failure may occur w/o any error info (from RPC), so provide one
3020 return setError(VBOX_E_VM_ERROR,
3021 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3022 alock.acquire();
3023
3024 // need to revalidate the state after acquiring the lock again
3025 if (mData->mSession.mState != SessionState_Locked)
3026 {
3027 pSessionControl->Uninitialize();
3028 return setError(VBOX_E_INVALID_SESSION_STATE,
3029 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3030 mUserData->s.strName.c_str());
3031 }
3032
3033 // add the caller's session to the list
3034 mData->mSession.mRemoteControls.push_back(pSessionControl);
3035 }
3036 else if ( mData->mSession.mState == SessionState_Locked
3037 || mData->mSession.mState == SessionState_Unlocking
3038 )
3039 {
3040 // sharing not permitted, or machine still unlocking:
3041 return setError(VBOX_E_INVALID_OBJECT_STATE,
3042 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3043 mUserData->s.strName.c_str());
3044 }
3045 else
3046 {
3047 // machine is not locked: then write-lock the machine (create the session machine)
3048
3049 // must not be busy
3050 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3051
3052 // get the caller's session PID
3053 RTPROCESS pid = NIL_RTPROCESS;
3054 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3055 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3056 Assert(pid != NIL_RTPROCESS);
3057
3058 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3059
3060 if (fLaunchingVMProcess)
3061 {
3062 if (mData->mSession.mPID == NIL_RTPROCESS)
3063 {
3064 // two or more clients racing for a lock, the one which set the
3065 // session state to Spawning will win, the others will get an
3066 // error as we can't decide here if waiting a little would help
3067 // (only for shared locks this would avoid an error)
3068 return setError(VBOX_E_INVALID_OBJECT_STATE,
3069 tr("The machine '%s' already has a lock request pending"),
3070 mUserData->s.strName.c_str());
3071 }
3072
3073 // this machine is awaiting for a spawning session to be opened:
3074 // then the calling process must be the one that got started by
3075 // LaunchVMProcess()
3076
3077 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3078 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3079
3080#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3081 /* Hardened windows builds spawns three processes when a VM is
3082 launched, the 3rd one is the one that will end up here. */
3083 RTPROCESS pidParent;
3084 int vrc = RTProcQueryParent(pid, &pidParent);
3085 if (RT_SUCCESS(vrc))
3086 vrc = RTProcQueryParent(pidParent, &pidParent);
3087 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3088 || vrc == VERR_ACCESS_DENIED)
3089 {
3090 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3091 mData->mSession.mPID = pid;
3092 }
3093#endif
3094
3095 if (mData->mSession.mPID != pid)
3096 return setError(E_ACCESSDENIED,
3097 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3098 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3099 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3100 }
3101
3102 // create the mutable SessionMachine from the current machine
3103 ComObjPtr<SessionMachine> sessionMachine;
3104 sessionMachine.createObject();
3105 rc = sessionMachine->init(this);
3106 AssertComRC(rc);
3107
3108 /* NOTE: doing return from this function after this point but
3109 * before the end is forbidden since it may call SessionMachine::uninit()
3110 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3111 * lock while still holding the Machine lock in alock so that a deadlock
3112 * is possible due to the wrong lock order. */
3113
3114 if (SUCCEEDED(rc))
3115 {
3116 /*
3117 * Set the session state to Spawning to protect against subsequent
3118 * attempts to open a session and to unregister the machine after
3119 * we release the lock.
3120 */
3121 SessionState_T origState = mData->mSession.mState;
3122 mData->mSession.mState = SessionState_Spawning;
3123
3124#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3125 /* Get the client token ID to be passed to the client process */
3126 Utf8Str strTokenId;
3127 sessionMachine->i_getTokenId(strTokenId);
3128 Assert(!strTokenId.isEmpty());
3129#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3130 /* Get the client token to be passed to the client process */
3131 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3132 /* The token is now "owned" by pToken, fix refcount */
3133 if (!pToken.isNull())
3134 pToken->Release();
3135#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3136
3137 /*
3138 * Release the lock before calling the client process -- it will call
3139 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3140 * because the state is Spawning, so that LaunchVMProcess() and
3141 * LockMachine() calls will fail. This method, called before we
3142 * acquire the lock again, will fail because of the wrong PID.
3143 *
3144 * Note that mData->mSession.mRemoteControls accessed outside
3145 * the lock may not be modified when state is Spawning, so it's safe.
3146 */
3147 alock.release();
3148
3149 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3150#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3151 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3152#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3153 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3154 /* Now the token is owned by the client process. */
3155 pToken.setNull();
3156#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3157 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3158
3159 /* The failure may occur w/o any error info (from RPC), so provide one */
3160 if (FAILED(rc))
3161 setError(VBOX_E_VM_ERROR,
3162 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3163
3164 // get session name, either to remember or to compare against
3165 // the already known session name.
3166 {
3167 Bstr bstrSessionName;
3168 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3169 if (SUCCEEDED(rc2))
3170 strSessionName = bstrSessionName;
3171 }
3172
3173 if ( SUCCEEDED(rc)
3174 && fLaunchingVMProcess
3175 )
3176 {
3177 /* complete the remote session initialization */
3178
3179 /* get the console from the direct session */
3180 ComPtr<IConsole> console;
3181 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3182 ComAssertComRC(rc);
3183
3184 if (SUCCEEDED(rc) && !console)
3185 {
3186 ComAssert(!!console);
3187 rc = E_FAIL;
3188 }
3189
3190 /* assign machine & console to the remote session */
3191 if (SUCCEEDED(rc))
3192 {
3193 /*
3194 * after LaunchVMProcess(), the first and the only
3195 * entry in remoteControls is that remote session
3196 */
3197 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3198 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3199 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3200
3201 /* The failure may occur w/o any error info (from RPC), so provide one */
3202 if (FAILED(rc))
3203 setError(VBOX_E_VM_ERROR,
3204 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3205 }
3206
3207 if (FAILED(rc))
3208 pSessionControl->Uninitialize();
3209 }
3210
3211 /* acquire the lock again */
3212 alock.acquire();
3213
3214 /* Restore the session state */
3215 mData->mSession.mState = origState;
3216 }
3217
3218 // finalize spawning anyway (this is why we don't return on errors above)
3219 if (fLaunchingVMProcess)
3220 {
3221 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3222 /* Note that the progress object is finalized later */
3223 /** @todo Consider checking mData->mSession.mProgress for cancellation
3224 * around here. */
3225
3226 /* We don't reset mSession.mPID here because it is necessary for
3227 * SessionMachine::uninit() to reap the child process later. */
3228
3229 if (FAILED(rc))
3230 {
3231 /* Close the remote session, remove the remote control from the list
3232 * and reset session state to Closed (@note keep the code in sync
3233 * with the relevant part in checkForSpawnFailure()). */
3234
3235 Assert(mData->mSession.mRemoteControls.size() == 1);
3236 if (mData->mSession.mRemoteControls.size() == 1)
3237 {
3238 ErrorInfoKeeper eik;
3239 mData->mSession.mRemoteControls.front()->Uninitialize();
3240 }
3241
3242 mData->mSession.mRemoteControls.clear();
3243 mData->mSession.mState = SessionState_Unlocked;
3244 }
3245 }
3246 else
3247 {
3248 /* memorize PID of the directly opened session */
3249 if (SUCCEEDED(rc))
3250 mData->mSession.mPID = pid;
3251 }
3252
3253 if (SUCCEEDED(rc))
3254 {
3255 mData->mSession.mLockType = aLockType;
3256 /* memorize the direct session control and cache IUnknown for it */
3257 mData->mSession.mDirectControl = pSessionControl;
3258 mData->mSession.mState = SessionState_Locked;
3259 if (!fLaunchingVMProcess)
3260 mData->mSession.mName = strSessionName;
3261 /* associate the SessionMachine with this Machine */
3262 mData->mSession.mMachine = sessionMachine;
3263
3264 /* request an IUnknown pointer early from the remote party for later
3265 * identity checks (it will be internally cached within mDirectControl
3266 * at least on XPCOM) */
3267 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3268 NOREF(unk);
3269 }
3270
3271 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3272 * would break the lock order */
3273 alock.release();
3274
3275 /* uninitialize the created session machine on failure */
3276 if (FAILED(rc))
3277 sessionMachine->uninit();
3278 }
3279
3280 if (SUCCEEDED(rc))
3281 {
3282 /*
3283 * tell the client watcher thread to update the set of
3284 * machines that have open sessions
3285 */
3286 mParent->i_updateClientWatcher();
3287
3288 if (oldState != SessionState_Locked)
3289 /* fire an event */
3290 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3291 }
3292
3293 return rc;
3294}
3295
3296/**
3297 * @note Locks objects!
3298 */
3299HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3300 const com::Utf8Str &aName,
3301 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3302 ComPtr<IProgress> &aProgress)
3303{
3304 Utf8Str strFrontend(aName);
3305 /* "emergencystop" doesn't need the session, so skip the checks/interface
3306 * retrieval. This code doesn't quite fit in here, but introducing a
3307 * special API method would be even more effort, and would require explicit
3308 * support by every API client. It's better to hide the feature a bit. */
3309 if (strFrontend != "emergencystop")
3310 CheckComArgNotNull(aSession);
3311
3312 HRESULT rc = S_OK;
3313 if (strFrontend.isEmpty())
3314 {
3315 Bstr bstrFrontend;
3316 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3317 if (FAILED(rc))
3318 return rc;
3319 strFrontend = bstrFrontend;
3320 if (strFrontend.isEmpty())
3321 {
3322 ComPtr<ISystemProperties> systemProperties;
3323 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3324 if (FAILED(rc))
3325 return rc;
3326 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3327 if (FAILED(rc))
3328 return rc;
3329 strFrontend = bstrFrontend;
3330 }
3331 /* paranoia - emergencystop is not a valid default */
3332 if (strFrontend == "emergencystop")
3333 strFrontend = Utf8Str::Empty;
3334 }
3335 /* default frontend: Qt GUI */
3336 if (strFrontend.isEmpty())
3337 strFrontend = "GUI/Qt";
3338
3339 if (strFrontend != "emergencystop")
3340 {
3341 /* check the session state */
3342 SessionState_T state;
3343 rc = aSession->COMGETTER(State)(&state);
3344 if (FAILED(rc))
3345 return rc;
3346
3347 if (state != SessionState_Unlocked)
3348 return setError(VBOX_E_INVALID_OBJECT_STATE,
3349 tr("The given session is busy"));
3350
3351 /* get the IInternalSessionControl interface */
3352 ComPtr<IInternalSessionControl> control(aSession);
3353 ComAssertMsgRet(!control.isNull(),
3354 ("No IInternalSessionControl interface"),
3355 E_INVALIDARG);
3356
3357 /* get the teleporter enable state for the progress object init. */
3358 BOOL fTeleporterEnabled;
3359 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3360 if (FAILED(rc))
3361 return rc;
3362
3363 /* create a progress object */
3364 ComObjPtr<ProgressProxy> progress;
3365 progress.createObject();
3366 rc = progress->init(mParent,
3367 static_cast<IMachine*>(this),
3368 Bstr(tr("Starting VM")).raw(),
3369 TRUE /* aCancelable */,
3370 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3371 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3372 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3373 2 /* uFirstOperationWeight */,
3374 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3375
3376 if (SUCCEEDED(rc))
3377 {
3378 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3379 if (SUCCEEDED(rc))
3380 {
3381 aProgress = progress;
3382
3383 /* signal the client watcher thread */
3384 mParent->i_updateClientWatcher();
3385
3386 /* fire an event */
3387 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3388 }
3389 }
3390 }
3391 else
3392 {
3393 /* no progress object - either instant success or failure */
3394 aProgress = NULL;
3395
3396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3397
3398 if (mData->mSession.mState != SessionState_Locked)
3399 return setError(VBOX_E_INVALID_OBJECT_STATE,
3400 tr("The machine '%s' is not locked by a session"),
3401 mUserData->s.strName.c_str());
3402
3403 /* must have a VM process associated - do not kill normal API clients
3404 * with an open session */
3405 if (!Global::IsOnline(mData->mMachineState))
3406 return setError(VBOX_E_INVALID_OBJECT_STATE,
3407 tr("The machine '%s' does not have a VM process"),
3408 mUserData->s.strName.c_str());
3409
3410 /* forcibly terminate the VM process */
3411 if (mData->mSession.mPID != NIL_RTPROCESS)
3412 RTProcTerminate(mData->mSession.mPID);
3413
3414 /* signal the client watcher thread, as most likely the client has
3415 * been terminated */
3416 mParent->i_updateClientWatcher();
3417 }
3418
3419 return rc;
3420}
3421
3422HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3423{
3424 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3425 return setError(E_INVALIDARG,
3426 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3427 aPosition, SchemaDefs::MaxBootPosition);
3428
3429 if (aDevice == DeviceType_USB)
3430 return setError(E_NOTIMPL,
3431 tr("Booting from USB device is currently not supported"));
3432
3433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3434
3435 HRESULT rc = i_checkStateDependency(MutableStateDep);
3436 if (FAILED(rc)) return rc;
3437
3438 i_setModified(IsModified_MachineData);
3439 mHWData.backup();
3440 mHWData->mBootOrder[aPosition - 1] = aDevice;
3441
3442 return S_OK;
3443}
3444
3445HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3446{
3447 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3448 return setError(E_INVALIDARG,
3449 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3450 aPosition, SchemaDefs::MaxBootPosition);
3451
3452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3453
3454 *aDevice = mHWData->mBootOrder[aPosition - 1];
3455
3456 return S_OK;
3457}
3458
3459HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3460 LONG aControllerPort,
3461 LONG aDevice,
3462 DeviceType_T aType,
3463 const ComPtr<IMedium> &aMedium)
3464{
3465 IMedium *aM = aMedium;
3466 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3467 aName.c_str(), aControllerPort, aDevice, aType, aM));
3468
3469 // request the host lock first, since might be calling Host methods for getting host drives;
3470 // next, protect the media tree all the while we're in here, as well as our member variables
3471 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3472 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3473
3474 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3475 if (FAILED(rc)) return rc;
3476
3477 /// @todo NEWMEDIA implicit machine registration
3478 if (!mData->mRegistered)
3479 return setError(VBOX_E_INVALID_OBJECT_STATE,
3480 tr("Cannot attach storage devices to an unregistered machine"));
3481
3482 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3483
3484 /* Check for an existing controller. */
3485 ComObjPtr<StorageController> ctl;
3486 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3487 if (FAILED(rc)) return rc;
3488
3489 StorageControllerType_T ctrlType;
3490 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3491 if (FAILED(rc))
3492 return setError(E_FAIL,
3493 tr("Could not get type of controller '%s'"),
3494 aName.c_str());
3495
3496 bool fSilent = false;
3497 Utf8Str strReconfig;
3498
3499 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3500 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3501 if ( mData->mMachineState == MachineState_Paused
3502 && strReconfig == "1")
3503 fSilent = true;
3504
3505 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3506 bool fHotplug = false;
3507 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3508 fHotplug = true;
3509
3510 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3511 return setError(VBOX_E_INVALID_VM_STATE,
3512 tr("Controller '%s' does not support hotplugging"),
3513 aName.c_str());
3514
3515 // check that the port and device are not out of range
3516 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3517 if (FAILED(rc)) return rc;
3518
3519 /* check if the device slot is already busy */
3520 MediumAttachment *pAttachTemp;
3521 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3522 aName,
3523 aControllerPort,
3524 aDevice)))
3525 {
3526 Medium *pMedium = pAttachTemp->i_getMedium();
3527 if (pMedium)
3528 {
3529 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3530 return setError(VBOX_E_OBJECT_IN_USE,
3531 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3532 pMedium->i_getLocationFull().c_str(),
3533 aControllerPort,
3534 aDevice,
3535 aName.c_str());
3536 }
3537 else
3538 return setError(VBOX_E_OBJECT_IN_USE,
3539 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3540 aControllerPort, aDevice, aName.c_str());
3541 }
3542
3543 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3544 if (aMedium && medium.isNull())
3545 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3546
3547 AutoCaller mediumCaller(medium);
3548 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3549
3550 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3551
3552 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3553 && !medium.isNull()
3554 && ( medium->i_getType() != MediumType_Readonly
3555 || medium->i_getDeviceType() != DeviceType_DVD)
3556 )
3557 return setError(VBOX_E_OBJECT_IN_USE,
3558 tr("Medium '%s' is already attached to this virtual machine"),
3559 medium->i_getLocationFull().c_str());
3560
3561 if (!medium.isNull())
3562 {
3563 MediumType_T mtype = medium->i_getType();
3564 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3565 // For DVDs it's not written to the config file, so needs no global config
3566 // version bump. For floppies it's a new attribute "type", which is ignored
3567 // by older VirtualBox version, so needs no global config version bump either.
3568 // For hard disks this type is not accepted.
3569 if (mtype == MediumType_MultiAttach)
3570 {
3571 // This type is new with VirtualBox 4.0 and therefore requires settings
3572 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3573 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3574 // two reasons: The medium type is a property of the media registry tree, which
3575 // can reside in the global config file (for pre-4.0 media); we would therefore
3576 // possibly need to bump the global config version. We don't want to do that though
3577 // because that might make downgrading to pre-4.0 impossible.
3578 // As a result, we can only use these two new types if the medium is NOT in the
3579 // global registry:
3580 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3581 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3582 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3583 )
3584 return setError(VBOX_E_INVALID_OBJECT_STATE,
3585 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3586 "to machines that were created with VirtualBox 4.0 or later"),
3587 medium->i_getLocationFull().c_str());
3588 }
3589 }
3590
3591 bool fIndirect = false;
3592 if (!medium.isNull())
3593 fIndirect = medium->i_isReadOnly();
3594 bool associate = true;
3595
3596 do
3597 {
3598 if ( aType == DeviceType_HardDisk
3599 && mMediumAttachments.isBackedUp())
3600 {
3601 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3602
3603 /* check if the medium was attached to the VM before we started
3604 * changing attachments in which case the attachment just needs to
3605 * be restored */
3606 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3607 {
3608 AssertReturn(!fIndirect, E_FAIL);
3609
3610 /* see if it's the same bus/channel/device */
3611 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3612 {
3613 /* the simplest case: restore the whole attachment
3614 * and return, nothing else to do */
3615 mMediumAttachments->push_back(pAttachTemp);
3616
3617 /* Reattach the medium to the VM. */
3618 if (fHotplug || fSilent)
3619 {
3620 mediumLock.release();
3621 treeLock.release();
3622 alock.release();
3623
3624 MediumLockList *pMediumLockList(new MediumLockList());
3625
3626 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3627 medium /* pToLockWrite */,
3628 false /* fMediumLockWriteAll */,
3629 NULL,
3630 *pMediumLockList);
3631 alock.acquire();
3632 if (FAILED(rc))
3633 delete pMediumLockList;
3634 else
3635 {
3636 mData->mSession.mLockedMedia.Unlock();
3637 alock.release();
3638 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3639 mData->mSession.mLockedMedia.Lock();
3640 alock.acquire();
3641 }
3642 alock.release();
3643
3644 if (SUCCEEDED(rc))
3645 {
3646 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3647 /* Remove lock list in case of error. */
3648 if (FAILED(rc))
3649 {
3650 mData->mSession.mLockedMedia.Unlock();
3651 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3652 mData->mSession.mLockedMedia.Lock();
3653 }
3654 }
3655 }
3656
3657 return S_OK;
3658 }
3659
3660 /* bus/channel/device differ; we need a new attachment object,
3661 * but don't try to associate it again */
3662 associate = false;
3663 break;
3664 }
3665 }
3666
3667 /* go further only if the attachment is to be indirect */
3668 if (!fIndirect)
3669 break;
3670
3671 /* perform the so called smart attachment logic for indirect
3672 * attachments. Note that smart attachment is only applicable to base
3673 * hard disks. */
3674
3675 if (medium->i_getParent().isNull())
3676 {
3677 /* first, investigate the backup copy of the current hard disk
3678 * attachments to make it possible to re-attach existing diffs to
3679 * another device slot w/o losing their contents */
3680 if (mMediumAttachments.isBackedUp())
3681 {
3682 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3683
3684 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3685 uint32_t foundLevel = 0;
3686
3687 for (MediumAttachmentList::const_iterator
3688 it = oldAtts.begin();
3689 it != oldAtts.end();
3690 ++it)
3691 {
3692 uint32_t level = 0;
3693 MediumAttachment *pAttach = *it;
3694 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3695 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3696 if (pMedium.isNull())
3697 continue;
3698
3699 if (pMedium->i_getBase(&level) == medium)
3700 {
3701 /* skip the hard disk if its currently attached (we
3702 * cannot attach the same hard disk twice) */
3703 if (i_findAttachment(*mMediumAttachments.data(),
3704 pMedium))
3705 continue;
3706
3707 /* matched device, channel and bus (i.e. attached to the
3708 * same place) will win and immediately stop the search;
3709 * otherwise the attachment that has the youngest
3710 * descendant of medium will be used
3711 */
3712 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3713 {
3714 /* the simplest case: restore the whole attachment
3715 * and return, nothing else to do */
3716 mMediumAttachments->push_back(*it);
3717
3718 /* Reattach the medium to the VM. */
3719 if (fHotplug || fSilent)
3720 {
3721 mediumLock.release();
3722 treeLock.release();
3723 alock.release();
3724
3725 MediumLockList *pMediumLockList(new MediumLockList());
3726
3727 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3728 medium /* pToLockWrite */,
3729 false /* fMediumLockWriteAll */,
3730 NULL,
3731 *pMediumLockList);
3732 alock.acquire();
3733 if (FAILED(rc))
3734 delete pMediumLockList;
3735 else
3736 {
3737 mData->mSession.mLockedMedia.Unlock();
3738 alock.release();
3739 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3740 mData->mSession.mLockedMedia.Lock();
3741 alock.acquire();
3742 }
3743 alock.release();
3744
3745 if (SUCCEEDED(rc))
3746 {
3747 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3748 /* Remove lock list in case of error. */
3749 if (FAILED(rc))
3750 {
3751 mData->mSession.mLockedMedia.Unlock();
3752 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3753 mData->mSession.mLockedMedia.Lock();
3754 }
3755 }
3756 }
3757
3758 return S_OK;
3759 }
3760 else if ( foundIt == oldAtts.end()
3761 || level > foundLevel /* prefer younger */
3762 )
3763 {
3764 foundIt = it;
3765 foundLevel = level;
3766 }
3767 }
3768 }
3769
3770 if (foundIt != oldAtts.end())
3771 {
3772 /* use the previously attached hard disk */
3773 medium = (*foundIt)->i_getMedium();
3774 mediumCaller.attach(medium);
3775 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3776 mediumLock.attach(medium);
3777 /* not implicit, doesn't require association with this VM */
3778 fIndirect = false;
3779 associate = false;
3780 /* go right to the MediumAttachment creation */
3781 break;
3782 }
3783 }
3784
3785 /* must give up the medium lock and medium tree lock as below we
3786 * go over snapshots, which needs a lock with higher lock order. */
3787 mediumLock.release();
3788 treeLock.release();
3789
3790 /* then, search through snapshots for the best diff in the given
3791 * hard disk's chain to base the new diff on */
3792
3793 ComObjPtr<Medium> base;
3794 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3795 while (snap)
3796 {
3797 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3798
3799 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3800
3801 MediumAttachment *pAttachFound = NULL;
3802 uint32_t foundLevel = 0;
3803
3804 for (MediumAttachmentList::const_iterator
3805 it = snapAtts.begin();
3806 it != snapAtts.end();
3807 ++it)
3808 {
3809 MediumAttachment *pAttach = *it;
3810 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3811 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3812 if (pMedium.isNull())
3813 continue;
3814
3815 uint32_t level = 0;
3816 if (pMedium->i_getBase(&level) == medium)
3817 {
3818 /* matched device, channel and bus (i.e. attached to the
3819 * same place) will win and immediately stop the search;
3820 * otherwise the attachment that has the youngest
3821 * descendant of medium will be used
3822 */
3823 if ( pAttach->i_getDevice() == aDevice
3824 && pAttach->i_getPort() == aControllerPort
3825 && pAttach->i_getControllerName() == aName
3826 )
3827 {
3828 pAttachFound = pAttach;
3829 break;
3830 }
3831 else if ( !pAttachFound
3832 || level > foundLevel /* prefer younger */
3833 )
3834 {
3835 pAttachFound = pAttach;
3836 foundLevel = level;
3837 }
3838 }
3839 }
3840
3841 if (pAttachFound)
3842 {
3843 base = pAttachFound->i_getMedium();
3844 break;
3845 }
3846
3847 snap = snap->i_getParent();
3848 }
3849
3850 /* re-lock medium tree and the medium, as we need it below */
3851 treeLock.acquire();
3852 mediumLock.acquire();
3853
3854 /* found a suitable diff, use it as a base */
3855 if (!base.isNull())
3856 {
3857 medium = base;
3858 mediumCaller.attach(medium);
3859 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3860 mediumLock.attach(medium);
3861 }
3862 }
3863
3864 Utf8Str strFullSnapshotFolder;
3865 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3866
3867 ComObjPtr<Medium> diff;
3868 diff.createObject();
3869 // store this diff in the same registry as the parent
3870 Guid uuidRegistryParent;
3871 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3872 {
3873 // parent image has no registry: this can happen if we're attaching a new immutable
3874 // image that has not yet been attached (medium then points to the base and we're
3875 // creating the diff image for the immutable, and the parent is not yet registered);
3876 // put the parent in the machine registry then
3877 mediumLock.release();
3878 treeLock.release();
3879 alock.release();
3880 i_addMediumToRegistry(medium);
3881 alock.acquire();
3882 treeLock.acquire();
3883 mediumLock.acquire();
3884 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3885 }
3886 rc = diff->init(mParent,
3887 medium->i_getPreferredDiffFormat(),
3888 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3889 uuidRegistryParent,
3890 DeviceType_HardDisk);
3891 if (FAILED(rc)) return rc;
3892
3893 /* Apply the normal locking logic to the entire chain. */
3894 MediumLockList *pMediumLockList(new MediumLockList());
3895 mediumLock.release();
3896 treeLock.release();
3897 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3898 diff /* pToLockWrite */,
3899 false /* fMediumLockWriteAll */,
3900 medium,
3901 *pMediumLockList);
3902 treeLock.acquire();
3903 mediumLock.acquire();
3904 if (SUCCEEDED(rc))
3905 {
3906 mediumLock.release();
3907 treeLock.release();
3908 rc = pMediumLockList->Lock();
3909 treeLock.acquire();
3910 mediumLock.acquire();
3911 if (FAILED(rc))
3912 setError(rc,
3913 tr("Could not lock medium when creating diff '%s'"),
3914 diff->i_getLocationFull().c_str());
3915 else
3916 {
3917 /* will release the lock before the potentially lengthy
3918 * operation, so protect with the special state */
3919 MachineState_T oldState = mData->mMachineState;
3920 i_setMachineState(MachineState_SettingUp);
3921
3922 mediumLock.release();
3923 treeLock.release();
3924 alock.release();
3925
3926 rc = medium->i_createDiffStorage(diff,
3927 medium->i_getPreferredDiffVariant(),
3928 pMediumLockList,
3929 NULL /* aProgress */,
3930 true /* aWait */,
3931 false /* aNotify */);
3932
3933 alock.acquire();
3934 treeLock.acquire();
3935 mediumLock.acquire();
3936
3937 i_setMachineState(oldState);
3938 }
3939 }
3940
3941 /* Unlock the media and free the associated memory. */
3942 delete pMediumLockList;
3943
3944 if (FAILED(rc)) return rc;
3945
3946 /* use the created diff for the actual attachment */
3947 medium = diff;
3948 mediumCaller.attach(medium);
3949 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3950 mediumLock.attach(medium);
3951 }
3952 while (0);
3953
3954 ComObjPtr<MediumAttachment> attachment;
3955 attachment.createObject();
3956 rc = attachment->init(this,
3957 medium,
3958 aName,
3959 aControllerPort,
3960 aDevice,
3961 aType,
3962 fIndirect,
3963 false /* fPassthrough */,
3964 false /* fTempEject */,
3965 false /* fNonRotational */,
3966 false /* fDiscard */,
3967 fHotplug /* fHotPluggable */,
3968 Utf8Str::Empty);
3969 if (FAILED(rc)) return rc;
3970
3971 if (associate && !medium.isNull())
3972 {
3973 // as the last step, associate the medium to the VM
3974 rc = medium->i_addBackReference(mData->mUuid);
3975 // here we can fail because of Deleting, or being in process of creating a Diff
3976 if (FAILED(rc)) return rc;
3977
3978 mediumLock.release();
3979 treeLock.release();
3980 alock.release();
3981 i_addMediumToRegistry(medium);
3982 alock.acquire();
3983 treeLock.acquire();
3984 mediumLock.acquire();
3985 }
3986
3987 /* success: finally remember the attachment */
3988 i_setModified(IsModified_Storage);
3989 mMediumAttachments.backup();
3990 mMediumAttachments->push_back(attachment);
3991
3992 mediumLock.release();
3993 treeLock.release();
3994 alock.release();
3995
3996 if (fHotplug || fSilent)
3997 {
3998 if (!medium.isNull())
3999 {
4000 MediumLockList *pMediumLockList(new MediumLockList());
4001
4002 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4003 medium /* pToLockWrite */,
4004 false /* fMediumLockWriteAll */,
4005 NULL,
4006 *pMediumLockList);
4007 alock.acquire();
4008 if (FAILED(rc))
4009 delete pMediumLockList;
4010 else
4011 {
4012 mData->mSession.mLockedMedia.Unlock();
4013 alock.release();
4014 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4015 mData->mSession.mLockedMedia.Lock();
4016 alock.acquire();
4017 }
4018 alock.release();
4019 }
4020
4021 if (SUCCEEDED(rc))
4022 {
4023 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4024 /* Remove lock list in case of error. */
4025 if (FAILED(rc))
4026 {
4027 mData->mSession.mLockedMedia.Unlock();
4028 mData->mSession.mLockedMedia.Remove(attachment);
4029 mData->mSession.mLockedMedia.Lock();
4030 }
4031 }
4032 }
4033
4034 /* Save modified registries, but skip this machine as it's the caller's
4035 * job to save its settings like all other settings changes. */
4036 mParent->i_unmarkRegistryModified(i_getId());
4037 mParent->i_saveModifiedRegistries();
4038
4039 if (SUCCEEDED(rc))
4040 {
4041 if (fIndirect && medium != aM)
4042 mParent->i_onMediumConfigChanged(medium);
4043 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4044 }
4045
4046 return rc;
4047}
4048
4049HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4050 LONG aDevice)
4051{
4052 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4053 aName.c_str(), aControllerPort, aDevice));
4054
4055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4056
4057 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4058 if (FAILED(rc)) return rc;
4059
4060 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4061
4062 /* Check for an existing controller. */
4063 ComObjPtr<StorageController> ctl;
4064 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4065 if (FAILED(rc)) return rc;
4066
4067 StorageControllerType_T ctrlType;
4068 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4069 if (FAILED(rc))
4070 return setError(E_FAIL,
4071 tr("Could not get type of controller '%s'"),
4072 aName.c_str());
4073
4074 bool fSilent = false;
4075 Utf8Str strReconfig;
4076
4077 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4078 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4079 if ( mData->mMachineState == MachineState_Paused
4080 && strReconfig == "1")
4081 fSilent = true;
4082
4083 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4084 bool fHotplug = false;
4085 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4086 fHotplug = true;
4087
4088 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4089 return setError(VBOX_E_INVALID_VM_STATE,
4090 tr("Controller '%s' does not support hotplugging"),
4091 aName.c_str());
4092
4093 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4094 aName,
4095 aControllerPort,
4096 aDevice);
4097 if (!pAttach)
4098 return setError(VBOX_E_OBJECT_NOT_FOUND,
4099 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4100 aDevice, aControllerPort, aName.c_str());
4101
4102 if (fHotplug && !pAttach->i_getHotPluggable())
4103 return setError(VBOX_E_NOT_SUPPORTED,
4104 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4105 aDevice, aControllerPort, aName.c_str());
4106
4107 /*
4108 * The VM has to detach the device before we delete any implicit diffs.
4109 * If this fails we can roll back without loosing data.
4110 */
4111 if (fHotplug || fSilent)
4112 {
4113 alock.release();
4114 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4115 alock.acquire();
4116 }
4117 if (FAILED(rc)) return rc;
4118
4119 /* If we are here everything went well and we can delete the implicit now. */
4120 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4121
4122 alock.release();
4123
4124 /* Save modified registries, but skip this machine as it's the caller's
4125 * job to save its settings like all other settings changes. */
4126 mParent->i_unmarkRegistryModified(i_getId());
4127 mParent->i_saveModifiedRegistries();
4128
4129 if (SUCCEEDED(rc))
4130 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4131
4132 return rc;
4133}
4134
4135HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4136 LONG aDevice, BOOL aPassthrough)
4137{
4138 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4139 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4140
4141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4142
4143 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4144 if (FAILED(rc)) return rc;
4145
4146 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4147
4148 /* Check for an existing controller. */
4149 ComObjPtr<StorageController> ctl;
4150 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4151 if (FAILED(rc)) return rc;
4152
4153 StorageControllerType_T ctrlType;
4154 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4155 if (FAILED(rc))
4156 return setError(E_FAIL,
4157 tr("Could not get type of controller '%s'"),
4158 aName.c_str());
4159
4160 bool fSilent = false;
4161 Utf8Str strReconfig;
4162
4163 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4164 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4165 if ( mData->mMachineState == MachineState_Paused
4166 && strReconfig == "1")
4167 fSilent = true;
4168
4169 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4170 bool fHotplug = false;
4171 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4172 fHotplug = true;
4173
4174 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4175 return setError(VBOX_E_INVALID_VM_STATE,
4176 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4177 aName.c_str());
4178
4179 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4180 aName,
4181 aControllerPort,
4182 aDevice);
4183 if (!pAttach)
4184 return setError(VBOX_E_OBJECT_NOT_FOUND,
4185 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4186 aDevice, aControllerPort, aName.c_str());
4187
4188
4189 i_setModified(IsModified_Storage);
4190 mMediumAttachments.backup();
4191
4192 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4193
4194 if (pAttach->i_getType() != DeviceType_DVD)
4195 return setError(E_INVALIDARG,
4196 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4197 aDevice, aControllerPort, aName.c_str());
4198
4199 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4200
4201 pAttach->i_updatePassthrough(!!aPassthrough);
4202
4203 attLock.release();
4204 alock.release();
4205 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4206 if (SUCCEEDED(rc) && fValueChanged)
4207 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4208
4209 return rc;
4210}
4211
4212HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4213 LONG aDevice, BOOL aTemporaryEject)
4214{
4215
4216 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4217 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4218
4219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4220
4221 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4222 if (FAILED(rc)) return rc;
4223
4224 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4225 aName,
4226 aControllerPort,
4227 aDevice);
4228 if (!pAttach)
4229 return setError(VBOX_E_OBJECT_NOT_FOUND,
4230 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4231 aDevice, aControllerPort, aName.c_str());
4232
4233
4234 i_setModified(IsModified_Storage);
4235 mMediumAttachments.backup();
4236
4237 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4238
4239 if (pAttach->i_getType() != DeviceType_DVD)
4240 return setError(E_INVALIDARG,
4241 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4242 aDevice, aControllerPort, aName.c_str());
4243 pAttach->i_updateTempEject(!!aTemporaryEject);
4244
4245 return S_OK;
4246}
4247
4248HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4249 LONG aDevice, BOOL aNonRotational)
4250{
4251
4252 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4253 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4254
4255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4256
4257 HRESULT rc = i_checkStateDependency(MutableStateDep);
4258 if (FAILED(rc)) return rc;
4259
4260 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4261
4262 if (Global::IsOnlineOrTransient(mData->mMachineState))
4263 return setError(VBOX_E_INVALID_VM_STATE,
4264 tr("Invalid machine state: %s"),
4265 Global::stringifyMachineState(mData->mMachineState));
4266
4267 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4268 aName,
4269 aControllerPort,
4270 aDevice);
4271 if (!pAttach)
4272 return setError(VBOX_E_OBJECT_NOT_FOUND,
4273 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4274 aDevice, aControllerPort, aName.c_str());
4275
4276
4277 i_setModified(IsModified_Storage);
4278 mMediumAttachments.backup();
4279
4280 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4281
4282 if (pAttach->i_getType() != DeviceType_HardDisk)
4283 return setError(E_INVALIDARG,
4284 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"),
4285 aDevice, aControllerPort, aName.c_str());
4286 pAttach->i_updateNonRotational(!!aNonRotational);
4287
4288 return S_OK;
4289}
4290
4291HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4292 LONG aDevice, BOOL aDiscard)
4293{
4294
4295 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4296 aName.c_str(), aControllerPort, aDevice, aDiscard));
4297
4298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4299
4300 HRESULT rc = i_checkStateDependency(MutableStateDep);
4301 if (FAILED(rc)) return rc;
4302
4303 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4304
4305 if (Global::IsOnlineOrTransient(mData->mMachineState))
4306 return setError(VBOX_E_INVALID_VM_STATE,
4307 tr("Invalid machine state: %s"),
4308 Global::stringifyMachineState(mData->mMachineState));
4309
4310 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4311 aName,
4312 aControllerPort,
4313 aDevice);
4314 if (!pAttach)
4315 return setError(VBOX_E_OBJECT_NOT_FOUND,
4316 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4317 aDevice, aControllerPort, aName.c_str());
4318
4319
4320 i_setModified(IsModified_Storage);
4321 mMediumAttachments.backup();
4322
4323 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4324
4325 if (pAttach->i_getType() != DeviceType_HardDisk)
4326 return setError(E_INVALIDARG,
4327 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"),
4328 aDevice, aControllerPort, aName.c_str());
4329 pAttach->i_updateDiscard(!!aDiscard);
4330
4331 return S_OK;
4332}
4333
4334HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4335 LONG aDevice, BOOL aHotPluggable)
4336{
4337 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4338 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4339
4340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4341
4342 HRESULT rc = i_checkStateDependency(MutableStateDep);
4343 if (FAILED(rc)) return rc;
4344
4345 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4346
4347 if (Global::IsOnlineOrTransient(mData->mMachineState))
4348 return setError(VBOX_E_INVALID_VM_STATE,
4349 tr("Invalid machine state: %s"),
4350 Global::stringifyMachineState(mData->mMachineState));
4351
4352 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4353 aName,
4354 aControllerPort,
4355 aDevice);
4356 if (!pAttach)
4357 return setError(VBOX_E_OBJECT_NOT_FOUND,
4358 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4359 aDevice, aControllerPort, aName.c_str());
4360
4361 /* Check for an existing controller. */
4362 ComObjPtr<StorageController> ctl;
4363 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4364 if (FAILED(rc)) return rc;
4365
4366 StorageControllerType_T ctrlType;
4367 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4368 if (FAILED(rc))
4369 return setError(E_FAIL,
4370 tr("Could not get type of controller '%s'"),
4371 aName.c_str());
4372
4373 if (!i_isControllerHotplugCapable(ctrlType))
4374 return setError(VBOX_E_NOT_SUPPORTED,
4375 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4376 aName.c_str());
4377
4378 i_setModified(IsModified_Storage);
4379 mMediumAttachments.backup();
4380
4381 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4382
4383 if (pAttach->i_getType() == DeviceType_Floppy)
4384 return setError(E_INVALIDARG,
4385 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"),
4386 aDevice, aControllerPort, aName.c_str());
4387 pAttach->i_updateHotPluggable(!!aHotPluggable);
4388
4389 return S_OK;
4390}
4391
4392HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4393 LONG aDevice)
4394{
4395 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4396 aName.c_str(), aControllerPort, aDevice));
4397
4398 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4399}
4400
4401HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4402 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4403{
4404 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4405 aName.c_str(), aControllerPort, aDevice));
4406
4407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4408
4409 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4410 if (FAILED(rc)) return rc;
4411
4412 if (Global::IsOnlineOrTransient(mData->mMachineState))
4413 return setError(VBOX_E_INVALID_VM_STATE,
4414 tr("Invalid machine state: %s"),
4415 Global::stringifyMachineState(mData->mMachineState));
4416
4417 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4418 aName,
4419 aControllerPort,
4420 aDevice);
4421 if (!pAttach)
4422 return setError(VBOX_E_OBJECT_NOT_FOUND,
4423 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4424 aDevice, aControllerPort, aName.c_str());
4425
4426
4427 i_setModified(IsModified_Storage);
4428 mMediumAttachments.backup();
4429
4430 IBandwidthGroup *iB = aBandwidthGroup;
4431 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4432 if (aBandwidthGroup && group.isNull())
4433 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4434
4435 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4436
4437 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4438 if (strBandwidthGroupOld.isNotEmpty())
4439 {
4440 /* Get the bandwidth group object and release it - this must not fail. */
4441 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4442 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4443 Assert(SUCCEEDED(rc));
4444
4445 pBandwidthGroupOld->i_release();
4446 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4447 }
4448
4449 if (!group.isNull())
4450 {
4451 group->i_reference();
4452 pAttach->i_updateBandwidthGroup(group->i_getName());
4453 }
4454
4455 return S_OK;
4456}
4457
4458HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4459 LONG aControllerPort,
4460 LONG aDevice,
4461 DeviceType_T aType)
4462{
4463 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4464 aName.c_str(), aControllerPort, aDevice, aType));
4465
4466 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4467}
4468
4469
4470HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4471 LONG aControllerPort,
4472 LONG aDevice,
4473 BOOL aForce)
4474{
4475 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4476 aName.c_str(), aControllerPort, aForce));
4477
4478 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4479}
4480
4481HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 const ComPtr<IMedium> &aMedium,
4485 BOOL aForce)
4486{
4487 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4488 aName.c_str(), aControllerPort, aDevice, aForce));
4489
4490 // request the host lock first, since might be calling Host methods for getting host drives;
4491 // next, protect the media tree all the while we're in here, as well as our member variables
4492 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4493 this->lockHandle(),
4494 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4495
4496 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4497 aName,
4498 aControllerPort,
4499 aDevice);
4500 if (pAttach.isNull())
4501 return setError(VBOX_E_OBJECT_NOT_FOUND,
4502 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4503 aDevice, aControllerPort, aName.c_str());
4504
4505 /* Remember previously mounted medium. The medium before taking the
4506 * backup is not necessarily the same thing. */
4507 ComObjPtr<Medium> oldmedium;
4508 oldmedium = pAttach->i_getMedium();
4509
4510 IMedium *iM = aMedium;
4511 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4512 if (aMedium && pMedium.isNull())
4513 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4514
4515 AutoCaller mediumCaller(pMedium);
4516 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4517
4518 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4519 if (pMedium)
4520 {
4521 DeviceType_T mediumType = pAttach->i_getType();
4522 switch (mediumType)
4523 {
4524 case DeviceType_DVD:
4525 case DeviceType_Floppy:
4526 break;
4527
4528 default:
4529 return setError(VBOX_E_INVALID_OBJECT_STATE,
4530 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4531 aControllerPort,
4532 aDevice,
4533 aName.c_str());
4534 }
4535 }
4536
4537 i_setModified(IsModified_Storage);
4538 mMediumAttachments.backup();
4539
4540 {
4541 // The backup operation makes the pAttach reference point to the
4542 // old settings. Re-get the correct reference.
4543 pAttach = i_findAttachment(*mMediumAttachments.data(),
4544 aName,
4545 aControllerPort,
4546 aDevice);
4547 if (!oldmedium.isNull())
4548 oldmedium->i_removeBackReference(mData->mUuid);
4549 if (!pMedium.isNull())
4550 {
4551 pMedium->i_addBackReference(mData->mUuid);
4552
4553 mediumLock.release();
4554 multiLock.release();
4555 i_addMediumToRegistry(pMedium);
4556 multiLock.acquire();
4557 mediumLock.acquire();
4558 }
4559
4560 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4561 pAttach->i_updateMedium(pMedium);
4562 }
4563
4564 i_setModified(IsModified_Storage);
4565
4566 mediumLock.release();
4567 multiLock.release();
4568 HRESULT rc = i_onMediumChange(pAttach, aForce);
4569 multiLock.acquire();
4570 mediumLock.acquire();
4571
4572 /* On error roll back this change only. */
4573 if (FAILED(rc))
4574 {
4575 if (!pMedium.isNull())
4576 pMedium->i_removeBackReference(mData->mUuid);
4577 pAttach = i_findAttachment(*mMediumAttachments.data(),
4578 aName,
4579 aControllerPort,
4580 aDevice);
4581 /* If the attachment is gone in the meantime, bail out. */
4582 if (pAttach.isNull())
4583 return rc;
4584 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4585 if (!oldmedium.isNull())
4586 oldmedium->i_addBackReference(mData->mUuid);
4587 pAttach->i_updateMedium(oldmedium);
4588 }
4589
4590 mediumLock.release();
4591 multiLock.release();
4592
4593 /* Save modified registries, but skip this machine as it's the caller's
4594 * job to save its settings like all other settings changes. */
4595 mParent->i_unmarkRegistryModified(i_getId());
4596 mParent->i_saveModifiedRegistries();
4597
4598 return rc;
4599}
4600HRESULT Machine::getMedium(const com::Utf8Str &aName,
4601 LONG aControllerPort,
4602 LONG aDevice,
4603 ComPtr<IMedium> &aMedium)
4604{
4605 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4606 aName.c_str(), aControllerPort, aDevice));
4607
4608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4609
4610 aMedium = NULL;
4611
4612 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4613 aName,
4614 aControllerPort,
4615 aDevice);
4616 if (pAttach.isNull())
4617 return setError(VBOX_E_OBJECT_NOT_FOUND,
4618 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4619 aDevice, aControllerPort, aName.c_str());
4620
4621 aMedium = pAttach->i_getMedium();
4622
4623 return S_OK;
4624}
4625
4626HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4627{
4628 if (aSlot < RT_ELEMENTS(mSerialPorts))
4629 {
4630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4631 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4632 return S_OK;
4633 }
4634 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4635}
4636
4637HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4638{
4639 if (aSlot < RT_ELEMENTS(mParallelPorts))
4640 {
4641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4642 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4643 return S_OK;
4644 }
4645 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4646}
4647
4648
4649HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4650{
4651 /* Do not assert if slot is out of range, just return the advertised
4652 status. testdriver/vbox.py triggers this in logVmInfo. */
4653 if (aSlot >= mNetworkAdapters.size())
4654 return setError(E_INVALIDARG,
4655 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4656 aSlot, mNetworkAdapters.size());
4657
4658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4659
4660 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4661
4662 return S_OK;
4663}
4664
4665HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4666{
4667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4668
4669 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4670 size_t i = 0;
4671 for (settings::StringsMap::const_iterator
4672 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4673 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4674 ++it, ++i)
4675 aKeys[i] = it->first;
4676
4677 return S_OK;
4678}
4679
4680 /**
4681 * @note Locks this object for reading.
4682 */
4683HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4684 com::Utf8Str &aValue)
4685{
4686 /* start with nothing found */
4687 aValue = "";
4688
4689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4690
4691 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4692 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4693 // found:
4694 aValue = it->second; // source is a Utf8Str
4695
4696 /* return the result to caller (may be empty) */
4697 return S_OK;
4698}
4699
4700 /**
4701 * @note Locks mParent for writing + this object for writing.
4702 */
4703HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4704{
4705 /* Because control characters in aKey have caused problems in the settings
4706 * they are rejected unless the key should be deleted. */
4707 if (!aValue.isEmpty())
4708 {
4709 for (size_t i = 0; i < aKey.length(); ++i)
4710 {
4711 char ch = aKey[i];
4712 if (RTLocCIsCntrl(ch))
4713 return E_INVALIDARG;
4714 }
4715 }
4716
4717 Utf8Str strOldValue; // empty
4718
4719 // locking note: we only hold the read lock briefly to look up the old value,
4720 // then release it and call the onExtraCanChange callbacks. There is a small
4721 // chance of a race insofar as the callback might be called twice if two callers
4722 // change the same key at the same time, but that's a much better solution
4723 // than the deadlock we had here before. The actual changing of the extradata
4724 // is then performed under the write lock and race-free.
4725
4726 // look up the old value first; if nothing has changed then we need not do anything
4727 {
4728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4729
4730 // For snapshots don't even think about allowing changes, extradata
4731 // is global for a machine, so there is nothing snapshot specific.
4732 if (i_isSnapshotMachine())
4733 return setError(VBOX_E_INVALID_VM_STATE,
4734 tr("Cannot set extradata for a snapshot"));
4735
4736 // check if the right IMachine instance is used
4737 if (mData->mRegistered && !i_isSessionMachine())
4738 return setError(VBOX_E_INVALID_VM_STATE,
4739 tr("Cannot set extradata for an immutable machine"));
4740
4741 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4742 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4743 strOldValue = it->second;
4744 }
4745
4746 bool fChanged;
4747 if ((fChanged = (strOldValue != aValue)))
4748 {
4749 // ask for permission from all listeners outside the locks;
4750 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4751 // lock to copy the list of callbacks to invoke
4752 Bstr bstrError;
4753 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4754 {
4755 const char *sep = bstrError.isEmpty() ? "" : ": ";
4756 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4757 return setError(E_ACCESSDENIED,
4758 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4759 aKey.c_str(),
4760 aValue.c_str(),
4761 sep,
4762 bstrError.raw());
4763 }
4764
4765 // data is changing and change not vetoed: then write it out under the lock
4766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4767
4768 if (aValue.isEmpty())
4769 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4770 else
4771 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4772 // creates a new key if needed
4773
4774 bool fNeedsGlobalSaveSettings = false;
4775 // This saving of settings is tricky: there is no "old state" for the
4776 // extradata items at all (unlike all other settings), so the old/new
4777 // settings comparison would give a wrong result!
4778 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4779
4780 if (fNeedsGlobalSaveSettings)
4781 {
4782 // save the global settings; for that we should hold only the VirtualBox lock
4783 alock.release();
4784 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4785 mParent->i_saveSettings();
4786 }
4787 }
4788
4789 // fire notification outside the lock
4790 if (fChanged)
4791 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4792
4793 return S_OK;
4794}
4795
4796HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4797{
4798 aProgress = NULL;
4799 NOREF(aSettingsFilePath);
4800 ReturnComNotImplemented();
4801}
4802
4803HRESULT Machine::saveSettings()
4804{
4805 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4806
4807 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4808 if (FAILED(rc)) return rc;
4809
4810 /* the settings file path may never be null */
4811 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4812
4813 /* save all VM data excluding snapshots */
4814 bool fNeedsGlobalSaveSettings = false;
4815 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4816 mlock.release();
4817
4818 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4819 {
4820 // save the global settings; for that we should hold only the VirtualBox lock
4821 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4822 rc = mParent->i_saveSettings();
4823 }
4824
4825 return rc;
4826}
4827
4828
4829HRESULT Machine::discardSettings()
4830{
4831 /*
4832 * We need to take the machine list lock here as well as the machine one
4833 * or we'll get into trouble should any media stuff require rolling back.
4834 *
4835 * Details:
4836 *
4837 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4838 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4839 * 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]
4840 * 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
4841 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4842 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4843 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4844 * 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
4845 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4846 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4847 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4848 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4849 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4850 * 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]
4851 * 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] (*)
4852 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4853 * 0:005> k
4854 * # Child-SP RetAddr Call Site
4855 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4856 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4857 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4858 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4859 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4860 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4861 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4862 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4863 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4864 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4865 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4866 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4867 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4868 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4869 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4870 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4871 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4872 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4873 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4874 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4875 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4876 *
4877 */
4878 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4880
4881 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4882 if (FAILED(rc)) return rc;
4883
4884 /*
4885 * during this rollback, the session will be notified if data has
4886 * been actually changed
4887 */
4888 i_rollback(true /* aNotify */);
4889
4890 return S_OK;
4891}
4892
4893/** @note Locks objects! */
4894HRESULT Machine::unregister(AutoCaller &autoCaller,
4895 CleanupMode_T aCleanupMode,
4896 std::vector<ComPtr<IMedium> > &aMedia)
4897{
4898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4899
4900 Guid id(i_getId());
4901
4902 if (mData->mSession.mState != SessionState_Unlocked)
4903 return setError(VBOX_E_INVALID_OBJECT_STATE,
4904 tr("Cannot unregister the machine '%s' while it is locked"),
4905 mUserData->s.strName.c_str());
4906
4907 // wait for state dependents to drop to zero
4908 i_ensureNoStateDependencies();
4909
4910 if (!mData->mAccessible)
4911 {
4912 // inaccessible machines can only be unregistered; uninitialize ourselves
4913 // here because currently there may be no unregistered that are inaccessible
4914 // (this state combination is not supported). Note releasing the caller and
4915 // leaving the lock before calling uninit()
4916 alock.release();
4917 autoCaller.release();
4918
4919 uninit();
4920
4921 mParent->i_unregisterMachine(this, id);
4922 // calls VirtualBox::i_saveSettings()
4923
4924 return S_OK;
4925 }
4926
4927 HRESULT rc = S_OK;
4928 mData->llFilesToDelete.clear();
4929
4930 if (!mSSData->strStateFilePath.isEmpty())
4931 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4932
4933 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4934 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4935 mData->llFilesToDelete.push_back(strNVRAMFile);
4936
4937 // This list collects the medium objects from all medium attachments
4938 // which we will detach from the machine and its snapshots, in a specific
4939 // order which allows for closing all media without getting "media in use"
4940 // errors, simply by going through the list from the front to the back:
4941 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4942 // and must be closed before the parent media from the snapshots, or closing the parents
4943 // will fail because they still have children);
4944 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4945 // the root ("first") snapshot of the machine.
4946 MediaList llMedia;
4947
4948 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4949 && mMediumAttachments->size()
4950 )
4951 {
4952 // we have media attachments: detach them all and add the Medium objects to our list
4953 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4954 }
4955
4956 if (mData->mFirstSnapshot)
4957 {
4958 // add the media from the medium attachments of the snapshots to llMedia
4959 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4960 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4961 // into the children first
4962
4963 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4964 MachineState_T oldState = mData->mMachineState;
4965 mData->mMachineState = MachineState_DeletingSnapshot;
4966
4967 // make a copy of the first snapshot reference so the refcount does not
4968 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4969 // (would hang due to the AutoCaller voodoo)
4970 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4971
4972 // GO!
4973 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4974
4975 mData->mMachineState = oldState;
4976 }
4977
4978 if (FAILED(rc))
4979 {
4980 i_rollbackMedia();
4981 return rc;
4982 }
4983
4984 // commit all the media changes made above
4985 i_commitMedia();
4986
4987 mData->mRegistered = false;
4988
4989 // machine lock no longer needed
4990 alock.release();
4991
4992 /* Make sure that the settings of the current VM are not saved, because
4993 * they are rather crippled at this point to meet the cleanup expectations
4994 * and there's no point destroying the VM config on disk just because. */
4995 mParent->i_unmarkRegistryModified(id);
4996
4997 // return media to caller
4998 aMedia.resize(llMedia.size());
4999 size_t i = 0;
5000 for (MediaList::const_iterator
5001 it = llMedia.begin();
5002 it != llMedia.end();
5003 ++it, ++i)
5004 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5005
5006 mParent->i_unregisterMachine(this, id);
5007 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5008
5009 return S_OK;
5010}
5011
5012/**
5013 * Task record for deleting a machine config.
5014 */
5015class Machine::DeleteConfigTask
5016 : public Machine::Task
5017{
5018public:
5019 DeleteConfigTask(Machine *m,
5020 Progress *p,
5021 const Utf8Str &t,
5022 const RTCList<ComPtr<IMedium> > &llMediums,
5023 const StringsList &llFilesToDelete)
5024 : Task(m, p, t),
5025 m_llMediums(llMediums),
5026 m_llFilesToDelete(llFilesToDelete)
5027 {}
5028
5029private:
5030 void handler()
5031 {
5032 try
5033 {
5034 m_pMachine->i_deleteConfigHandler(*this);
5035 }
5036 catch (...)
5037 {
5038 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5039 }
5040 }
5041
5042 RTCList<ComPtr<IMedium> > m_llMediums;
5043 StringsList m_llFilesToDelete;
5044
5045 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5046};
5047
5048/**
5049 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5050 * SessionMachine::taskHandler().
5051 *
5052 * @note Locks this object for writing.
5053 *
5054 * @param task
5055 * @return
5056 */
5057void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5058{
5059 LogFlowThisFuncEnter();
5060
5061 AutoCaller autoCaller(this);
5062 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5063 if (FAILED(autoCaller.rc()))
5064 {
5065 /* we might have been uninitialized because the session was accidentally
5066 * closed by the client, so don't assert */
5067 HRESULT rc = setError(E_FAIL,
5068 tr("The session has been accidentally closed"));
5069 task.m_pProgress->i_notifyComplete(rc);
5070 LogFlowThisFuncLeave();
5071 return;
5072 }
5073
5074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5075
5076 HRESULT rc = S_OK;
5077
5078 try
5079 {
5080 ULONG uLogHistoryCount = 3;
5081 ComPtr<ISystemProperties> systemProperties;
5082 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5083 if (FAILED(rc)) throw rc;
5084
5085 if (!systemProperties.isNull())
5086 {
5087 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5088 if (FAILED(rc)) throw rc;
5089 }
5090
5091 MachineState_T oldState = mData->mMachineState;
5092 i_setMachineState(MachineState_SettingUp);
5093 alock.release();
5094 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5095 {
5096 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5097 {
5098 AutoCaller mac(pMedium);
5099 if (FAILED(mac.rc())) throw mac.rc();
5100 Utf8Str strLocation = pMedium->i_getLocationFull();
5101 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5102 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5103 if (FAILED(rc)) throw rc;
5104 }
5105 if (pMedium->i_isMediumFormatFile())
5106 {
5107 ComPtr<IProgress> pProgress2;
5108 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5109 if (FAILED(rc)) throw rc;
5110 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5111 if (FAILED(rc)) throw rc;
5112 }
5113
5114 /* Close the medium, deliberately without checking the return
5115 * code, and without leaving any trace in the error info, as
5116 * a failure here is a very minor issue, which shouldn't happen
5117 * as above we even managed to delete the medium. */
5118 {
5119 ErrorInfoKeeper eik;
5120 pMedium->Close();
5121 }
5122 }
5123 i_setMachineState(oldState);
5124 alock.acquire();
5125
5126 // delete the files pushed on the task list by Machine::Delete()
5127 // (this includes saved states of the machine and snapshots and
5128 // medium storage files from the IMedium list passed in, and the
5129 // machine XML file)
5130 for (StringsList::const_iterator
5131 it = task.m_llFilesToDelete.begin();
5132 it != task.m_llFilesToDelete.end();
5133 ++it)
5134 {
5135 const Utf8Str &strFile = *it;
5136 LogFunc(("Deleting file %s\n", strFile.c_str()));
5137 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5138 if (FAILED(rc)) throw rc;
5139
5140 int vrc = RTFileDelete(strFile.c_str());
5141 if (RT_FAILURE(vrc))
5142 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5143 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5144 }
5145
5146 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5147 if (FAILED(rc)) throw rc;
5148
5149 /* delete the settings only when the file actually exists */
5150 if (mData->pMachineConfigFile->fileExists())
5151 {
5152 /* Delete any backup or uncommitted XML files. Ignore failures.
5153 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5154 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5155 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5156 RTFileDelete(otherXml.c_str());
5157 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5158 RTFileDelete(otherXml.c_str());
5159
5160 /* delete the Logs folder, nothing important should be left
5161 * there (we don't check for errors because the user might have
5162 * some private files there that we don't want to delete) */
5163 Utf8Str logFolder;
5164 getLogFolder(logFolder);
5165 Assert(logFolder.length());
5166 if (RTDirExists(logFolder.c_str()))
5167 {
5168 /* Delete all VBox.log[.N] files from the Logs folder
5169 * (this must be in sync with the rotation logic in
5170 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5171 * files that may have been created by the GUI. */
5172 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5173 RTFileDelete(log.c_str());
5174 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5175 RTFileDelete(log.c_str());
5176 for (ULONG i = uLogHistoryCount; i > 0; i--)
5177 {
5178 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5179 RTFileDelete(log.c_str());
5180 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5181 RTFileDelete(log.c_str());
5182 }
5183 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5184 RTFileDelete(log.c_str());
5185#if defined(RT_OS_WINDOWS)
5186 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5187 RTFileDelete(log.c_str());
5188 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5189 RTFileDelete(log.c_str());
5190#endif
5191
5192 RTDirRemove(logFolder.c_str());
5193 }
5194
5195 /* delete the Snapshots folder, nothing important should be left
5196 * there (we don't check for errors because the user might have
5197 * some private files there that we don't want to delete) */
5198 Utf8Str strFullSnapshotFolder;
5199 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5200 Assert(!strFullSnapshotFolder.isEmpty());
5201 if (RTDirExists(strFullSnapshotFolder.c_str()))
5202 RTDirRemove(strFullSnapshotFolder.c_str());
5203
5204 // delete the directory that contains the settings file, but only
5205 // if it matches the VM name
5206 Utf8Str settingsDir;
5207 if (i_isInOwnDir(&settingsDir))
5208 RTDirRemove(settingsDir.c_str());
5209 }
5210
5211 alock.release();
5212
5213 mParent->i_saveModifiedRegistries();
5214 }
5215 catch (HRESULT aRC) { rc = aRC; }
5216
5217 task.m_pProgress->i_notifyComplete(rc);
5218
5219 LogFlowThisFuncLeave();
5220}
5221
5222HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5223{
5224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5225
5226 HRESULT rc = i_checkStateDependency(MutableStateDep);
5227 if (FAILED(rc)) return rc;
5228
5229 if (mData->mRegistered)
5230 return setError(VBOX_E_INVALID_VM_STATE,
5231 tr("Cannot delete settings of a registered machine"));
5232
5233 // collect files to delete
5234 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5235 // machine config file
5236 if (mData->pMachineConfigFile->fileExists())
5237 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5238 // backup of machine config file
5239 Utf8Str strTmp(mData->m_strConfigFileFull);
5240 strTmp.append("-prev");
5241 if (RTFileExists(strTmp.c_str()))
5242 llFilesToDelete.push_back(strTmp);
5243
5244 RTCList<ComPtr<IMedium> > llMediums;
5245 for (size_t i = 0; i < aMedia.size(); ++i)
5246 {
5247 IMedium *pIMedium(aMedia[i]);
5248 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5249 if (pMedium.isNull())
5250 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5251 SafeArray<BSTR> ids;
5252 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5253 if (FAILED(rc)) return rc;
5254 /* At this point the medium should not have any back references
5255 * anymore. If it has it is attached to another VM and *must* not
5256 * deleted. */
5257 if (ids.size() < 1)
5258 llMediums.append(pMedium);
5259 }
5260
5261 ComObjPtr<Progress> pProgress;
5262 pProgress.createObject();
5263 rc = pProgress->init(i_getVirtualBox(),
5264 static_cast<IMachine*>(this) /* aInitiator */,
5265 tr("Deleting files"),
5266 true /* fCancellable */,
5267 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5268 tr("Collecting file inventory"));
5269 if (FAILED(rc))
5270 return rc;
5271
5272 /* create and start the task on a separate thread (note that it will not
5273 * start working until we release alock) */
5274 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5275 rc = pTask->createThread();
5276 pTask = NULL;
5277 if (FAILED(rc))
5278 return rc;
5279
5280 pProgress.queryInterfaceTo(aProgress.asOutParam());
5281
5282 LogFlowFuncLeave();
5283
5284 return S_OK;
5285}
5286
5287HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5288{
5289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5290
5291 ComObjPtr<Snapshot> pSnapshot;
5292 HRESULT rc;
5293
5294 if (aNameOrId.isEmpty())
5295 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5296 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5297 else
5298 {
5299 Guid uuid(aNameOrId);
5300 if (uuid.isValid())
5301 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5302 else
5303 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5304 }
5305 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5306
5307 return rc;
5308}
5309
5310HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5311 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5312{
5313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5314
5315 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5316 if (FAILED(rc)) return rc;
5317
5318 ComObjPtr<SharedFolder> sharedFolder;
5319 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5320 if (SUCCEEDED(rc))
5321 return setError(VBOX_E_OBJECT_IN_USE,
5322 tr("Shared folder named '%s' already exists"),
5323 aName.c_str());
5324
5325 sharedFolder.createObject();
5326 rc = sharedFolder->init(i_getMachine(),
5327 aName,
5328 aHostPath,
5329 !!aWritable,
5330 !!aAutomount,
5331 aAutoMountPoint,
5332 true /* fFailOnError */);
5333 if (FAILED(rc)) return rc;
5334
5335 i_setModified(IsModified_SharedFolders);
5336 mHWData.backup();
5337 mHWData->mSharedFolders.push_back(sharedFolder);
5338
5339 /* inform the direct session if any */
5340 alock.release();
5341 i_onSharedFolderChange();
5342
5343 return S_OK;
5344}
5345
5346HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5347{
5348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5349
5350 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5351 if (FAILED(rc)) return rc;
5352
5353 ComObjPtr<SharedFolder> sharedFolder;
5354 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5355 if (FAILED(rc)) return rc;
5356
5357 i_setModified(IsModified_SharedFolders);
5358 mHWData.backup();
5359 mHWData->mSharedFolders.remove(sharedFolder);
5360
5361 /* inform the direct session if any */
5362 alock.release();
5363 i_onSharedFolderChange();
5364
5365 return S_OK;
5366}
5367
5368HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5369{
5370 /* start with No */
5371 *aCanShow = FALSE;
5372
5373 ComPtr<IInternalSessionControl> directControl;
5374 {
5375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5376
5377 if (mData->mSession.mState != SessionState_Locked)
5378 return setError(VBOX_E_INVALID_VM_STATE,
5379 tr("Machine is not locked for session (session state: %s)"),
5380 Global::stringifySessionState(mData->mSession.mState));
5381
5382 if (mData->mSession.mLockType == LockType_VM)
5383 directControl = mData->mSession.mDirectControl;
5384 }
5385
5386 /* ignore calls made after #OnSessionEnd() is called */
5387 if (!directControl)
5388 return S_OK;
5389
5390 LONG64 dummy;
5391 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5392}
5393
5394HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5395{
5396 ComPtr<IInternalSessionControl> directControl;
5397 {
5398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5399
5400 if (mData->mSession.mState != SessionState_Locked)
5401 return setError(E_FAIL,
5402 tr("Machine is not locked for session (session state: %s)"),
5403 Global::stringifySessionState(mData->mSession.mState));
5404
5405 if (mData->mSession.mLockType == LockType_VM)
5406 directControl = mData->mSession.mDirectControl;
5407 }
5408
5409 /* ignore calls made after #OnSessionEnd() is called */
5410 if (!directControl)
5411 return S_OK;
5412
5413 BOOL dummy;
5414 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5415}
5416
5417#ifdef VBOX_WITH_GUEST_PROPS
5418/**
5419 * Look up a guest property in VBoxSVC's internal structures.
5420 */
5421HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5422 com::Utf8Str &aValue,
5423 LONG64 *aTimestamp,
5424 com::Utf8Str &aFlags) const
5425{
5426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5427
5428 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5429 if (it != mHWData->mGuestProperties.end())
5430 {
5431 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5432 aValue = it->second.strValue;
5433 *aTimestamp = it->second.mTimestamp;
5434 GuestPropWriteFlags(it->second.mFlags, szFlags);
5435 aFlags = Utf8Str(szFlags);
5436 }
5437
5438 return S_OK;
5439}
5440
5441/**
5442 * Query the VM that a guest property belongs to for the property.
5443 * @returns E_ACCESSDENIED if the VM process is not available or not
5444 * currently handling queries and the lookup should then be done in
5445 * VBoxSVC.
5446 */
5447HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5448 com::Utf8Str &aValue,
5449 LONG64 *aTimestamp,
5450 com::Utf8Str &aFlags) const
5451{
5452 HRESULT rc = S_OK;
5453 Bstr bstrValue;
5454 Bstr bstrFlags;
5455
5456 ComPtr<IInternalSessionControl> directControl;
5457 {
5458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5459 if (mData->mSession.mLockType == LockType_VM)
5460 directControl = mData->mSession.mDirectControl;
5461 }
5462
5463 /* ignore calls made after #OnSessionEnd() is called */
5464 if (!directControl)
5465 rc = E_ACCESSDENIED;
5466 else
5467 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5468 0 /* accessMode */,
5469 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5470
5471 aValue = bstrValue;
5472 aFlags = bstrFlags;
5473
5474 return rc;
5475}
5476#endif // VBOX_WITH_GUEST_PROPS
5477
5478HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5479 com::Utf8Str &aValue,
5480 LONG64 *aTimestamp,
5481 com::Utf8Str &aFlags)
5482{
5483#ifndef VBOX_WITH_GUEST_PROPS
5484 ReturnComNotImplemented();
5485#else // VBOX_WITH_GUEST_PROPS
5486
5487 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5488
5489 if (rc == E_ACCESSDENIED)
5490 /* The VM is not running or the service is not (yet) accessible */
5491 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5492 return rc;
5493#endif // VBOX_WITH_GUEST_PROPS
5494}
5495
5496HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5497{
5498 LONG64 dummyTimestamp;
5499 com::Utf8Str dummyFlags;
5500 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5501 return rc;
5502
5503}
5504HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5505{
5506 com::Utf8Str dummyFlags;
5507 com::Utf8Str dummyValue;
5508 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5509 return rc;
5510}
5511
5512#ifdef VBOX_WITH_GUEST_PROPS
5513/**
5514 * Set a guest property in VBoxSVC's internal structures.
5515 */
5516HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5517 const com::Utf8Str &aFlags, bool fDelete)
5518{
5519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5520 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5521 if (FAILED(rc)) return rc;
5522
5523 try
5524 {
5525 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5526 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5527 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5528
5529 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5530 if (it == mHWData->mGuestProperties.end())
5531 {
5532 if (!fDelete)
5533 {
5534 i_setModified(IsModified_MachineData);
5535 mHWData.backupEx();
5536
5537 RTTIMESPEC time;
5538 HWData::GuestProperty prop;
5539 prop.strValue = Bstr(aValue).raw();
5540 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5541 prop.mFlags = fFlags;
5542 mHWData->mGuestProperties[aName] = prop;
5543 }
5544 }
5545 else
5546 {
5547 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5548 {
5549 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5550 }
5551 else
5552 {
5553 i_setModified(IsModified_MachineData);
5554 mHWData.backupEx();
5555
5556 /* The backupEx() operation invalidates our iterator,
5557 * so get a new one. */
5558 it = mHWData->mGuestProperties.find(aName);
5559 Assert(it != mHWData->mGuestProperties.end());
5560
5561 if (!fDelete)
5562 {
5563 RTTIMESPEC time;
5564 it->second.strValue = aValue;
5565 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5566 it->second.mFlags = fFlags;
5567 }
5568 else
5569 mHWData->mGuestProperties.erase(it);
5570 }
5571 }
5572
5573 if (SUCCEEDED(rc))
5574 {
5575 alock.release();
5576
5577 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5578 }
5579 }
5580 catch (std::bad_alloc &)
5581 {
5582 rc = E_OUTOFMEMORY;
5583 }
5584
5585 return rc;
5586}
5587
5588/**
5589 * Set a property on the VM that that property belongs to.
5590 * @returns E_ACCESSDENIED if the VM process is not available or not
5591 * currently handling queries and the setting should then be done in
5592 * VBoxSVC.
5593 */
5594HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5595 const com::Utf8Str &aFlags, bool fDelete)
5596{
5597 HRESULT rc;
5598
5599 try
5600 {
5601 ComPtr<IInternalSessionControl> directControl;
5602 {
5603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5604 if (mData->mSession.mLockType == LockType_VM)
5605 directControl = mData->mSession.mDirectControl;
5606 }
5607
5608 Bstr dummy1; /* will not be changed (setter) */
5609 Bstr dummy2; /* will not be changed (setter) */
5610 LONG64 dummy64;
5611 if (!directControl)
5612 rc = E_ACCESSDENIED;
5613 else
5614 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5615 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5616 fDelete ? 2 : 1 /* accessMode */,
5617 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5618 }
5619 catch (std::bad_alloc &)
5620 {
5621 rc = E_OUTOFMEMORY;
5622 }
5623
5624 return rc;
5625}
5626#endif // VBOX_WITH_GUEST_PROPS
5627
5628HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5629 const com::Utf8Str &aFlags)
5630{
5631#ifndef VBOX_WITH_GUEST_PROPS
5632 ReturnComNotImplemented();
5633#else // VBOX_WITH_GUEST_PROPS
5634 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5635 if (rc == E_ACCESSDENIED)
5636 /* The VM is not running or the service is not (yet) accessible */
5637 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5638 return rc;
5639#endif // VBOX_WITH_GUEST_PROPS
5640}
5641
5642HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5643{
5644 return setGuestProperty(aProperty, aValue, "");
5645}
5646
5647HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5648{
5649#ifndef VBOX_WITH_GUEST_PROPS
5650 ReturnComNotImplemented();
5651#else // VBOX_WITH_GUEST_PROPS
5652 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5653 if (rc == E_ACCESSDENIED)
5654 /* The VM is not running or the service is not (yet) accessible */
5655 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5656 return rc;
5657#endif // VBOX_WITH_GUEST_PROPS
5658}
5659
5660#ifdef VBOX_WITH_GUEST_PROPS
5661/**
5662 * Enumerate the guest properties in VBoxSVC's internal structures.
5663 */
5664HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5665 std::vector<com::Utf8Str> &aNames,
5666 std::vector<com::Utf8Str> &aValues,
5667 std::vector<LONG64> &aTimestamps,
5668 std::vector<com::Utf8Str> &aFlags)
5669{
5670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5671 Utf8Str strPatterns(aPatterns);
5672
5673 /*
5674 * Look for matching patterns and build up a list.
5675 */
5676 HWData::GuestPropertyMap propMap;
5677 for (HWData::GuestPropertyMap::const_iterator
5678 it = mHWData->mGuestProperties.begin();
5679 it != mHWData->mGuestProperties.end();
5680 ++it)
5681 {
5682 if ( strPatterns.isEmpty()
5683 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5684 RTSTR_MAX,
5685 it->first.c_str(),
5686 RTSTR_MAX,
5687 NULL)
5688 )
5689 propMap.insert(*it);
5690 }
5691
5692 alock.release();
5693
5694 /*
5695 * And build up the arrays for returning the property information.
5696 */
5697 size_t cEntries = propMap.size();
5698
5699 aNames.resize(cEntries);
5700 aValues.resize(cEntries);
5701 aTimestamps.resize(cEntries);
5702 aFlags.resize(cEntries);
5703
5704 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5705 size_t i = 0;
5706 for (HWData::GuestPropertyMap::const_iterator
5707 it = propMap.begin();
5708 it != propMap.end();
5709 ++it, ++i)
5710 {
5711 aNames[i] = it->first;
5712 aValues[i] = it->second.strValue;
5713 aTimestamps[i] = it->second.mTimestamp;
5714 GuestPropWriteFlags(it->second.mFlags, szFlags);
5715 aFlags[i] = Utf8Str(szFlags);
5716 }
5717
5718 return S_OK;
5719}
5720
5721/**
5722 * Enumerate the properties managed by a VM.
5723 * @returns E_ACCESSDENIED if the VM process is not available or not
5724 * currently handling queries and the setting should then be done in
5725 * VBoxSVC.
5726 */
5727HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5728 std::vector<com::Utf8Str> &aNames,
5729 std::vector<com::Utf8Str> &aValues,
5730 std::vector<LONG64> &aTimestamps,
5731 std::vector<com::Utf8Str> &aFlags)
5732{
5733 HRESULT rc;
5734 ComPtr<IInternalSessionControl> directControl;
5735 {
5736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5737 if (mData->mSession.mLockType == LockType_VM)
5738 directControl = mData->mSession.mDirectControl;
5739 }
5740
5741 com::SafeArray<BSTR> bNames;
5742 com::SafeArray<BSTR> bValues;
5743 com::SafeArray<LONG64> bTimestamps;
5744 com::SafeArray<BSTR> bFlags;
5745
5746 if (!directControl)
5747 rc = E_ACCESSDENIED;
5748 else
5749 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5750 ComSafeArrayAsOutParam(bNames),
5751 ComSafeArrayAsOutParam(bValues),
5752 ComSafeArrayAsOutParam(bTimestamps),
5753 ComSafeArrayAsOutParam(bFlags));
5754 size_t i;
5755 aNames.resize(bNames.size());
5756 for (i = 0; i < bNames.size(); ++i)
5757 aNames[i] = Utf8Str(bNames[i]);
5758 aValues.resize(bValues.size());
5759 for (i = 0; i < bValues.size(); ++i)
5760 aValues[i] = Utf8Str(bValues[i]);
5761 aTimestamps.resize(bTimestamps.size());
5762 for (i = 0; i < bTimestamps.size(); ++i)
5763 aTimestamps[i] = bTimestamps[i];
5764 aFlags.resize(bFlags.size());
5765 for (i = 0; i < bFlags.size(); ++i)
5766 aFlags[i] = Utf8Str(bFlags[i]);
5767
5768 return rc;
5769}
5770#endif // VBOX_WITH_GUEST_PROPS
5771HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5772 std::vector<com::Utf8Str> &aNames,
5773 std::vector<com::Utf8Str> &aValues,
5774 std::vector<LONG64> &aTimestamps,
5775 std::vector<com::Utf8Str> &aFlags)
5776{
5777#ifndef VBOX_WITH_GUEST_PROPS
5778 ReturnComNotImplemented();
5779#else // VBOX_WITH_GUEST_PROPS
5780
5781 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5782
5783 if (rc == E_ACCESSDENIED)
5784 /* The VM is not running or the service is not (yet) accessible */
5785 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5786 return rc;
5787#endif // VBOX_WITH_GUEST_PROPS
5788}
5789
5790HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5791 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5792{
5793 MediumAttachmentList atts;
5794
5795 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5796 if (FAILED(rc)) return rc;
5797
5798 aMediumAttachments.resize(atts.size());
5799 size_t i = 0;
5800 for (MediumAttachmentList::const_iterator
5801 it = atts.begin();
5802 it != atts.end();
5803 ++it, ++i)
5804 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5805
5806 return S_OK;
5807}
5808
5809HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5810 LONG aControllerPort,
5811 LONG aDevice,
5812 ComPtr<IMediumAttachment> &aAttachment)
5813{
5814 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5815 aName.c_str(), aControllerPort, aDevice));
5816
5817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5818
5819 aAttachment = NULL;
5820
5821 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5822 aName,
5823 aControllerPort,
5824 aDevice);
5825 if (pAttach.isNull())
5826 return setError(VBOX_E_OBJECT_NOT_FOUND,
5827 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5828 aDevice, aControllerPort, aName.c_str());
5829
5830 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5831
5832 return S_OK;
5833}
5834
5835
5836HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5837 StorageBus_T aConnectionType,
5838 ComPtr<IStorageController> &aController)
5839{
5840 if ( (aConnectionType <= StorageBus_Null)
5841 || (aConnectionType > StorageBus_VirtioSCSI))
5842 return setError(E_INVALIDARG,
5843 tr("Invalid connection type: %d"),
5844 aConnectionType);
5845
5846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5847
5848 HRESULT rc = i_checkStateDependency(MutableStateDep);
5849 if (FAILED(rc)) return rc;
5850
5851 /* try to find one with the name first. */
5852 ComObjPtr<StorageController> ctrl;
5853
5854 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5855 if (SUCCEEDED(rc))
5856 return setError(VBOX_E_OBJECT_IN_USE,
5857 tr("Storage controller named '%s' already exists"),
5858 aName.c_str());
5859
5860 ctrl.createObject();
5861
5862 /* get a new instance number for the storage controller */
5863 ULONG ulInstance = 0;
5864 bool fBootable = true;
5865 for (StorageControllerList::const_iterator
5866 it = mStorageControllers->begin();
5867 it != mStorageControllers->end();
5868 ++it)
5869 {
5870 if ((*it)->i_getStorageBus() == aConnectionType)
5871 {
5872 ULONG ulCurInst = (*it)->i_getInstance();
5873
5874 if (ulCurInst >= ulInstance)
5875 ulInstance = ulCurInst + 1;
5876
5877 /* Only one controller of each type can be marked as bootable. */
5878 if ((*it)->i_getBootable())
5879 fBootable = false;
5880 }
5881 }
5882
5883 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5884 if (FAILED(rc)) return rc;
5885
5886 i_setModified(IsModified_Storage);
5887 mStorageControllers.backup();
5888 mStorageControllers->push_back(ctrl);
5889
5890 ctrl.queryInterfaceTo(aController.asOutParam());
5891
5892 /* inform the direct session if any */
5893 alock.release();
5894 i_onStorageControllerChange(i_getId(), aName);
5895
5896 return S_OK;
5897}
5898
5899HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5900 ComPtr<IStorageController> &aStorageController)
5901{
5902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5903
5904 ComObjPtr<StorageController> ctrl;
5905
5906 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5907 if (SUCCEEDED(rc))
5908 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5909
5910 return rc;
5911}
5912
5913HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5914 ULONG aInstance,
5915 ComPtr<IStorageController> &aStorageController)
5916{
5917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5918
5919 for (StorageControllerList::const_iterator
5920 it = mStorageControllers->begin();
5921 it != mStorageControllers->end();
5922 ++it)
5923 {
5924 if ( (*it)->i_getStorageBus() == aConnectionType
5925 && (*it)->i_getInstance() == aInstance)
5926 {
5927 (*it).queryInterfaceTo(aStorageController.asOutParam());
5928 return S_OK;
5929 }
5930 }
5931
5932 return setError(VBOX_E_OBJECT_NOT_FOUND,
5933 tr("Could not find a storage controller with instance number '%lu'"),
5934 aInstance);
5935}
5936
5937HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5938{
5939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5940
5941 HRESULT rc = i_checkStateDependency(MutableStateDep);
5942 if (FAILED(rc)) return rc;
5943
5944 ComObjPtr<StorageController> ctrl;
5945
5946 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5947 if (SUCCEEDED(rc))
5948 {
5949 /* Ensure that only one controller of each type is marked as bootable. */
5950 if (aBootable == TRUE)
5951 {
5952 for (StorageControllerList::const_iterator
5953 it = mStorageControllers->begin();
5954 it != mStorageControllers->end();
5955 ++it)
5956 {
5957 ComObjPtr<StorageController> aCtrl = (*it);
5958
5959 if ( (aCtrl->i_getName() != aName)
5960 && aCtrl->i_getBootable() == TRUE
5961 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5962 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5963 {
5964 aCtrl->i_setBootable(FALSE);
5965 break;
5966 }
5967 }
5968 }
5969
5970 if (SUCCEEDED(rc))
5971 {
5972 ctrl->i_setBootable(aBootable);
5973 i_setModified(IsModified_Storage);
5974 }
5975 }
5976
5977 if (SUCCEEDED(rc))
5978 {
5979 /* inform the direct session if any */
5980 alock.release();
5981 i_onStorageControllerChange(i_getId(), aName);
5982 }
5983
5984 return rc;
5985}
5986
5987HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5988{
5989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5990
5991 HRESULT rc = i_checkStateDependency(MutableStateDep);
5992 if (FAILED(rc)) return rc;
5993
5994 ComObjPtr<StorageController> ctrl;
5995 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5996 if (FAILED(rc)) return rc;
5997
5998 MediumAttachmentList llDetachedAttachments;
5999 {
6000 /* find all attached devices to the appropriate storage controller and detach them all */
6001 // make a temporary list because detachDevice invalidates iterators into
6002 // mMediumAttachments
6003 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6004
6005 for (MediumAttachmentList::const_iterator
6006 it = llAttachments2.begin();
6007 it != llAttachments2.end();
6008 ++it)
6009 {
6010 MediumAttachment *pAttachTemp = *it;
6011
6012 AutoCaller localAutoCaller(pAttachTemp);
6013 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6014
6015 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6016
6017 if (pAttachTemp->i_getControllerName() == aName)
6018 {
6019 llDetachedAttachments.push_back(pAttachTemp);
6020 rc = i_detachDevice(pAttachTemp, alock, NULL);
6021 if (FAILED(rc)) return rc;
6022 }
6023 }
6024 }
6025
6026 /* send event about detached devices before removing parent controller */
6027 for (MediumAttachmentList::const_iterator
6028 it = llDetachedAttachments.begin();
6029 it != llDetachedAttachments.end();
6030 ++it)
6031 {
6032 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6033 }
6034
6035 /* We can remove it now. */
6036 i_setModified(IsModified_Storage);
6037 mStorageControllers.backup();
6038
6039 ctrl->i_unshare();
6040
6041 mStorageControllers->remove(ctrl);
6042
6043 /* inform the direct session if any */
6044 alock.release();
6045 i_onStorageControllerChange(i_getId(), aName);
6046
6047 return S_OK;
6048}
6049
6050HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6051 ComPtr<IUSBController> &aController)
6052{
6053 if ( (aType <= USBControllerType_Null)
6054 || (aType >= USBControllerType_Last))
6055 return setError(E_INVALIDARG,
6056 tr("Invalid USB controller type: %d"),
6057 aType);
6058
6059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6060
6061 HRESULT rc = i_checkStateDependency(MutableStateDep);
6062 if (FAILED(rc)) return rc;
6063
6064 /* try to find one with the same type first. */
6065 ComObjPtr<USBController> ctrl;
6066
6067 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6068 if (SUCCEEDED(rc))
6069 return setError(VBOX_E_OBJECT_IN_USE,
6070 tr("USB controller named '%s' already exists"),
6071 aName.c_str());
6072
6073 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6074 ULONG maxInstances;
6075 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6076 if (FAILED(rc))
6077 return rc;
6078
6079 ULONG cInstances = i_getUSBControllerCountByType(aType);
6080 if (cInstances >= maxInstances)
6081 return setError(E_INVALIDARG,
6082 tr("Too many USB controllers of this type"));
6083
6084 ctrl.createObject();
6085
6086 rc = ctrl->init(this, aName, aType);
6087 if (FAILED(rc)) return rc;
6088
6089 i_setModified(IsModified_USB);
6090 mUSBControllers.backup();
6091 mUSBControllers->push_back(ctrl);
6092
6093 ctrl.queryInterfaceTo(aController.asOutParam());
6094
6095 /* inform the direct session if any */
6096 alock.release();
6097 i_onUSBControllerChange();
6098
6099 return S_OK;
6100}
6101
6102HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6103{
6104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6105
6106 ComObjPtr<USBController> ctrl;
6107
6108 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6109 if (SUCCEEDED(rc))
6110 ctrl.queryInterfaceTo(aController.asOutParam());
6111
6112 return rc;
6113}
6114
6115HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6116 ULONG *aControllers)
6117{
6118 if ( (aType <= USBControllerType_Null)
6119 || (aType >= USBControllerType_Last))
6120 return setError(E_INVALIDARG,
6121 tr("Invalid USB controller type: %d"),
6122 aType);
6123
6124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6125
6126 ComObjPtr<USBController> ctrl;
6127
6128 *aControllers = i_getUSBControllerCountByType(aType);
6129
6130 return S_OK;
6131}
6132
6133HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6134{
6135
6136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6137
6138 HRESULT rc = i_checkStateDependency(MutableStateDep);
6139 if (FAILED(rc)) return rc;
6140
6141 ComObjPtr<USBController> ctrl;
6142 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6143 if (FAILED(rc)) return rc;
6144
6145 i_setModified(IsModified_USB);
6146 mUSBControllers.backup();
6147
6148 ctrl->i_unshare();
6149
6150 mUSBControllers->remove(ctrl);
6151
6152 /* inform the direct session if any */
6153 alock.release();
6154 i_onUSBControllerChange();
6155
6156 return S_OK;
6157}
6158
6159HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6160 ULONG *aOriginX,
6161 ULONG *aOriginY,
6162 ULONG *aWidth,
6163 ULONG *aHeight,
6164 BOOL *aEnabled)
6165{
6166 uint32_t u32OriginX= 0;
6167 uint32_t u32OriginY= 0;
6168 uint32_t u32Width = 0;
6169 uint32_t u32Height = 0;
6170 uint16_t u16Flags = 0;
6171
6172 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6173 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6174 if (RT_FAILURE(vrc))
6175 {
6176#ifdef RT_OS_WINDOWS
6177 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6178 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6179 * So just assign fEnable to TRUE again.
6180 * The right fix would be to change GUI API wrappers to make sure that parameters
6181 * are changed only if API succeeds.
6182 */
6183 *aEnabled = TRUE;
6184#endif
6185 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6186 tr("Saved guest size is not available (%Rrc)"),
6187 vrc);
6188 }
6189
6190 *aOriginX = u32OriginX;
6191 *aOriginY = u32OriginY;
6192 *aWidth = u32Width;
6193 *aHeight = u32Height;
6194 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6195
6196 return S_OK;
6197}
6198
6199HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6200 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6201{
6202 if (aScreenId != 0)
6203 return E_NOTIMPL;
6204
6205 if ( aBitmapFormat != BitmapFormat_BGR0
6206 && aBitmapFormat != BitmapFormat_BGRA
6207 && aBitmapFormat != BitmapFormat_RGBA
6208 && aBitmapFormat != BitmapFormat_PNG)
6209 return setError(E_NOTIMPL,
6210 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6211
6212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6213
6214 uint8_t *pu8Data = NULL;
6215 uint32_t cbData = 0;
6216 uint32_t u32Width = 0;
6217 uint32_t u32Height = 0;
6218
6219 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6220
6221 if (RT_FAILURE(vrc))
6222 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6223 tr("Saved thumbnail data is not available (%Rrc)"),
6224 vrc);
6225
6226 HRESULT hr = S_OK;
6227
6228 *aWidth = u32Width;
6229 *aHeight = u32Height;
6230
6231 if (cbData > 0)
6232 {
6233 /* Convert pixels to the format expected by the API caller. */
6234 if (aBitmapFormat == BitmapFormat_BGR0)
6235 {
6236 /* [0] B, [1] G, [2] R, [3] 0. */
6237 aData.resize(cbData);
6238 memcpy(&aData.front(), pu8Data, cbData);
6239 }
6240 else if (aBitmapFormat == BitmapFormat_BGRA)
6241 {
6242 /* [0] B, [1] G, [2] R, [3] A. */
6243 aData.resize(cbData);
6244 for (uint32_t i = 0; i < cbData; i += 4)
6245 {
6246 aData[i] = pu8Data[i];
6247 aData[i + 1] = pu8Data[i + 1];
6248 aData[i + 2] = pu8Data[i + 2];
6249 aData[i + 3] = 0xff;
6250 }
6251 }
6252 else if (aBitmapFormat == BitmapFormat_RGBA)
6253 {
6254 /* [0] R, [1] G, [2] B, [3] A. */
6255 aData.resize(cbData);
6256 for (uint32_t i = 0; i < cbData; i += 4)
6257 {
6258 aData[i] = pu8Data[i + 2];
6259 aData[i + 1] = pu8Data[i + 1];
6260 aData[i + 2] = pu8Data[i];
6261 aData[i + 3] = 0xff;
6262 }
6263 }
6264 else if (aBitmapFormat == BitmapFormat_PNG)
6265 {
6266 uint8_t *pu8PNG = NULL;
6267 uint32_t cbPNG = 0;
6268 uint32_t cxPNG = 0;
6269 uint32_t cyPNG = 0;
6270
6271 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6272
6273 if (RT_SUCCESS(vrc))
6274 {
6275 aData.resize(cbPNG);
6276 if (cbPNG)
6277 memcpy(&aData.front(), pu8PNG, cbPNG);
6278 }
6279 else
6280 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6281 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6282 vrc);
6283
6284 RTMemFree(pu8PNG);
6285 }
6286 }
6287
6288 freeSavedDisplayScreenshot(pu8Data);
6289
6290 return hr;
6291}
6292
6293HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6294 ULONG *aWidth,
6295 ULONG *aHeight,
6296 std::vector<BitmapFormat_T> &aBitmapFormats)
6297{
6298 if (aScreenId != 0)
6299 return E_NOTIMPL;
6300
6301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6302
6303 uint8_t *pu8Data = NULL;
6304 uint32_t cbData = 0;
6305 uint32_t u32Width = 0;
6306 uint32_t u32Height = 0;
6307
6308 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6309
6310 if (RT_FAILURE(vrc))
6311 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6312 tr("Saved screenshot data is not available (%Rrc)"),
6313 vrc);
6314
6315 *aWidth = u32Width;
6316 *aHeight = u32Height;
6317 aBitmapFormats.resize(1);
6318 aBitmapFormats[0] = BitmapFormat_PNG;
6319
6320 freeSavedDisplayScreenshot(pu8Data);
6321
6322 return S_OK;
6323}
6324
6325HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6326 BitmapFormat_T aBitmapFormat,
6327 ULONG *aWidth,
6328 ULONG *aHeight,
6329 std::vector<BYTE> &aData)
6330{
6331 if (aScreenId != 0)
6332 return E_NOTIMPL;
6333
6334 if (aBitmapFormat != BitmapFormat_PNG)
6335 return E_NOTIMPL;
6336
6337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6338
6339 uint8_t *pu8Data = NULL;
6340 uint32_t cbData = 0;
6341 uint32_t u32Width = 0;
6342 uint32_t u32Height = 0;
6343
6344 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6345
6346 if (RT_FAILURE(vrc))
6347 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6348 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6349 vrc);
6350
6351 *aWidth = u32Width;
6352 *aHeight = u32Height;
6353
6354 aData.resize(cbData);
6355 if (cbData)
6356 memcpy(&aData.front(), pu8Data, cbData);
6357
6358 freeSavedDisplayScreenshot(pu8Data);
6359
6360 return S_OK;
6361}
6362
6363HRESULT Machine::hotPlugCPU(ULONG aCpu)
6364{
6365 HRESULT rc = S_OK;
6366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6367
6368 if (!mHWData->mCPUHotPlugEnabled)
6369 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6370
6371 if (aCpu >= mHWData->mCPUCount)
6372 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6373
6374 if (mHWData->mCPUAttached[aCpu])
6375 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6376
6377 alock.release();
6378 rc = i_onCPUChange(aCpu, false);
6379 alock.acquire();
6380 if (FAILED(rc)) return rc;
6381
6382 i_setModified(IsModified_MachineData);
6383 mHWData.backup();
6384 mHWData->mCPUAttached[aCpu] = true;
6385
6386 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6387 if (Global::IsOnline(mData->mMachineState))
6388 i_saveSettings(NULL);
6389
6390 return S_OK;
6391}
6392
6393HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6394{
6395 HRESULT rc = S_OK;
6396
6397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6398
6399 if (!mHWData->mCPUHotPlugEnabled)
6400 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6401
6402 if (aCpu >= SchemaDefs::MaxCPUCount)
6403 return setError(E_INVALIDARG,
6404 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6405 SchemaDefs::MaxCPUCount);
6406
6407 if (!mHWData->mCPUAttached[aCpu])
6408 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6409
6410 /* CPU 0 can't be detached */
6411 if (aCpu == 0)
6412 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6413
6414 alock.release();
6415 rc = i_onCPUChange(aCpu, true);
6416 alock.acquire();
6417 if (FAILED(rc)) return rc;
6418
6419 i_setModified(IsModified_MachineData);
6420 mHWData.backup();
6421 mHWData->mCPUAttached[aCpu] = false;
6422
6423 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6424 if (Global::IsOnline(mData->mMachineState))
6425 i_saveSettings(NULL);
6426
6427 return S_OK;
6428}
6429
6430HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6431{
6432 *aAttached = false;
6433
6434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6435
6436 /* If hotplug is enabled the CPU is always enabled. */
6437 if (!mHWData->mCPUHotPlugEnabled)
6438 {
6439 if (aCpu < mHWData->mCPUCount)
6440 *aAttached = true;
6441 }
6442 else
6443 {
6444 if (aCpu < SchemaDefs::MaxCPUCount)
6445 *aAttached = mHWData->mCPUAttached[aCpu];
6446 }
6447
6448 return S_OK;
6449}
6450
6451HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6452{
6453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 Utf8Str log = i_getLogFilename(aIdx);
6456 if (!RTFileExists(log.c_str()))
6457 log.setNull();
6458 aFilename = log;
6459
6460 return S_OK;
6461}
6462
6463HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6464{
6465 if (aSize < 0)
6466 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6467
6468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6469
6470 HRESULT rc = S_OK;
6471 Utf8Str log = i_getLogFilename(aIdx);
6472
6473 /* do not unnecessarily hold the lock while doing something which does
6474 * not need the lock and potentially takes a long time. */
6475 alock.release();
6476
6477 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6478 * keeps the SOAP reply size under 1M for the webservice (we're using
6479 * base64 encoded strings for binary data for years now, avoiding the
6480 * expansion of each byte array element to approx. 25 bytes of XML. */
6481 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6482 aData.resize(cbData);
6483
6484 RTFILE LogFile;
6485 int vrc = RTFileOpen(&LogFile, log.c_str(),
6486 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6487 if (RT_SUCCESS(vrc))
6488 {
6489 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6490 if (RT_SUCCESS(vrc))
6491 aData.resize(cbData);
6492 else
6493 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6494 tr("Could not read log file '%s' (%Rrc)"),
6495 log.c_str(), vrc);
6496 RTFileClose(LogFile);
6497 }
6498 else
6499 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6500 tr("Could not open log file '%s' (%Rrc)"),
6501 log.c_str(), vrc);
6502
6503 if (FAILED(rc))
6504 aData.resize(0);
6505
6506 return rc;
6507}
6508
6509
6510/**
6511 * Currently this method doesn't attach device to the running VM,
6512 * just makes sure it's plugged on next VM start.
6513 */
6514HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6515{
6516 // lock scope
6517 {
6518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6519
6520 HRESULT rc = i_checkStateDependency(MutableStateDep);
6521 if (FAILED(rc)) return rc;
6522
6523 ChipsetType_T aChipset = ChipsetType_PIIX3;
6524 COMGETTER(ChipsetType)(&aChipset);
6525
6526 if (aChipset != ChipsetType_ICH9)
6527 {
6528 return setError(E_INVALIDARG,
6529 tr("Host PCI attachment only supported with ICH9 chipset"));
6530 }
6531
6532 // check if device with this host PCI address already attached
6533 for (HWData::PCIDeviceAssignmentList::const_iterator
6534 it = mHWData->mPCIDeviceAssignments.begin();
6535 it != mHWData->mPCIDeviceAssignments.end();
6536 ++it)
6537 {
6538 LONG iHostAddress = -1;
6539 ComPtr<PCIDeviceAttachment> pAttach;
6540 pAttach = *it;
6541 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6542 if (iHostAddress == aHostAddress)
6543 return setError(E_INVALIDARG,
6544 tr("Device with host PCI address already attached to this VM"));
6545 }
6546
6547 ComObjPtr<PCIDeviceAttachment> pda;
6548 char name[32];
6549
6550 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6551 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6552 pda.createObject();
6553 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6554 i_setModified(IsModified_MachineData);
6555 mHWData.backup();
6556 mHWData->mPCIDeviceAssignments.push_back(pda);
6557 }
6558
6559 return S_OK;
6560}
6561
6562/**
6563 * Currently this method doesn't detach device from the running VM,
6564 * just makes sure it's not plugged on next VM start.
6565 */
6566HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6567{
6568 ComObjPtr<PCIDeviceAttachment> pAttach;
6569 bool fRemoved = false;
6570 HRESULT rc;
6571
6572 // lock scope
6573 {
6574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6575
6576 rc = i_checkStateDependency(MutableStateDep);
6577 if (FAILED(rc)) return rc;
6578
6579 for (HWData::PCIDeviceAssignmentList::const_iterator
6580 it = mHWData->mPCIDeviceAssignments.begin();
6581 it != mHWData->mPCIDeviceAssignments.end();
6582 ++it)
6583 {
6584 LONG iHostAddress = -1;
6585 pAttach = *it;
6586 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6587 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6588 {
6589 i_setModified(IsModified_MachineData);
6590 mHWData.backup();
6591 mHWData->mPCIDeviceAssignments.remove(pAttach);
6592 fRemoved = true;
6593 break;
6594 }
6595 }
6596 }
6597
6598
6599 /* Fire event outside of the lock */
6600 if (fRemoved)
6601 {
6602 Assert(!pAttach.isNull());
6603 ComPtr<IEventSource> es;
6604 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6605 Assert(SUCCEEDED(rc));
6606 Bstr mid;
6607 rc = this->COMGETTER(Id)(mid.asOutParam());
6608 Assert(SUCCEEDED(rc));
6609 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6610 }
6611
6612 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6613 tr("No host PCI device %08x attached"),
6614 aHostAddress
6615 );
6616}
6617
6618HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6619{
6620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6621
6622 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6623 size_t i = 0;
6624 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6625 it = mHWData->mPCIDeviceAssignments.begin();
6626 it != mHWData->mPCIDeviceAssignments.end();
6627 ++it, ++i)
6628 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6629
6630 return S_OK;
6631}
6632
6633HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6634{
6635 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6636
6637 return S_OK;
6638}
6639
6640HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6641{
6642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6643
6644 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6645
6646 return S_OK;
6647}
6648
6649HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6650{
6651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6652 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6653 if (SUCCEEDED(hrc))
6654 {
6655 hrc = mHWData.backupEx();
6656 if (SUCCEEDED(hrc))
6657 {
6658 i_setModified(IsModified_MachineData);
6659 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6660 }
6661 }
6662 return hrc;
6663}
6664
6665HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6666{
6667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6668 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6669 return S_OK;
6670}
6671
6672HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6673{
6674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6675 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6676 if (SUCCEEDED(hrc))
6677 {
6678 hrc = mHWData.backupEx();
6679 if (SUCCEEDED(hrc))
6680 {
6681 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6682 if (SUCCEEDED(hrc))
6683 i_setModified(IsModified_MachineData);
6684 }
6685 }
6686 return hrc;
6687}
6688
6689HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6690{
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692
6693 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6694
6695 return S_OK;
6696}
6697
6698HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6699{
6700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6701 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6702 if (SUCCEEDED(hrc))
6703 {
6704 hrc = mHWData.backupEx();
6705 if (SUCCEEDED(hrc))
6706 {
6707 i_setModified(IsModified_MachineData);
6708 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6709 }
6710 }
6711 return hrc;
6712}
6713
6714HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6715{
6716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6719
6720 return S_OK;
6721}
6722
6723HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6724{
6725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6726
6727 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6728 if ( SUCCEEDED(hrc)
6729 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6730 {
6731 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6732 int vrc;
6733
6734 if (aAutostartEnabled)
6735 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6736 else
6737 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6738
6739 if (RT_SUCCESS(vrc))
6740 {
6741 hrc = mHWData.backupEx();
6742 if (SUCCEEDED(hrc))
6743 {
6744 i_setModified(IsModified_MachineData);
6745 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6746 }
6747 }
6748 else if (vrc == VERR_NOT_SUPPORTED)
6749 hrc = setError(VBOX_E_NOT_SUPPORTED,
6750 tr("The VM autostart feature is not supported on this platform"));
6751 else if (vrc == VERR_PATH_NOT_FOUND)
6752 hrc = setError(E_FAIL,
6753 tr("The path to the autostart database is not set"));
6754 else
6755 hrc = setError(E_UNEXPECTED,
6756 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6757 aAutostartEnabled ? "Adding" : "Removing",
6758 mUserData->s.strName.c_str(), vrc);
6759 }
6760 return hrc;
6761}
6762
6763HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6764{
6765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6766
6767 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6768
6769 return S_OK;
6770}
6771
6772HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6773{
6774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6775 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6776 if (SUCCEEDED(hrc))
6777 {
6778 hrc = mHWData.backupEx();
6779 if (SUCCEEDED(hrc))
6780 {
6781 i_setModified(IsModified_MachineData);
6782 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6783 }
6784 }
6785 return hrc;
6786}
6787
6788HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6789{
6790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6791
6792 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6793
6794 return S_OK;
6795}
6796
6797HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6798{
6799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6800 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6801 if ( SUCCEEDED(hrc)
6802 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6803 {
6804 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6805 int vrc;
6806
6807 if (aAutostopType != AutostopType_Disabled)
6808 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6809 else
6810 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6811
6812 if (RT_SUCCESS(vrc))
6813 {
6814 hrc = mHWData.backupEx();
6815 if (SUCCEEDED(hrc))
6816 {
6817 i_setModified(IsModified_MachineData);
6818 mHWData->mAutostart.enmAutostopType = aAutostopType;
6819 }
6820 }
6821 else if (vrc == VERR_NOT_SUPPORTED)
6822 hrc = setError(VBOX_E_NOT_SUPPORTED,
6823 tr("The VM autostop feature is not supported on this platform"));
6824 else if (vrc == VERR_PATH_NOT_FOUND)
6825 hrc = setError(E_FAIL,
6826 tr("The path to the autostart database is not set"));
6827 else
6828 hrc = setError(E_UNEXPECTED,
6829 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6830 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6831 mUserData->s.strName.c_str(), vrc);
6832 }
6833 return hrc;
6834}
6835
6836HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6837{
6838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6839
6840 aDefaultFrontend = mHWData->mDefaultFrontend;
6841
6842 return S_OK;
6843}
6844
6845HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6846{
6847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6848 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6849 if (SUCCEEDED(hrc))
6850 {
6851 hrc = mHWData.backupEx();
6852 if (SUCCEEDED(hrc))
6853 {
6854 i_setModified(IsModified_MachineData);
6855 mHWData->mDefaultFrontend = aDefaultFrontend;
6856 }
6857 }
6858 return hrc;
6859}
6860
6861HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6862{
6863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6864 size_t cbIcon = mUserData->s.ovIcon.size();
6865 aIcon.resize(cbIcon);
6866 if (cbIcon)
6867 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6868 return S_OK;
6869}
6870
6871HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6872{
6873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6874 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6875 if (SUCCEEDED(hrc))
6876 {
6877 i_setModified(IsModified_MachineData);
6878 mUserData.backup();
6879 size_t cbIcon = aIcon.size();
6880 mUserData->s.ovIcon.resize(cbIcon);
6881 if (cbIcon)
6882 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6883 }
6884 return hrc;
6885}
6886
6887HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6888{
6889#ifdef VBOX_WITH_USB
6890 *aUSBProxyAvailable = true;
6891#else
6892 *aUSBProxyAvailable = false;
6893#endif
6894 return S_OK;
6895}
6896
6897HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6898{
6899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6900
6901 *aVMProcessPriority = mUserData->s.enmVMPriority;
6902
6903 return S_OK;
6904}
6905
6906HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6907{
6908 RT_NOREF(aVMProcessPriority);
6909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6910 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6911 if (SUCCEEDED(hrc))
6912 {
6913 hrc = mUserData.backupEx();
6914 if (SUCCEEDED(hrc))
6915 {
6916 i_setModified(IsModified_MachineData);
6917 mUserData->s.enmVMPriority = aVMProcessPriority;
6918 }
6919 }
6920 alock.release();
6921 if (SUCCEEDED(hrc))
6922 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6923 return hrc;
6924}
6925
6926HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6927 ComPtr<IProgress> &aProgress)
6928{
6929 ComObjPtr<Progress> pP;
6930 Progress *ppP = pP;
6931 IProgress *iP = static_cast<IProgress *>(ppP);
6932 IProgress **pProgress = &iP;
6933
6934 IMachine *pTarget = aTarget;
6935
6936 /* Convert the options. */
6937 RTCList<CloneOptions_T> optList;
6938 if (aOptions.size())
6939 for (size_t i = 0; i < aOptions.size(); ++i)
6940 optList.append(aOptions[i]);
6941
6942 if (optList.contains(CloneOptions_Link))
6943 {
6944 if (!i_isSnapshotMachine())
6945 return setError(E_INVALIDARG,
6946 tr("Linked clone can only be created from a snapshot"));
6947 if (aMode != CloneMode_MachineState)
6948 return setError(E_INVALIDARG,
6949 tr("Linked clone can only be created for a single machine state"));
6950 }
6951 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6952
6953 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6954
6955 HRESULT rc = pWorker->start(pProgress);
6956
6957 pP = static_cast<Progress *>(*pProgress);
6958 pP.queryInterfaceTo(aProgress.asOutParam());
6959
6960 return rc;
6961
6962}
6963
6964HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6965 const com::Utf8Str &aType,
6966 ComPtr<IProgress> &aProgress)
6967{
6968 LogFlowThisFuncEnter();
6969
6970 ComObjPtr<Progress> ptrProgress;
6971 HRESULT hrc = ptrProgress.createObject();
6972 if (SUCCEEDED(hrc))
6973 {
6974 /* Initialize our worker task */
6975 MachineMoveVM *pTask = NULL;
6976 try
6977 {
6978 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6979 }
6980 catch (std::bad_alloc &)
6981 {
6982 return E_OUTOFMEMORY;
6983 }
6984
6985 hrc = pTask->init();//no exceptions are thrown
6986
6987 if (SUCCEEDED(hrc))
6988 {
6989 hrc = pTask->createThread();
6990 pTask = NULL; /* Consumed by createThread(). */
6991 if (SUCCEEDED(hrc))
6992 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6993 else
6994 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6995 }
6996 else
6997 delete pTask;
6998 }
6999
7000 LogFlowThisFuncLeave();
7001 return hrc;
7002
7003}
7004
7005HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7006{
7007 NOREF(aProgress);
7008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7009
7010 // This check should always fail.
7011 HRESULT rc = i_checkStateDependency(MutableStateDep);
7012 if (FAILED(rc)) return rc;
7013
7014 AssertFailedReturn(E_NOTIMPL);
7015}
7016
7017HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7018{
7019 NOREF(aSavedStateFile);
7020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7021
7022 // This check should always fail.
7023 HRESULT rc = i_checkStateDependency(MutableStateDep);
7024 if (FAILED(rc)) return rc;
7025
7026 AssertFailedReturn(E_NOTIMPL);
7027}
7028
7029HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7030{
7031 NOREF(aFRemoveFile);
7032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7033
7034 // This check should always fail.
7035 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7036 if (FAILED(rc)) return rc;
7037
7038 AssertFailedReturn(E_NOTIMPL);
7039}
7040
7041// public methods for internal purposes
7042/////////////////////////////////////////////////////////////////////////////
7043
7044/**
7045 * Adds the given IsModified_* flag to the dirty flags of the machine.
7046 * This must be called either during i_loadSettings or under the machine write lock.
7047 * @param fl Flag
7048 * @param fAllowStateModification If state modifications are allowed.
7049 */
7050void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7051{
7052 mData->flModifications |= fl;
7053 if (fAllowStateModification && i_isStateModificationAllowed())
7054 mData->mCurrentStateModified = true;
7055}
7056
7057/**
7058 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7059 * care of the write locking.
7060 *
7061 * @param fModification The flag to add.
7062 * @param fAllowStateModification If state modifications are allowed.
7063 */
7064void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7065{
7066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7067 i_setModified(fModification, fAllowStateModification);
7068}
7069
7070/**
7071 * Saves the registry entry of this machine to the given configuration node.
7072 *
7073 * @param data Machine registry data.
7074 *
7075 * @note locks this object for reading.
7076 */
7077HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7078{
7079 AutoLimitedCaller autoCaller(this);
7080 AssertComRCReturnRC(autoCaller.rc());
7081
7082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7083
7084 data.uuid = mData->mUuid;
7085 data.strSettingsFile = mData->m_strConfigFile;
7086
7087 return S_OK;
7088}
7089
7090/**
7091 * Calculates the absolute path of the given path taking the directory of the
7092 * machine settings file as the current directory.
7093 *
7094 * @param strPath Path to calculate the absolute path for.
7095 * @param aResult Where to put the result (used only on success, can be the
7096 * same Utf8Str instance as passed in @a aPath).
7097 * @return IPRT result.
7098 *
7099 * @note Locks this object for reading.
7100 */
7101int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7102{
7103 AutoCaller autoCaller(this);
7104 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7105
7106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7107
7108 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7109
7110 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7111
7112 strSettingsDir.stripFilename();
7113 char szFolder[RTPATH_MAX];
7114 size_t cbFolder = sizeof(szFolder);
7115 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7116 if (RT_SUCCESS(vrc))
7117 aResult = szFolder;
7118
7119 return vrc;
7120}
7121
7122/**
7123 * Copies strSource to strTarget, making it relative to the machine folder
7124 * if it is a subdirectory thereof, or simply copying it otherwise.
7125 *
7126 * @param strSource Path to evaluate and copy.
7127 * @param strTarget Buffer to receive target path.
7128 *
7129 * @note Locks this object for reading.
7130 */
7131void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7132 Utf8Str &strTarget)
7133{
7134 AutoCaller autoCaller(this);
7135 AssertComRCReturn(autoCaller.rc(), (void)0);
7136
7137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7138
7139 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7140 // use strTarget as a temporary buffer to hold the machine settings dir
7141 strTarget = mData->m_strConfigFileFull;
7142 strTarget.stripFilename();
7143 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7144 {
7145 // is relative: then append what's left
7146 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7147 // for empty paths (only possible for subdirs) use "." to avoid
7148 // triggering default settings for not present config attributes.
7149 if (strTarget.isEmpty())
7150 strTarget = ".";
7151 }
7152 else
7153 // is not relative: then overwrite
7154 strTarget = strSource;
7155}
7156
7157/**
7158 * Returns the full path to the machine's log folder in the
7159 * \a aLogFolder argument.
7160 */
7161void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturnVoid(autoCaller.rc());
7165
7166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7167
7168 char szTmp[RTPATH_MAX];
7169 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7170 if (RT_SUCCESS(vrc))
7171 {
7172 if (szTmp[0] && !mUserData.isNull())
7173 {
7174 char szTmp2[RTPATH_MAX];
7175 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7176 if (RT_SUCCESS(vrc))
7177 aLogFolder.printf("%s%c%s",
7178 szTmp2,
7179 RTPATH_DELIMITER,
7180 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7181 }
7182 else
7183 vrc = VERR_PATH_IS_RELATIVE;
7184 }
7185
7186 if (RT_FAILURE(vrc))
7187 {
7188 // fallback if VBOX_USER_LOGHOME is not set or invalid
7189 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7190 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7191 aLogFolder.append(RTPATH_DELIMITER);
7192 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7193 }
7194}
7195
7196/**
7197 * Returns the full path to the machine's log file for an given index.
7198 */
7199Utf8Str Machine::i_getLogFilename(ULONG idx)
7200{
7201 Utf8Str logFolder;
7202 getLogFolder(logFolder);
7203 Assert(logFolder.length());
7204
7205 Utf8Str log;
7206 if (idx == 0)
7207 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7208#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7209 else if (idx == 1)
7210 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7211 else
7212 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7213#else
7214 else
7215 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7216#endif
7217 return log;
7218}
7219
7220/**
7221 * Returns the full path to the machine's hardened log file.
7222 */
7223Utf8Str Machine::i_getHardeningLogFilename(void)
7224{
7225 Utf8Str strFilename;
7226 getLogFolder(strFilename);
7227 Assert(strFilename.length());
7228 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7229 return strFilename;
7230}
7231
7232/**
7233 * Returns the default NVRAM filename based on the location of the VM config.
7234 * Note that this is a relative path.
7235 */
7236Utf8Str Machine::i_getDefaultNVRAMFilename()
7237{
7238 AutoCaller autoCaller(this);
7239 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7240
7241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7242
7243 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7244 || i_isSnapshotMachine())
7245 return Utf8Str::Empty;
7246
7247 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7248 strNVRAMFilePath.stripPath();
7249 strNVRAMFilePath.stripSuffix();
7250 strNVRAMFilePath += ".nvram";
7251
7252 return strNVRAMFilePath;
7253}
7254
7255/**
7256 * Returns the NVRAM filename for a new snapshot. This intentionally works
7257 * similarly to the saved state file naming. Note that this is usually
7258 * a relative path, unless the snapshot folder is absolute.
7259 */
7260Utf8Str Machine::i_getSnapshotNVRAMFilename()
7261{
7262 AutoCaller autoCaller(this);
7263 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7264
7265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7266
7267 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7268 return Utf8Str::Empty;
7269
7270 RTTIMESPEC ts;
7271 RTTimeNow(&ts);
7272 RTTIME time;
7273 RTTimeExplode(&time, &ts);
7274
7275 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7276 strNVRAMFilePath += RTPATH_DELIMITER;
7277 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7278 time.i32Year, time.u8Month, time.u8MonthDay,
7279 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7280
7281 return strNVRAMFilePath;
7282}
7283
7284/**
7285 * Composes a unique saved state filename based on the current system time. The filename is
7286 * granular to the second so this will work so long as no more than one snapshot is taken on
7287 * a machine per second.
7288 *
7289 * Before version 4.1, we used this formula for saved state files:
7290 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7291 * which no longer works because saved state files can now be shared between the saved state of the
7292 * "saved" machine and an online snapshot, and the following would cause problems:
7293 * 1) save machine
7294 * 2) create online snapshot from that machine state --> reusing saved state file
7295 * 3) save machine again --> filename would be reused, breaking the online snapshot
7296 *
7297 * So instead we now use a timestamp.
7298 *
7299 * @param strStateFilePath
7300 */
7301
7302void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7303{
7304 AutoCaller autoCaller(this);
7305 AssertComRCReturnVoid(autoCaller.rc());
7306
7307 {
7308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7309 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7310 }
7311
7312 RTTIMESPEC ts;
7313 RTTimeNow(&ts);
7314 RTTIME time;
7315 RTTimeExplode(&time, &ts);
7316
7317 strStateFilePath += RTPATH_DELIMITER;
7318 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7319 time.i32Year, time.u8Month, time.u8MonthDay,
7320 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7321}
7322
7323/**
7324 * Returns whether at least one USB controller is present for the VM.
7325 */
7326bool Machine::i_isUSBControllerPresent()
7327{
7328 AutoCaller autoCaller(this);
7329 AssertComRCReturn(autoCaller.rc(), false);
7330
7331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7332
7333 return (mUSBControllers->size() > 0);
7334}
7335
7336
7337/**
7338 * @note Locks this object for writing, calls the client process
7339 * (inside the lock).
7340 */
7341HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7342 const Utf8Str &strFrontend,
7343 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7344 ProgressProxy *aProgress)
7345{
7346 LogFlowThisFuncEnter();
7347
7348 AssertReturn(aControl, E_FAIL);
7349 AssertReturn(aProgress, E_FAIL);
7350 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7351
7352 AutoCaller autoCaller(this);
7353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7354
7355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7356
7357 if (!mData->mRegistered)
7358 return setError(E_UNEXPECTED,
7359 tr("The machine '%s' is not registered"),
7360 mUserData->s.strName.c_str());
7361
7362 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7363
7364 /* The process started when launching a VM with separate UI/VM processes is always
7365 * the UI process, i.e. needs special handling as it won't claim the session. */
7366 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7367
7368 if (fSeparate)
7369 {
7370 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7371 return setError(VBOX_E_INVALID_OBJECT_STATE,
7372 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7373 mUserData->s.strName.c_str());
7374 }
7375 else
7376 {
7377 if ( mData->mSession.mState == SessionState_Locked
7378 || mData->mSession.mState == SessionState_Spawning
7379 || mData->mSession.mState == SessionState_Unlocking)
7380 return setError(VBOX_E_INVALID_OBJECT_STATE,
7381 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7382 mUserData->s.strName.c_str());
7383
7384 /* may not be busy */
7385 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7386 }
7387
7388 /* Hardening logging */
7389#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7390 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7391 {
7392 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7393 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7394 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7395 {
7396 Utf8Str strStartupLogDir = strHardeningLogFile;
7397 strStartupLogDir.stripFilename();
7398 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7399 file without stripping the file. */
7400 }
7401 strSupHardeningLogArg.append(strHardeningLogFile);
7402
7403 /* Remove legacy log filename to avoid confusion. */
7404 Utf8Str strOldStartupLogFile;
7405 getLogFolder(strOldStartupLogFile);
7406 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7407 RTFileDelete(strOldStartupLogFile.c_str());
7408 }
7409#else
7410 Utf8Str strSupHardeningLogArg;
7411#endif
7412
7413 Utf8Str strAppOverride;
7414#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7415 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7416#endif
7417
7418 bool fUseVBoxSDS = false;
7419 Utf8Str strCanonicalName;
7420 if (false)
7421 { }
7422#ifdef VBOX_WITH_QTGUI
7423 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7424 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7425 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7426 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7427 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7428 {
7429 strCanonicalName = "GUI/Qt";
7430 fUseVBoxSDS = true;
7431 }
7432#endif
7433#ifdef VBOX_WITH_VBOXSDL
7434 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7435 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7436 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7437 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7438 {
7439 strCanonicalName = "GUI/SDL";
7440 fUseVBoxSDS = true;
7441 }
7442#endif
7443#ifdef VBOX_WITH_HEADLESS
7444 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7445 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7446 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7447 {
7448 strCanonicalName = "headless";
7449 }
7450#endif
7451 else
7452 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7453
7454 Utf8Str idStr = mData->mUuid.toString();
7455 Utf8Str const &strMachineName = mUserData->s.strName;
7456 RTPROCESS pid = NIL_RTPROCESS;
7457
7458#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7459 RT_NOREF(fUseVBoxSDS);
7460#else
7461 DWORD idCallerSession = ~(DWORD)0;
7462 if (fUseVBoxSDS)
7463 {
7464 /*
7465 * The VBoxSDS should be used for process launching the VM with
7466 * GUI only if the caller and the VBoxSDS are in different Windows
7467 * sessions and the caller in the interactive one.
7468 */
7469 fUseVBoxSDS = false;
7470
7471 /* Get windows session of the current process. The process token used
7472 due to several reasons:
7473 1. The token is absent for the current thread except someone set it
7474 for us.
7475 2. Needs to get the id of the session where the process is started.
7476 We only need to do this once, though. */
7477 static DWORD s_idCurrentSession = ~(DWORD)0;
7478 DWORD idCurrentSession = s_idCurrentSession;
7479 if (idCurrentSession == ~(DWORD)0)
7480 {
7481 HANDLE hCurrentProcessToken = NULL;
7482 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7483 {
7484 DWORD cbIgn = 0;
7485 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7486 s_idCurrentSession = idCurrentSession;
7487 else
7488 {
7489 idCurrentSession = ~(DWORD)0;
7490 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7491 }
7492 CloseHandle(hCurrentProcessToken);
7493 }
7494 else
7495 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7496 }
7497
7498 /* get the caller's session */
7499 HRESULT hrc = CoImpersonateClient();
7500 if (SUCCEEDED(hrc))
7501 {
7502 HANDLE hCallerThreadToken;
7503 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7504 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7505 &hCallerThreadToken))
7506 {
7507 SetLastError(NO_ERROR);
7508 DWORD cbIgn = 0;
7509 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7510 {
7511 /* Only need to use SDS if the session ID differs: */
7512 if (idCurrentSession != idCallerSession)
7513 {
7514 fUseVBoxSDS = false;
7515
7516 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7517 DWORD cbTokenGroups = 0;
7518 PTOKEN_GROUPS pTokenGroups = NULL;
7519 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7520 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7521 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7522 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7523 {
7524 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7525 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7526 PSID pInteractiveSid = NULL;
7527 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7528 {
7529 /* Iterate over the groups looking for the interactive SID: */
7530 fUseVBoxSDS = false;
7531 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7532 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7533 {
7534 fUseVBoxSDS = true;
7535 break;
7536 }
7537 FreeSid(pInteractiveSid);
7538 }
7539 }
7540 else
7541 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7542 RTMemTmpFree(pTokenGroups);
7543 }
7544 }
7545 else
7546 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7547 CloseHandle(hCallerThreadToken);
7548 }
7549 else
7550 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7551 CoRevertToSelf();
7552 }
7553 else
7554 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7555 }
7556 if (fUseVBoxSDS)
7557 {
7558 /* connect to VBoxSDS */
7559 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7560 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7561 if (FAILED(rc))
7562 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7563 strMachineName.c_str());
7564
7565 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7566 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7567 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7568 service to access the files. */
7569 rc = CoSetProxyBlanket(pVBoxSDS,
7570 RPC_C_AUTHN_DEFAULT,
7571 RPC_C_AUTHZ_DEFAULT,
7572 COLE_DEFAULT_PRINCIPAL,
7573 RPC_C_AUTHN_LEVEL_DEFAULT,
7574 RPC_C_IMP_LEVEL_IMPERSONATE,
7575 NULL,
7576 EOAC_DEFAULT);
7577 if (FAILED(rc))
7578 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7579
7580 size_t const cEnvVars = aEnvironmentChanges.size();
7581 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7582 for (size_t i = 0; i < cEnvVars; i++)
7583 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7584
7585 ULONG uPid = 0;
7586 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7587 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7588 idCallerSession, &uPid);
7589 if (FAILED(rc))
7590 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7591 pid = (RTPROCESS)uPid;
7592 }
7593 else
7594#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7595 {
7596 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7597 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7598 if (RT_FAILURE(vrc))
7599 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7600 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7601 }
7602
7603 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7604 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7605
7606 if (!fSeparate)
7607 {
7608 /*
7609 * Note that we don't release the lock here before calling the client,
7610 * because it doesn't need to call us back if called with a NULL argument.
7611 * Releasing the lock here is dangerous because we didn't prepare the
7612 * launch data yet, but the client we've just started may happen to be
7613 * too fast and call LockMachine() that will fail (because of PID, etc.),
7614 * so that the Machine will never get out of the Spawning session state.
7615 */
7616
7617 /* inform the session that it will be a remote one */
7618 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7619#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7620 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7621#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7622 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7623#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7624 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7625
7626 if (FAILED(rc))
7627 {
7628 /* restore the session state */
7629 mData->mSession.mState = SessionState_Unlocked;
7630 alock.release();
7631 mParent->i_addProcessToReap(pid);
7632 /* The failure may occur w/o any error info (from RPC), so provide one */
7633 return setError(VBOX_E_VM_ERROR,
7634 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7635 }
7636
7637 /* attach launch data to the machine */
7638 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7639 mData->mSession.mRemoteControls.push_back(aControl);
7640 mData->mSession.mProgress = aProgress;
7641 mData->mSession.mPID = pid;
7642 mData->mSession.mState = SessionState_Spawning;
7643 Assert(strCanonicalName.isNotEmpty());
7644 mData->mSession.mName = strCanonicalName;
7645 }
7646 else
7647 {
7648 /* For separate UI process we declare the launch as completed instantly, as the
7649 * actual headless VM start may or may not come. No point in remembering anything
7650 * yet, as what matters for us is when the headless VM gets started. */
7651 aProgress->i_notifyComplete(S_OK);
7652 }
7653
7654 alock.release();
7655 mParent->i_addProcessToReap(pid);
7656
7657 LogFlowThisFuncLeave();
7658 return S_OK;
7659}
7660
7661/**
7662 * Returns @c true if the given session machine instance has an open direct
7663 * session (and optionally also for direct sessions which are closing) and
7664 * returns the session control machine instance if so.
7665 *
7666 * Note that when the method returns @c false, the arguments remain unchanged.
7667 *
7668 * @param aMachine Session machine object.
7669 * @param aControl Direct session control object (optional).
7670 * @param aRequireVM If true then only allow VM sessions.
7671 * @param aAllowClosing If true then additionally a session which is currently
7672 * being closed will also be allowed.
7673 *
7674 * @note locks this object for reading.
7675 */
7676bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7677 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7678 bool aRequireVM /*= false*/,
7679 bool aAllowClosing /*= false*/)
7680{
7681 AutoLimitedCaller autoCaller(this);
7682 AssertComRCReturn(autoCaller.rc(), false);
7683
7684 /* just return false for inaccessible machines */
7685 if (getObjectState().getState() != ObjectState::Ready)
7686 return false;
7687
7688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7689
7690 if ( ( mData->mSession.mState == SessionState_Locked
7691 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7692 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7693 )
7694 {
7695 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7696
7697 aMachine = mData->mSession.mMachine;
7698
7699 if (aControl != NULL)
7700 *aControl = mData->mSession.mDirectControl;
7701
7702 return true;
7703 }
7704
7705 return false;
7706}
7707
7708/**
7709 * Returns @c true if the given machine has an spawning direct session.
7710 *
7711 * @note locks this object for reading.
7712 */
7713bool Machine::i_isSessionSpawning()
7714{
7715 AutoLimitedCaller autoCaller(this);
7716 AssertComRCReturn(autoCaller.rc(), false);
7717
7718 /* just return false for inaccessible machines */
7719 if (getObjectState().getState() != ObjectState::Ready)
7720 return false;
7721
7722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7723
7724 if (mData->mSession.mState == SessionState_Spawning)
7725 return true;
7726
7727 return false;
7728}
7729
7730/**
7731 * Called from the client watcher thread to check for unexpected client process
7732 * death during Session_Spawning state (e.g. before it successfully opened a
7733 * direct session).
7734 *
7735 * On Win32 and on OS/2, this method is called only when we've got the
7736 * direct client's process termination notification, so it always returns @c
7737 * true.
7738 *
7739 * On other platforms, this method returns @c true if the client process is
7740 * terminated and @c false if it's still alive.
7741 *
7742 * @note Locks this object for writing.
7743 */
7744bool Machine::i_checkForSpawnFailure()
7745{
7746 AutoCaller autoCaller(this);
7747 if (!autoCaller.isOk())
7748 {
7749 /* nothing to do */
7750 LogFlowThisFunc(("Already uninitialized!\n"));
7751 return true;
7752 }
7753
7754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7755
7756 if (mData->mSession.mState != SessionState_Spawning)
7757 {
7758 /* nothing to do */
7759 LogFlowThisFunc(("Not spawning any more!\n"));
7760 return true;
7761 }
7762
7763 HRESULT rc = S_OK;
7764
7765 /* PID not yet initialized, skip check. */
7766 if (mData->mSession.mPID == NIL_RTPROCESS)
7767 return false;
7768
7769 RTPROCSTATUS status;
7770 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7771
7772 if (vrc != VERR_PROCESS_RUNNING)
7773 {
7774 Utf8Str strExtraInfo;
7775
7776#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7777 /* If the startup logfile exists and is of non-zero length, tell the
7778 user to look there for more details to encourage them to attach it
7779 when reporting startup issues. */
7780 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7781 uint64_t cbStartupLogFile = 0;
7782 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7783 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7784 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7785#endif
7786
7787 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7788 rc = setError(E_FAIL,
7789 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7790 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7791 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7792 rc = setError(E_FAIL,
7793 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7794 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7795 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7796 rc = setError(E_FAIL,
7797 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7798 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7799 else
7800 rc = setErrorBoth(E_FAIL, vrc,
7801 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7802 i_getName().c_str(), vrc, strExtraInfo.c_str());
7803 }
7804
7805 if (FAILED(rc))
7806 {
7807 /* Close the remote session, remove the remote control from the list
7808 * and reset session state to Closed (@note keep the code in sync with
7809 * the relevant part in LockMachine()). */
7810
7811 Assert(mData->mSession.mRemoteControls.size() == 1);
7812 if (mData->mSession.mRemoteControls.size() == 1)
7813 {
7814 ErrorInfoKeeper eik;
7815 mData->mSession.mRemoteControls.front()->Uninitialize();
7816 }
7817
7818 mData->mSession.mRemoteControls.clear();
7819 mData->mSession.mState = SessionState_Unlocked;
7820
7821 /* finalize the progress after setting the state */
7822 if (!mData->mSession.mProgress.isNull())
7823 {
7824 mData->mSession.mProgress->notifyComplete(rc);
7825 mData->mSession.mProgress.setNull();
7826 }
7827
7828 mData->mSession.mPID = NIL_RTPROCESS;
7829
7830 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7831 return true;
7832 }
7833
7834 return false;
7835}
7836
7837/**
7838 * Checks whether the machine can be registered. If so, commits and saves
7839 * all settings.
7840 *
7841 * @note Must be called from mParent's write lock. Locks this object and
7842 * children for writing.
7843 */
7844HRESULT Machine::i_prepareRegister()
7845{
7846 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7847
7848 AutoLimitedCaller autoCaller(this);
7849 AssertComRCReturnRC(autoCaller.rc());
7850
7851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7852
7853 /* wait for state dependents to drop to zero */
7854 i_ensureNoStateDependencies();
7855
7856 if (!mData->mAccessible)
7857 return setError(VBOX_E_INVALID_OBJECT_STATE,
7858 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7859 mUserData->s.strName.c_str(),
7860 mData->mUuid.toString().c_str());
7861
7862 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7863
7864 if (mData->mRegistered)
7865 return setError(VBOX_E_INVALID_OBJECT_STATE,
7866 tr("The machine '%s' with UUID {%s} is already registered"),
7867 mUserData->s.strName.c_str(),
7868 mData->mUuid.toString().c_str());
7869
7870 HRESULT rc = S_OK;
7871
7872 // Ensure the settings are saved. If we are going to be registered and
7873 // no config file exists yet, create it by calling i_saveSettings() too.
7874 if ( (mData->flModifications)
7875 || (!mData->pMachineConfigFile->fileExists())
7876 )
7877 {
7878 rc = i_saveSettings(NULL);
7879 // no need to check whether VirtualBox.xml needs saving too since
7880 // we can't have a machine XML file rename pending
7881 if (FAILED(rc)) return rc;
7882 }
7883
7884 /* more config checking goes here */
7885
7886 if (SUCCEEDED(rc))
7887 {
7888 /* we may have had implicit modifications we want to fix on success */
7889 i_commit();
7890
7891 mData->mRegistered = true;
7892 }
7893 else
7894 {
7895 /* we may have had implicit modifications we want to cancel on failure*/
7896 i_rollback(false /* aNotify */);
7897 }
7898
7899 return rc;
7900}
7901
7902/**
7903 * Increases the number of objects dependent on the machine state or on the
7904 * registered state. Guarantees that these two states will not change at least
7905 * until #i_releaseStateDependency() is called.
7906 *
7907 * Depending on the @a aDepType value, additional state checks may be made.
7908 * These checks will set extended error info on failure. See
7909 * #i_checkStateDependency() for more info.
7910 *
7911 * If this method returns a failure, the dependency is not added and the caller
7912 * is not allowed to rely on any particular machine state or registration state
7913 * value and may return the failed result code to the upper level.
7914 *
7915 * @param aDepType Dependency type to add.
7916 * @param aState Current machine state (NULL if not interested).
7917 * @param aRegistered Current registered state (NULL if not interested).
7918 *
7919 * @note Locks this object for writing.
7920 */
7921HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7922 MachineState_T *aState /* = NULL */,
7923 BOOL *aRegistered /* = NULL */)
7924{
7925 AutoCaller autoCaller(this);
7926 AssertComRCReturnRC(autoCaller.rc());
7927
7928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7929
7930 HRESULT rc = i_checkStateDependency(aDepType);
7931 if (FAILED(rc)) return rc;
7932
7933 {
7934 if (mData->mMachineStateChangePending != 0)
7935 {
7936 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7937 * drop to zero so don't add more. It may make sense to wait a bit
7938 * and retry before reporting an error (since the pending state
7939 * transition should be really quick) but let's just assert for
7940 * now to see if it ever happens on practice. */
7941
7942 AssertFailed();
7943
7944 return setError(E_ACCESSDENIED,
7945 tr("Machine state change is in progress. Please retry the operation later."));
7946 }
7947
7948 ++mData->mMachineStateDeps;
7949 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7950 }
7951
7952 if (aState)
7953 *aState = mData->mMachineState;
7954 if (aRegistered)
7955 *aRegistered = mData->mRegistered;
7956
7957 return S_OK;
7958}
7959
7960/**
7961 * Decreases the number of objects dependent on the machine state.
7962 * Must always complete the #i_addStateDependency() call after the state
7963 * dependency is no more necessary.
7964 */
7965void Machine::i_releaseStateDependency()
7966{
7967 AutoCaller autoCaller(this);
7968 AssertComRCReturnVoid(autoCaller.rc());
7969
7970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7971
7972 /* releaseStateDependency() w/o addStateDependency()? */
7973 AssertReturnVoid(mData->mMachineStateDeps != 0);
7974 -- mData->mMachineStateDeps;
7975
7976 if (mData->mMachineStateDeps == 0)
7977 {
7978 /* inform i_ensureNoStateDependencies() that there are no more deps */
7979 if (mData->mMachineStateChangePending != 0)
7980 {
7981 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7982 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7983 }
7984 }
7985}
7986
7987Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7988{
7989 /* start with nothing found */
7990 Utf8Str strResult("");
7991
7992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7993
7994 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7995 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7996 // found:
7997 strResult = it->second; // source is a Utf8Str
7998
7999 return strResult;
8000}
8001
8002// protected methods
8003/////////////////////////////////////////////////////////////////////////////
8004
8005/**
8006 * Performs machine state checks based on the @a aDepType value. If a check
8007 * fails, this method will set extended error info, otherwise it will return
8008 * S_OK. It is supposed, that on failure, the caller will immediately return
8009 * the return value of this method to the upper level.
8010 *
8011 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8012 *
8013 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8014 * current state of this machine object allows to change settings of the
8015 * machine (i.e. the machine is not registered, or registered but not running
8016 * and not saved). It is useful to call this method from Machine setters
8017 * before performing any change.
8018 *
8019 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8020 * as for MutableStateDep except that if the machine is saved, S_OK is also
8021 * returned. This is useful in setters which allow changing machine
8022 * properties when it is in the saved state.
8023 *
8024 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8025 * if the current state of this machine object allows to change runtime
8026 * changeable settings of the machine (i.e. the machine is not registered, or
8027 * registered but either running or not running and not saved). It is useful
8028 * to call this method from Machine setters before performing any changes to
8029 * runtime changeable settings.
8030 *
8031 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8032 * the same as for MutableOrRunningStateDep except that if the machine is
8033 * saved, S_OK is also returned. This is useful in setters which allow
8034 * changing runtime and saved state changeable machine properties.
8035 *
8036 * @param aDepType Dependency type to check.
8037 *
8038 * @note Non Machine based classes should use #i_addStateDependency() and
8039 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8040 * template.
8041 *
8042 * @note This method must be called from under this object's read or write
8043 * lock.
8044 */
8045HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8046{
8047 switch (aDepType)
8048 {
8049 case AnyStateDep:
8050 {
8051 break;
8052 }
8053 case MutableStateDep:
8054 {
8055 if ( mData->mRegistered
8056 && ( !i_isSessionMachine()
8057 || ( mData->mMachineState != MachineState_Aborted
8058 && mData->mMachineState != MachineState_Teleported
8059 && mData->mMachineState != MachineState_PoweredOff
8060 )
8061 )
8062 )
8063 return setError(VBOX_E_INVALID_VM_STATE,
8064 tr("The machine is not mutable (state is %s)"),
8065 Global::stringifyMachineState(mData->mMachineState));
8066 break;
8067 }
8068 case MutableOrSavedStateDep:
8069 {
8070 if ( mData->mRegistered
8071 && ( !i_isSessionMachine()
8072 || ( mData->mMachineState != MachineState_Aborted
8073 && mData->mMachineState != MachineState_Teleported
8074 && mData->mMachineState != MachineState_Saved
8075 && mData->mMachineState != MachineState_PoweredOff
8076 )
8077 )
8078 )
8079 return setError(VBOX_E_INVALID_VM_STATE,
8080 tr("The machine is not mutable or saved (state is %s)"),
8081 Global::stringifyMachineState(mData->mMachineState));
8082 break;
8083 }
8084 case MutableOrRunningStateDep:
8085 {
8086 if ( mData->mRegistered
8087 && ( !i_isSessionMachine()
8088 || ( mData->mMachineState != MachineState_Aborted
8089 && mData->mMachineState != MachineState_Teleported
8090 && mData->mMachineState != MachineState_PoweredOff
8091 && !Global::IsOnline(mData->mMachineState)
8092 )
8093 )
8094 )
8095 return setError(VBOX_E_INVALID_VM_STATE,
8096 tr("The machine is not mutable or running (state is %s)"),
8097 Global::stringifyMachineState(mData->mMachineState));
8098 break;
8099 }
8100 case MutableOrSavedOrRunningStateDep:
8101 {
8102 if ( mData->mRegistered
8103 && ( !i_isSessionMachine()
8104 || ( mData->mMachineState != MachineState_Aborted
8105 && mData->mMachineState != MachineState_Teleported
8106 && mData->mMachineState != MachineState_Saved
8107 && mData->mMachineState != MachineState_PoweredOff
8108 && !Global::IsOnline(mData->mMachineState)
8109 )
8110 )
8111 )
8112 return setError(VBOX_E_INVALID_VM_STATE,
8113 tr("The machine is not mutable, saved or running (state is %s)"),
8114 Global::stringifyMachineState(mData->mMachineState));
8115 break;
8116 }
8117 }
8118
8119 return S_OK;
8120}
8121
8122/**
8123 * Helper to initialize all associated child objects and allocate data
8124 * structures.
8125 *
8126 * This method must be called as a part of the object's initialization procedure
8127 * (usually done in the #init() method).
8128 *
8129 * @note Must be called only from #init() or from #i_registeredInit().
8130 */
8131HRESULT Machine::initDataAndChildObjects()
8132{
8133 AutoCaller autoCaller(this);
8134 AssertComRCReturnRC(autoCaller.rc());
8135 AssertReturn( getObjectState().getState() == ObjectState::InInit
8136 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8137
8138 AssertReturn(!mData->mAccessible, E_FAIL);
8139
8140 /* allocate data structures */
8141 mSSData.allocate();
8142 mUserData.allocate();
8143 mHWData.allocate();
8144 mMediumAttachments.allocate();
8145 mStorageControllers.allocate();
8146 mUSBControllers.allocate();
8147
8148 /* initialize mOSTypeId */
8149 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8150
8151/** @todo r=bird: init() methods never fails, right? Why don't we make them
8152 * return void then! */
8153
8154 /* create associated BIOS settings object */
8155 unconst(mBIOSSettings).createObject();
8156 mBIOSSettings->init(this);
8157
8158 /* create associated record settings object */
8159 unconst(mRecordingSettings).createObject();
8160 mRecordingSettings->init(this);
8161
8162 /* create the graphics adapter object (always present) */
8163 unconst(mGraphicsAdapter).createObject();
8164 mGraphicsAdapter->init(this);
8165
8166 /* create an associated VRDE object (default is disabled) */
8167 unconst(mVRDEServer).createObject();
8168 mVRDEServer->init(this);
8169
8170 /* create associated serial port objects */
8171 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8172 {
8173 unconst(mSerialPorts[slot]).createObject();
8174 mSerialPorts[slot]->init(this, slot);
8175 }
8176
8177 /* create associated parallel port objects */
8178 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8179 {
8180 unconst(mParallelPorts[slot]).createObject();
8181 mParallelPorts[slot]->init(this, slot);
8182 }
8183
8184 /* create the audio adapter object (always present, default is disabled) */
8185 unconst(mAudioAdapter).createObject();
8186 mAudioAdapter->init(this);
8187
8188 /* create the USB device filters object (always present) */
8189 unconst(mUSBDeviceFilters).createObject();
8190 mUSBDeviceFilters->init(this);
8191
8192 /* create associated network adapter objects */
8193 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8194 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8195 {
8196 unconst(mNetworkAdapters[slot]).createObject();
8197 mNetworkAdapters[slot]->init(this, slot);
8198 }
8199
8200 /* create the bandwidth control */
8201 unconst(mBandwidthControl).createObject();
8202 mBandwidthControl->init(this);
8203
8204 return S_OK;
8205}
8206
8207/**
8208 * Helper to uninitialize all associated child objects and to free all data
8209 * structures.
8210 *
8211 * This method must be called as a part of the object's uninitialization
8212 * procedure (usually done in the #uninit() method).
8213 *
8214 * @note Must be called only from #uninit() or from #i_registeredInit().
8215 */
8216void Machine::uninitDataAndChildObjects()
8217{
8218 AutoCaller autoCaller(this);
8219 AssertComRCReturnVoid(autoCaller.rc());
8220 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8221 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8222 || getObjectState().getState() == ObjectState::InUninit
8223 || getObjectState().getState() == ObjectState::Limited);
8224
8225 /* tell all our other child objects we've been uninitialized */
8226 if (mBandwidthControl)
8227 {
8228 mBandwidthControl->uninit();
8229 unconst(mBandwidthControl).setNull();
8230 }
8231
8232 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8233 {
8234 if (mNetworkAdapters[slot])
8235 {
8236 mNetworkAdapters[slot]->uninit();
8237 unconst(mNetworkAdapters[slot]).setNull();
8238 }
8239 }
8240
8241 if (mUSBDeviceFilters)
8242 {
8243 mUSBDeviceFilters->uninit();
8244 unconst(mUSBDeviceFilters).setNull();
8245 }
8246
8247 if (mAudioAdapter)
8248 {
8249 mAudioAdapter->uninit();
8250 unconst(mAudioAdapter).setNull();
8251 }
8252
8253 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8254 {
8255 if (mParallelPorts[slot])
8256 {
8257 mParallelPorts[slot]->uninit();
8258 unconst(mParallelPorts[slot]).setNull();
8259 }
8260 }
8261
8262 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8263 {
8264 if (mSerialPorts[slot])
8265 {
8266 mSerialPorts[slot]->uninit();
8267 unconst(mSerialPorts[slot]).setNull();
8268 }
8269 }
8270
8271 if (mVRDEServer)
8272 {
8273 mVRDEServer->uninit();
8274 unconst(mVRDEServer).setNull();
8275 }
8276
8277 if (mGraphicsAdapter)
8278 {
8279 mGraphicsAdapter->uninit();
8280 unconst(mGraphicsAdapter).setNull();
8281 }
8282
8283 if (mBIOSSettings)
8284 {
8285 mBIOSSettings->uninit();
8286 unconst(mBIOSSettings).setNull();
8287 }
8288
8289 if (mRecordingSettings)
8290 {
8291 mRecordingSettings->uninit();
8292 unconst(mRecordingSettings).setNull();
8293 }
8294
8295 /* Deassociate media (only when a real Machine or a SnapshotMachine
8296 * instance is uninitialized; SessionMachine instances refer to real
8297 * Machine media). This is necessary for a clean re-initialization of
8298 * the VM after successfully re-checking the accessibility state. Note
8299 * that in case of normal Machine or SnapshotMachine uninitialization (as
8300 * a result of unregistering or deleting the snapshot), outdated media
8301 * attachments will already be uninitialized and deleted, so this
8302 * code will not affect them. */
8303 if ( !mMediumAttachments.isNull()
8304 && !i_isSessionMachine()
8305 )
8306 {
8307 for (MediumAttachmentList::const_iterator
8308 it = mMediumAttachments->begin();
8309 it != mMediumAttachments->end();
8310 ++it)
8311 {
8312 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8313 if (pMedium.isNull())
8314 continue;
8315 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8316 AssertComRC(rc);
8317 }
8318 }
8319
8320 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8321 {
8322 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8323 if (mData->mFirstSnapshot)
8324 {
8325 // snapshots tree is protected by machine write lock; strictly
8326 // this isn't necessary here since we're deleting the entire
8327 // machine, but otherwise we assert in Snapshot::uninit()
8328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8329 mData->mFirstSnapshot->uninit();
8330 mData->mFirstSnapshot.setNull();
8331 }
8332
8333 mData->mCurrentSnapshot.setNull();
8334 }
8335
8336 /* free data structures (the essential mData structure is not freed here
8337 * since it may be still in use) */
8338 mMediumAttachments.free();
8339 mStorageControllers.free();
8340 mUSBControllers.free();
8341 mHWData.free();
8342 mUserData.free();
8343 mSSData.free();
8344}
8345
8346/**
8347 * Returns a pointer to the Machine object for this machine that acts like a
8348 * parent for complex machine data objects such as shared folders, etc.
8349 *
8350 * For primary Machine objects and for SnapshotMachine objects, returns this
8351 * object's pointer itself. For SessionMachine objects, returns the peer
8352 * (primary) machine pointer.
8353 */
8354Machine *Machine::i_getMachine()
8355{
8356 if (i_isSessionMachine())
8357 return (Machine*)mPeer;
8358 return this;
8359}
8360
8361/**
8362 * Makes sure that there are no machine state dependents. If necessary, waits
8363 * for the number of dependents to drop to zero.
8364 *
8365 * Make sure this method is called from under this object's write lock to
8366 * guarantee that no new dependents may be added when this method returns
8367 * control to the caller.
8368 *
8369 * @note Locks this object for writing. The lock will be released while waiting
8370 * (if necessary).
8371 *
8372 * @warning To be used only in methods that change the machine state!
8373 */
8374void Machine::i_ensureNoStateDependencies()
8375{
8376 AssertReturnVoid(isWriteLockOnCurrentThread());
8377
8378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8379
8380 /* Wait for all state dependents if necessary */
8381 if (mData->mMachineStateDeps != 0)
8382 {
8383 /* lazy semaphore creation */
8384 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8385 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8386
8387 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8388 mData->mMachineStateDeps));
8389
8390 ++mData->mMachineStateChangePending;
8391
8392 /* reset the semaphore before waiting, the last dependent will signal
8393 * it */
8394 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8395
8396 alock.release();
8397
8398 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8399
8400 alock.acquire();
8401
8402 -- mData->mMachineStateChangePending;
8403 }
8404}
8405
8406/**
8407 * Changes the machine state and informs callbacks.
8408 *
8409 * This method is not intended to fail so it either returns S_OK or asserts (and
8410 * returns a failure).
8411 *
8412 * @note Locks this object for writing.
8413 */
8414HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8415{
8416 LogFlowThisFuncEnter();
8417 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8418 Assert(aMachineState != MachineState_Null);
8419
8420 AutoCaller autoCaller(this);
8421 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8422
8423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8424
8425 /* wait for state dependents to drop to zero */
8426 i_ensureNoStateDependencies();
8427
8428 MachineState_T const enmOldState = mData->mMachineState;
8429 if (enmOldState != aMachineState)
8430 {
8431 mData->mMachineState = aMachineState;
8432 RTTimeNow(&mData->mLastStateChange);
8433
8434#ifdef VBOX_WITH_DTRACE_R3_MAIN
8435 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8436#endif
8437 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8438 }
8439
8440 LogFlowThisFuncLeave();
8441 return S_OK;
8442}
8443
8444/**
8445 * Searches for a shared folder with the given logical name
8446 * in the collection of shared folders.
8447 *
8448 * @param aName logical name of the shared folder
8449 * @param aSharedFolder where to return the found object
8450 * @param aSetError whether to set the error info if the folder is
8451 * not found
8452 * @return
8453 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8454 *
8455 * @note
8456 * must be called from under the object's lock!
8457 */
8458HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8459 ComObjPtr<SharedFolder> &aSharedFolder,
8460 bool aSetError /* = false */)
8461{
8462 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8463 for (HWData::SharedFolderList::const_iterator
8464 it = mHWData->mSharedFolders.begin();
8465 it != mHWData->mSharedFolders.end();
8466 ++it)
8467 {
8468 SharedFolder *pSF = *it;
8469 AutoCaller autoCaller(pSF);
8470 if (pSF->i_getName() == aName)
8471 {
8472 aSharedFolder = pSF;
8473 rc = S_OK;
8474 break;
8475 }
8476 }
8477
8478 if (aSetError && FAILED(rc))
8479 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8480
8481 return rc;
8482}
8483
8484/**
8485 * Initializes all machine instance data from the given settings structures
8486 * from XML. The exception is the machine UUID which needs special handling
8487 * depending on the caller's use case, so the caller needs to set that herself.
8488 *
8489 * This gets called in several contexts during machine initialization:
8490 *
8491 * -- When machine XML exists on disk already and needs to be loaded into memory,
8492 * for example, from #i_registeredInit() to load all registered machines on
8493 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8494 * attached to the machine should be part of some media registry already.
8495 *
8496 * -- During OVF import, when a machine config has been constructed from an
8497 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8498 * ensure that the media listed as attachments in the config (which have
8499 * been imported from the OVF) receive the correct registry ID.
8500 *
8501 * -- During VM cloning.
8502 *
8503 * @param config Machine settings from XML.
8504 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8505 * for each attached medium in the config.
8506 * @return
8507 */
8508HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8509 const Guid *puuidRegistry)
8510{
8511 // copy name, description, OS type, teleporter, UTC etc.
8512 mUserData->s = config.machineUserData;
8513
8514 // look up the object by Id to check it is valid
8515 ComObjPtr<GuestOSType> pGuestOSType;
8516 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8517 if (!pGuestOSType.isNull())
8518 mUserData->s.strOsType = pGuestOSType->i_id();
8519
8520 // stateFile (optional)
8521 if (config.strStateFile.isEmpty())
8522 mSSData->strStateFilePath.setNull();
8523 else
8524 {
8525 Utf8Str stateFilePathFull(config.strStateFile);
8526 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8527 if (RT_FAILURE(vrc))
8528 return setErrorBoth(E_FAIL, vrc,
8529 tr("Invalid saved state file path '%s' (%Rrc)"),
8530 config.strStateFile.c_str(),
8531 vrc);
8532 mSSData->strStateFilePath = stateFilePathFull;
8533 }
8534
8535 // snapshot folder needs special processing so set it again
8536 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8537 if (FAILED(rc)) return rc;
8538
8539 /* Copy the extra data items (config may or may not be the same as
8540 * mData->pMachineConfigFile) if necessary. When loading the XML files
8541 * from disk they are the same, but not for OVF import. */
8542 if (mData->pMachineConfigFile != &config)
8543 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8544
8545 /* currentStateModified (optional, default is true) */
8546 mData->mCurrentStateModified = config.fCurrentStateModified;
8547
8548 mData->mLastStateChange = config.timeLastStateChange;
8549
8550 /*
8551 * note: all mUserData members must be assigned prior this point because
8552 * we need to commit changes in order to let mUserData be shared by all
8553 * snapshot machine instances.
8554 */
8555 mUserData.commitCopy();
8556
8557 // machine registry, if present (must be loaded before snapshots)
8558 if (config.canHaveOwnMediaRegistry())
8559 {
8560 // determine machine folder
8561 Utf8Str strMachineFolder = i_getSettingsFileFull();
8562 strMachineFolder.stripFilename();
8563 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8564 config.mediaRegistry,
8565 strMachineFolder);
8566 if (FAILED(rc)) return rc;
8567 }
8568
8569 /* Snapshot node (optional) */
8570 size_t cRootSnapshots;
8571 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8572 {
8573 // there must be only one root snapshot
8574 Assert(cRootSnapshots == 1);
8575
8576 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8577
8578 rc = i_loadSnapshot(snap,
8579 config.uuidCurrentSnapshot,
8580 NULL); // no parent == first snapshot
8581 if (FAILED(rc)) return rc;
8582 }
8583
8584 // hardware data
8585 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8586 if (FAILED(rc)) return rc;
8587
8588 /*
8589 * NOTE: the assignment below must be the last thing to do,
8590 * otherwise it will be not possible to change the settings
8591 * somewhere in the code above because all setters will be
8592 * blocked by i_checkStateDependency(MutableStateDep).
8593 */
8594
8595 /* set the machine state to Aborted or Saved when appropriate */
8596 if (config.fAborted)
8597 {
8598 mSSData->strStateFilePath.setNull();
8599
8600 /* no need to use i_setMachineState() during init() */
8601 mData->mMachineState = MachineState_Aborted;
8602 }
8603 else if (!mSSData->strStateFilePath.isEmpty())
8604 {
8605 /* no need to use i_setMachineState() during init() */
8606 mData->mMachineState = MachineState_Saved;
8607 }
8608
8609 // after loading settings, we are no longer different from the XML on disk
8610 mData->flModifications = 0;
8611
8612 return S_OK;
8613}
8614
8615/**
8616 * Recursively loads all snapshots starting from the given.
8617 *
8618 * @param data snapshot settings.
8619 * @param aCurSnapshotId Current snapshot ID from the settings file.
8620 * @param aParentSnapshot Parent snapshot.
8621 */
8622HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8623 const Guid &aCurSnapshotId,
8624 Snapshot *aParentSnapshot)
8625{
8626 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8627 AssertReturn(!i_isSessionMachine(), E_FAIL);
8628
8629 HRESULT rc = S_OK;
8630
8631 Utf8Str strStateFile;
8632 if (!data.strStateFile.isEmpty())
8633 {
8634 /* optional */
8635 strStateFile = data.strStateFile;
8636 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8637 if (RT_FAILURE(vrc))
8638 return setErrorBoth(E_FAIL, vrc,
8639 tr("Invalid saved state file path '%s' (%Rrc)"),
8640 strStateFile.c_str(),
8641 vrc);
8642 }
8643
8644 /* create a snapshot machine object */
8645 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8646 pSnapshotMachine.createObject();
8647 rc = pSnapshotMachine->initFromSettings(this,
8648 data.hardware,
8649 &data.debugging,
8650 &data.autostart,
8651 data.uuid.ref(),
8652 strStateFile);
8653 if (FAILED(rc)) return rc;
8654
8655 /* create a snapshot object */
8656 ComObjPtr<Snapshot> pSnapshot;
8657 pSnapshot.createObject();
8658 /* initialize the snapshot */
8659 rc = pSnapshot->init(mParent, // VirtualBox object
8660 data.uuid,
8661 data.strName,
8662 data.strDescription,
8663 data.timestamp,
8664 pSnapshotMachine,
8665 aParentSnapshot);
8666 if (FAILED(rc)) return rc;
8667
8668 /* memorize the first snapshot if necessary */
8669 if (!mData->mFirstSnapshot)
8670 mData->mFirstSnapshot = pSnapshot;
8671
8672 /* memorize the current snapshot when appropriate */
8673 if ( !mData->mCurrentSnapshot
8674 && pSnapshot->i_getId() == aCurSnapshotId
8675 )
8676 mData->mCurrentSnapshot = pSnapshot;
8677
8678 // now create the children
8679 for (settings::SnapshotsList::const_iterator
8680 it = data.llChildSnapshots.begin();
8681 it != data.llChildSnapshots.end();
8682 ++it)
8683 {
8684 const settings::Snapshot &childData = *it;
8685 // recurse
8686 rc = i_loadSnapshot(childData,
8687 aCurSnapshotId,
8688 pSnapshot); // parent = the one we created above
8689 if (FAILED(rc)) return rc;
8690 }
8691
8692 return rc;
8693}
8694
8695/**
8696 * Loads settings into mHWData.
8697 *
8698 * @param puuidRegistry Registry ID.
8699 * @param puuidSnapshot Snapshot ID
8700 * @param data Reference to the hardware settings.
8701 * @param pDbg Pointer to the debugging settings.
8702 * @param pAutostart Pointer to the autostart settings.
8703 */
8704HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8705 const Guid *puuidSnapshot,
8706 const settings::Hardware &data,
8707 const settings::Debugging *pDbg,
8708 const settings::Autostart *pAutostart)
8709{
8710 AssertReturn(!i_isSessionMachine(), E_FAIL);
8711
8712 HRESULT rc = S_OK;
8713
8714 try
8715 {
8716 ComObjPtr<GuestOSType> pGuestOSType;
8717 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8718
8719 /* The hardware version attribute (optional). */
8720 mHWData->mHWVersion = data.strVersion;
8721 mHWData->mHardwareUUID = data.uuid;
8722
8723 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8724 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8725 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8726 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8727 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8728 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8729 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8730 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8731 mHWData->mPAEEnabled = data.fPAE;
8732 mHWData->mLongMode = data.enmLongMode;
8733 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8734 mHWData->mAPIC = data.fAPIC;
8735 mHWData->mX2APIC = data.fX2APIC;
8736 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8737 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8738 mHWData->mSpecCtrl = data.fSpecCtrl;
8739 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8740 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8741 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8742 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8743 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8744 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8745 mHWData->mCPUCount = data.cCPUs;
8746 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8747 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8748 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8749 mHWData->mCpuProfile = data.strCpuProfile;
8750
8751 // cpu
8752 if (mHWData->mCPUHotPlugEnabled)
8753 {
8754 for (settings::CpuList::const_iterator
8755 it = data.llCpus.begin();
8756 it != data.llCpus.end();
8757 ++it)
8758 {
8759 const settings::Cpu &cpu = *it;
8760
8761 mHWData->mCPUAttached[cpu.ulId] = true;
8762 }
8763 }
8764
8765 // cpuid leafs
8766 for (settings::CpuIdLeafsList::const_iterator
8767 it = data.llCpuIdLeafs.begin();
8768 it != data.llCpuIdLeafs.end();
8769 ++it)
8770 {
8771 const settings::CpuIdLeaf &rLeaf= *it;
8772 if ( rLeaf.idx < UINT32_C(0x20)
8773 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8774 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8775 mHWData->mCpuIdLeafList.push_back(rLeaf);
8776 /* else: just ignore */
8777 }
8778
8779 mHWData->mMemorySize = data.ulMemorySizeMB;
8780 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8781
8782 // boot order
8783 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8784 {
8785 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8786 if (it == data.mapBootOrder.end())
8787 mHWData->mBootOrder[i] = DeviceType_Null;
8788 else
8789 mHWData->mBootOrder[i] = it->second;
8790 }
8791
8792 mHWData->mFirmwareType = data.firmwareType;
8793 mHWData->mPointingHIDType = data.pointingHIDType;
8794 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8795 mHWData->mChipsetType = data.chipsetType;
8796 mHWData->mIommuType = data.iommuType;
8797 mHWData->mParavirtProvider = data.paravirtProvider;
8798 mHWData->mParavirtDebug = data.strParavirtDebug;
8799 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8800 mHWData->mHPETEnabled = data.fHPETEnabled;
8801
8802 /* GraphicsAdapter */
8803 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8804 if (FAILED(rc)) return rc;
8805
8806 /* VRDEServer */
8807 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8808 if (FAILED(rc)) return rc;
8809
8810 /* BIOS */
8811 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8812 if (FAILED(rc)) return rc;
8813
8814 /* Recording settings */
8815 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8816 if (FAILED(rc)) return rc;
8817
8818 // Bandwidth control (must come before network adapters)
8819 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8820 if (FAILED(rc)) return rc;
8821
8822 /* USB controllers */
8823 for (settings::USBControllerList::const_iterator
8824 it = data.usbSettings.llUSBControllers.begin();
8825 it != data.usbSettings.llUSBControllers.end();
8826 ++it)
8827 {
8828 const settings::USBController &settingsCtrl = *it;
8829 ComObjPtr<USBController> newCtrl;
8830
8831 newCtrl.createObject();
8832 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8833 mUSBControllers->push_back(newCtrl);
8834 }
8835
8836 /* USB device filters */
8837 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8838 if (FAILED(rc)) return rc;
8839
8840 // network adapters (establish array size first and apply defaults, to
8841 // ensure reading the same settings as we saved, since the list skips
8842 // adapters having defaults)
8843 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8844 size_t oldCount = mNetworkAdapters.size();
8845 if (newCount > oldCount)
8846 {
8847 mNetworkAdapters.resize(newCount);
8848 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8849 {
8850 unconst(mNetworkAdapters[slot]).createObject();
8851 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8852 }
8853 }
8854 else if (newCount < oldCount)
8855 mNetworkAdapters.resize(newCount);
8856 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8857 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8858 for (settings::NetworkAdaptersList::const_iterator
8859 it = data.llNetworkAdapters.begin();
8860 it != data.llNetworkAdapters.end();
8861 ++it)
8862 {
8863 const settings::NetworkAdapter &nic = *it;
8864
8865 /* slot uniqueness is guaranteed by XML Schema */
8866 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8867 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8868 if (FAILED(rc)) return rc;
8869 }
8870
8871 // serial ports (establish defaults first, to ensure reading the same
8872 // settings as we saved, since the list skips ports having defaults)
8873 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8874 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8875 for (settings::SerialPortsList::const_iterator
8876 it = data.llSerialPorts.begin();
8877 it != data.llSerialPorts.end();
8878 ++it)
8879 {
8880 const settings::SerialPort &s = *it;
8881
8882 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8883 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8884 if (FAILED(rc)) return rc;
8885 }
8886
8887 // parallel ports (establish defaults first, to ensure reading the same
8888 // settings as we saved, since the list skips ports having defaults)
8889 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8890 mParallelPorts[i]->i_applyDefaults();
8891 for (settings::ParallelPortsList::const_iterator
8892 it = data.llParallelPorts.begin();
8893 it != data.llParallelPorts.end();
8894 ++it)
8895 {
8896 const settings::ParallelPort &p = *it;
8897
8898 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8899 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8900 if (FAILED(rc)) return rc;
8901 }
8902
8903 /* AudioAdapter */
8904 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8905 if (FAILED(rc)) return rc;
8906
8907 /* storage controllers */
8908 rc = i_loadStorageControllers(data.storage,
8909 puuidRegistry,
8910 puuidSnapshot);
8911 if (FAILED(rc)) return rc;
8912
8913 /* Shared folders */
8914 for (settings::SharedFoldersList::const_iterator
8915 it = data.llSharedFolders.begin();
8916 it != data.llSharedFolders.end();
8917 ++it)
8918 {
8919 const settings::SharedFolder &sf = *it;
8920
8921 ComObjPtr<SharedFolder> sharedFolder;
8922 /* Check for double entries. Not allowed! */
8923 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8924 if (SUCCEEDED(rc))
8925 return setError(VBOX_E_OBJECT_IN_USE,
8926 tr("Shared folder named '%s' already exists"),
8927 sf.strName.c_str());
8928
8929 /* Create the new shared folder. Don't break on error. This will be
8930 * reported when the machine starts. */
8931 sharedFolder.createObject();
8932 rc = sharedFolder->init(i_getMachine(),
8933 sf.strName,
8934 sf.strHostPath,
8935 RT_BOOL(sf.fWritable),
8936 RT_BOOL(sf.fAutoMount),
8937 sf.strAutoMountPoint,
8938 false /* fFailOnError */);
8939 if (FAILED(rc)) return rc;
8940 mHWData->mSharedFolders.push_back(sharedFolder);
8941 }
8942
8943 // Clipboard
8944 mHWData->mClipboardMode = data.clipboardMode;
8945 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8946
8947 // drag'n'drop
8948 mHWData->mDnDMode = data.dndMode;
8949
8950 // guest settings
8951 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8952
8953 // IO settings
8954 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8955 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8956
8957 // Host PCI devices
8958 for (settings::HostPCIDeviceAttachmentList::const_iterator
8959 it = data.pciAttachments.begin();
8960 it != data.pciAttachments.end();
8961 ++it)
8962 {
8963 const settings::HostPCIDeviceAttachment &hpda = *it;
8964 ComObjPtr<PCIDeviceAttachment> pda;
8965
8966 pda.createObject();
8967 pda->i_loadSettings(this, hpda);
8968 mHWData->mPCIDeviceAssignments.push_back(pda);
8969 }
8970
8971 /*
8972 * (The following isn't really real hardware, but it lives in HWData
8973 * for reasons of convenience.)
8974 */
8975
8976#ifdef VBOX_WITH_GUEST_PROPS
8977 /* Guest properties (optional) */
8978
8979 /* Only load transient guest properties for configs which have saved
8980 * state, because there shouldn't be any for powered off VMs. The same
8981 * logic applies for snapshots, as offline snapshots shouldn't have
8982 * any such properties. They confuse the code in various places.
8983 * Note: can't rely on the machine state, as it isn't set yet. */
8984 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8985 /* apologies for the hacky unconst() usage, but this needs hacking
8986 * actually inconsistent settings into consistency, otherwise there
8987 * will be some corner cases where the inconsistency survives
8988 * surprisingly long without getting fixed, especially for snapshots
8989 * as there are no config changes. */
8990 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8991 for (settings::GuestPropertiesList::iterator
8992 it = llGuestProperties.begin();
8993 it != llGuestProperties.end();
8994 /*nothing*/)
8995 {
8996 const settings::GuestProperty &prop = *it;
8997 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8998 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8999 if ( fSkipTransientGuestProperties
9000 && ( fFlags & GUEST_PROP_F_TRANSIENT
9001 || fFlags & GUEST_PROP_F_TRANSRESET))
9002 {
9003 it = llGuestProperties.erase(it);
9004 continue;
9005 }
9006 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9007 mHWData->mGuestProperties[prop.strName] = property;
9008 ++it;
9009 }
9010#endif /* VBOX_WITH_GUEST_PROPS defined */
9011
9012 rc = i_loadDebugging(pDbg);
9013 if (FAILED(rc))
9014 return rc;
9015
9016 mHWData->mAutostart = *pAutostart;
9017
9018 /* default frontend */
9019 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9020 }
9021 catch (std::bad_alloc &)
9022 {
9023 return E_OUTOFMEMORY;
9024 }
9025
9026 AssertComRC(rc);
9027 return rc;
9028}
9029
9030/**
9031 * Called from i_loadHardware() to load the debugging settings of the
9032 * machine.
9033 *
9034 * @param pDbg Pointer to the settings.
9035 */
9036HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9037{
9038 mHWData->mDebugging = *pDbg;
9039 /* no more processing currently required, this will probably change. */
9040 return S_OK;
9041}
9042
9043/**
9044 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9045 *
9046 * @param data storage settings.
9047 * @param puuidRegistry media registry ID to set media to or NULL;
9048 * see Machine::i_loadMachineDataFromSettings()
9049 * @param puuidSnapshot snapshot ID
9050 * @return
9051 */
9052HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9053 const Guid *puuidRegistry,
9054 const Guid *puuidSnapshot)
9055{
9056 AssertReturn(!i_isSessionMachine(), E_FAIL);
9057
9058 HRESULT rc = S_OK;
9059
9060 for (settings::StorageControllersList::const_iterator
9061 it = data.llStorageControllers.begin();
9062 it != data.llStorageControllers.end();
9063 ++it)
9064 {
9065 const settings::StorageController &ctlData = *it;
9066
9067 ComObjPtr<StorageController> pCtl;
9068 /* Try to find one with the name first. */
9069 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9070 if (SUCCEEDED(rc))
9071 return setError(VBOX_E_OBJECT_IN_USE,
9072 tr("Storage controller named '%s' already exists"),
9073 ctlData.strName.c_str());
9074
9075 pCtl.createObject();
9076 rc = pCtl->init(this,
9077 ctlData.strName,
9078 ctlData.storageBus,
9079 ctlData.ulInstance,
9080 ctlData.fBootable);
9081 if (FAILED(rc)) return rc;
9082
9083 mStorageControllers->push_back(pCtl);
9084
9085 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9086 if (FAILED(rc)) return rc;
9087
9088 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9089 if (FAILED(rc)) return rc;
9090
9091 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9092 if (FAILED(rc)) return rc;
9093
9094 /* Load the attached devices now. */
9095 rc = i_loadStorageDevices(pCtl,
9096 ctlData,
9097 puuidRegistry,
9098 puuidSnapshot);
9099 if (FAILED(rc)) return rc;
9100 }
9101
9102 return S_OK;
9103}
9104
9105/**
9106 * Called from i_loadStorageControllers for a controller's devices.
9107 *
9108 * @param aStorageController
9109 * @param data
9110 * @param puuidRegistry media registry ID to set media to or NULL; see
9111 * Machine::i_loadMachineDataFromSettings()
9112 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9113 * @return
9114 */
9115HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9116 const settings::StorageController &data,
9117 const Guid *puuidRegistry,
9118 const Guid *puuidSnapshot)
9119{
9120 HRESULT rc = S_OK;
9121
9122 /* paranoia: detect duplicate attachments */
9123 for (settings::AttachedDevicesList::const_iterator
9124 it = data.llAttachedDevices.begin();
9125 it != data.llAttachedDevices.end();
9126 ++it)
9127 {
9128 const settings::AttachedDevice &ad = *it;
9129
9130 for (settings::AttachedDevicesList::const_iterator it2 = it;
9131 it2 != data.llAttachedDevices.end();
9132 ++it2)
9133 {
9134 if (it == it2)
9135 continue;
9136
9137 const settings::AttachedDevice &ad2 = *it2;
9138
9139 if ( ad.lPort == ad2.lPort
9140 && ad.lDevice == ad2.lDevice)
9141 {
9142 return setError(E_FAIL,
9143 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9144 aStorageController->i_getName().c_str(),
9145 ad.lPort,
9146 ad.lDevice,
9147 mUserData->s.strName.c_str());
9148 }
9149 }
9150 }
9151
9152 for (settings::AttachedDevicesList::const_iterator
9153 it = data.llAttachedDevices.begin();
9154 it != data.llAttachedDevices.end();
9155 ++it)
9156 {
9157 const settings::AttachedDevice &dev = *it;
9158 ComObjPtr<Medium> medium;
9159
9160 switch (dev.deviceType)
9161 {
9162 case DeviceType_Floppy:
9163 case DeviceType_DVD:
9164 if (dev.strHostDriveSrc.isNotEmpty())
9165 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9166 false /* fRefresh */, medium);
9167 else
9168 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9169 dev.uuid,
9170 false /* fRefresh */,
9171 false /* aSetError */,
9172 medium);
9173 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9174 // This is not an error. The host drive or UUID might have vanished, so just go
9175 // ahead without this removeable medium attachment
9176 rc = S_OK;
9177 break;
9178
9179 case DeviceType_HardDisk:
9180 {
9181 /* find a hard disk by UUID */
9182 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9183 if (FAILED(rc))
9184 {
9185 if (i_isSnapshotMachine())
9186 {
9187 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9188 // so the user knows that the bad disk is in a snapshot somewhere
9189 com::ErrorInfo info;
9190 return setError(E_FAIL,
9191 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9192 puuidSnapshot->raw(),
9193 info.getText().raw());
9194 }
9195 else
9196 return rc;
9197 }
9198
9199 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9200
9201 if (medium->i_getType() == MediumType_Immutable)
9202 {
9203 if (i_isSnapshotMachine())
9204 return setError(E_FAIL,
9205 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9206 "of the virtual machine '%s' ('%s')"),
9207 medium->i_getLocationFull().c_str(),
9208 dev.uuid.raw(),
9209 puuidSnapshot->raw(),
9210 mUserData->s.strName.c_str(),
9211 mData->m_strConfigFileFull.c_str());
9212
9213 return setError(E_FAIL,
9214 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9215 medium->i_getLocationFull().c_str(),
9216 dev.uuid.raw(),
9217 mUserData->s.strName.c_str(),
9218 mData->m_strConfigFileFull.c_str());
9219 }
9220
9221 if (medium->i_getType() == MediumType_MultiAttach)
9222 {
9223 if (i_isSnapshotMachine())
9224 return setError(E_FAIL,
9225 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9226 "of the virtual machine '%s' ('%s')"),
9227 medium->i_getLocationFull().c_str(),
9228 dev.uuid.raw(),
9229 puuidSnapshot->raw(),
9230 mUserData->s.strName.c_str(),
9231 mData->m_strConfigFileFull.c_str());
9232
9233 return setError(E_FAIL,
9234 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9235 medium->i_getLocationFull().c_str(),
9236 dev.uuid.raw(),
9237 mUserData->s.strName.c_str(),
9238 mData->m_strConfigFileFull.c_str());
9239 }
9240
9241 if ( !i_isSnapshotMachine()
9242 && medium->i_getChildren().size() != 0
9243 )
9244 return setError(E_FAIL,
9245 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9246 "because it has %d differencing child hard disks"),
9247 medium->i_getLocationFull().c_str(),
9248 dev.uuid.raw(),
9249 mUserData->s.strName.c_str(),
9250 mData->m_strConfigFileFull.c_str(),
9251 medium->i_getChildren().size());
9252
9253 if (i_findAttachment(*mMediumAttachments.data(),
9254 medium))
9255 return setError(E_FAIL,
9256 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9257 medium->i_getLocationFull().c_str(),
9258 dev.uuid.raw(),
9259 mUserData->s.strName.c_str(),
9260 mData->m_strConfigFileFull.c_str());
9261
9262 break;
9263 }
9264
9265 default:
9266 return setError(E_FAIL,
9267 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9268 medium->i_getLocationFull().c_str(),
9269 mUserData->s.strName.c_str(),
9270 mData->m_strConfigFileFull.c_str());
9271 }
9272
9273 if (FAILED(rc))
9274 break;
9275
9276 /* Bandwidth groups are loaded at this point. */
9277 ComObjPtr<BandwidthGroup> pBwGroup;
9278
9279 if (!dev.strBwGroup.isEmpty())
9280 {
9281 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9282 if (FAILED(rc))
9283 return setError(E_FAIL,
9284 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9285 medium->i_getLocationFull().c_str(),
9286 dev.strBwGroup.c_str(),
9287 mUserData->s.strName.c_str(),
9288 mData->m_strConfigFileFull.c_str());
9289 pBwGroup->i_reference();
9290 }
9291
9292 const Utf8Str controllerName = aStorageController->i_getName();
9293 ComObjPtr<MediumAttachment> pAttachment;
9294 pAttachment.createObject();
9295 rc = pAttachment->init(this,
9296 medium,
9297 controllerName,
9298 dev.lPort,
9299 dev.lDevice,
9300 dev.deviceType,
9301 false,
9302 dev.fPassThrough,
9303 dev.fTempEject,
9304 dev.fNonRotational,
9305 dev.fDiscard,
9306 dev.fHotPluggable,
9307 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9308 if (FAILED(rc)) break;
9309
9310 /* associate the medium with this machine and snapshot */
9311 if (!medium.isNull())
9312 {
9313 AutoCaller medCaller(medium);
9314 if (FAILED(medCaller.rc())) return medCaller.rc();
9315 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9316
9317 if (i_isSnapshotMachine())
9318 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9319 else
9320 rc = medium->i_addBackReference(mData->mUuid);
9321 /* If the medium->addBackReference fails it sets an appropriate
9322 * error message, so no need to do any guesswork here. */
9323
9324 if (puuidRegistry)
9325 // caller wants registry ID to be set on all attached media (OVF import case)
9326 medium->i_addRegistry(*puuidRegistry);
9327 }
9328
9329 if (FAILED(rc))
9330 break;
9331
9332 /* back up mMediumAttachments to let registeredInit() properly rollback
9333 * on failure (= limited accessibility) */
9334 i_setModified(IsModified_Storage);
9335 mMediumAttachments.backup();
9336 mMediumAttachments->push_back(pAttachment);
9337 }
9338
9339 return rc;
9340}
9341
9342/**
9343 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9344 *
9345 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9346 * @param aSnapshot where to return the found snapshot
9347 * @param aSetError true to set extended error info on failure
9348 */
9349HRESULT Machine::i_findSnapshotById(const Guid &aId,
9350 ComObjPtr<Snapshot> &aSnapshot,
9351 bool aSetError /* = false */)
9352{
9353 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9354
9355 if (!mData->mFirstSnapshot)
9356 {
9357 if (aSetError)
9358 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9359 return E_FAIL;
9360 }
9361
9362 if (aId.isZero())
9363 aSnapshot = mData->mFirstSnapshot;
9364 else
9365 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9366
9367 if (!aSnapshot)
9368 {
9369 if (aSetError)
9370 return setError(E_FAIL,
9371 tr("Could not find a snapshot with UUID {%s}"),
9372 aId.toString().c_str());
9373 return E_FAIL;
9374 }
9375
9376 return S_OK;
9377}
9378
9379/**
9380 * Returns the snapshot with the given name or fails of no such snapshot.
9381 *
9382 * @param strName snapshot name to find
9383 * @param aSnapshot where to return the found snapshot
9384 * @param aSetError true to set extended error info on failure
9385 */
9386HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9387 ComObjPtr<Snapshot> &aSnapshot,
9388 bool aSetError /* = false */)
9389{
9390 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9391
9392 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9393
9394 if (!mData->mFirstSnapshot)
9395 {
9396 if (aSetError)
9397 return setError(VBOX_E_OBJECT_NOT_FOUND,
9398 tr("This machine does not have any snapshots"));
9399 return VBOX_E_OBJECT_NOT_FOUND;
9400 }
9401
9402 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9403
9404 if (!aSnapshot)
9405 {
9406 if (aSetError)
9407 return setError(VBOX_E_OBJECT_NOT_FOUND,
9408 tr("Could not find a snapshot named '%s'"), strName.c_str());
9409 return VBOX_E_OBJECT_NOT_FOUND;
9410 }
9411
9412 return S_OK;
9413}
9414
9415/**
9416 * Returns a storage controller object with the given name.
9417 *
9418 * @param aName storage controller name to find
9419 * @param aStorageController where to return the found storage controller
9420 * @param aSetError true to set extended error info on failure
9421 */
9422HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9423 ComObjPtr<StorageController> &aStorageController,
9424 bool aSetError /* = false */)
9425{
9426 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9427
9428 for (StorageControllerList::const_iterator
9429 it = mStorageControllers->begin();
9430 it != mStorageControllers->end();
9431 ++it)
9432 {
9433 if ((*it)->i_getName() == aName)
9434 {
9435 aStorageController = (*it);
9436 return S_OK;
9437 }
9438 }
9439
9440 if (aSetError)
9441 return setError(VBOX_E_OBJECT_NOT_FOUND,
9442 tr("Could not find a storage controller named '%s'"),
9443 aName.c_str());
9444 return VBOX_E_OBJECT_NOT_FOUND;
9445}
9446
9447/**
9448 * Returns a USB controller object with the given name.
9449 *
9450 * @param aName USB controller name to find
9451 * @param aUSBController where to return the found USB controller
9452 * @param aSetError true to set extended error info on failure
9453 */
9454HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9455 ComObjPtr<USBController> &aUSBController,
9456 bool aSetError /* = false */)
9457{
9458 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9459
9460 for (USBControllerList::const_iterator
9461 it = mUSBControllers->begin();
9462 it != mUSBControllers->end();
9463 ++it)
9464 {
9465 if ((*it)->i_getName() == aName)
9466 {
9467 aUSBController = (*it);
9468 return S_OK;
9469 }
9470 }
9471
9472 if (aSetError)
9473 return setError(VBOX_E_OBJECT_NOT_FOUND,
9474 tr("Could not find a storage controller named '%s'"),
9475 aName.c_str());
9476 return VBOX_E_OBJECT_NOT_FOUND;
9477}
9478
9479/**
9480 * Returns the number of USB controller instance of the given type.
9481 *
9482 * @param enmType USB controller type.
9483 */
9484ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9485{
9486 ULONG cCtrls = 0;
9487
9488 for (USBControllerList::const_iterator
9489 it = mUSBControllers->begin();
9490 it != mUSBControllers->end();
9491 ++it)
9492 {
9493 if ((*it)->i_getControllerType() == enmType)
9494 cCtrls++;
9495 }
9496
9497 return cCtrls;
9498}
9499
9500HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9501 MediumAttachmentList &atts)
9502{
9503 AutoCaller autoCaller(this);
9504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9505
9506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9507
9508 for (MediumAttachmentList::const_iterator
9509 it = mMediumAttachments->begin();
9510 it != mMediumAttachments->end();
9511 ++it)
9512 {
9513 const ComObjPtr<MediumAttachment> &pAtt = *it;
9514 // should never happen, but deal with NULL pointers in the list.
9515 AssertContinue(!pAtt.isNull());
9516
9517 // getControllerName() needs caller+read lock
9518 AutoCaller autoAttCaller(pAtt);
9519 if (FAILED(autoAttCaller.rc()))
9520 {
9521 atts.clear();
9522 return autoAttCaller.rc();
9523 }
9524 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9525
9526 if (pAtt->i_getControllerName() == aName)
9527 atts.push_back(pAtt);
9528 }
9529
9530 return S_OK;
9531}
9532
9533
9534/**
9535 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9536 * file if the machine name was changed and about creating a new settings file
9537 * if this is a new machine.
9538 *
9539 * @note Must be never called directly but only from #saveSettings().
9540 */
9541HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9542{
9543 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9544
9545 HRESULT rc = S_OK;
9546
9547 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9548
9549 /// @todo need to handle primary group change, too
9550
9551 /* attempt to rename the settings file if machine name is changed */
9552 if ( mUserData->s.fNameSync
9553 && mUserData.isBackedUp()
9554 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9555 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9556 )
9557 {
9558 bool dirRenamed = false;
9559 bool fileRenamed = false;
9560
9561 Utf8Str configFile, newConfigFile;
9562 Utf8Str configFilePrev, newConfigFilePrev;
9563 Utf8Str NVRAMFile, newNVRAMFile;
9564 Utf8Str configDir, newConfigDir;
9565
9566 do
9567 {
9568 int vrc = VINF_SUCCESS;
9569
9570 Utf8Str name = mUserData.backedUpData()->s.strName;
9571 Utf8Str newName = mUserData->s.strName;
9572 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9573 if (group == "/")
9574 group.setNull();
9575 Utf8Str newGroup = mUserData->s.llGroups.front();
9576 if (newGroup == "/")
9577 newGroup.setNull();
9578
9579 configFile = mData->m_strConfigFileFull;
9580
9581 /* first, rename the directory if it matches the group and machine name */
9582 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9583 /** @todo hack, make somehow use of ComposeMachineFilename */
9584 if (mUserData->s.fDirectoryIncludesUUID)
9585 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9586 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9587 /** @todo hack, make somehow use of ComposeMachineFilename */
9588 if (mUserData->s.fDirectoryIncludesUUID)
9589 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9590 configDir = configFile;
9591 configDir.stripFilename();
9592 newConfigDir = configDir;
9593 if ( configDir.length() >= groupPlusName.length()
9594 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9595 groupPlusName.c_str()))
9596 {
9597 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9598 Utf8Str newConfigBaseDir(newConfigDir);
9599 newConfigDir.append(newGroupPlusName);
9600 /* consistency: use \ if appropriate on the platform */
9601 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9602 /* new dir and old dir cannot be equal here because of 'if'
9603 * above and because name != newName */
9604 Assert(configDir != newConfigDir);
9605 if (!fSettingsFileIsNew)
9606 {
9607 /* perform real rename only if the machine is not new */
9608 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9609 if ( vrc == VERR_FILE_NOT_FOUND
9610 || vrc == VERR_PATH_NOT_FOUND)
9611 {
9612 /* create the parent directory, then retry renaming */
9613 Utf8Str parent(newConfigDir);
9614 parent.stripFilename();
9615 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9616 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9617 }
9618 if (RT_FAILURE(vrc))
9619 {
9620 rc = setErrorBoth(E_FAIL, vrc,
9621 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9622 configDir.c_str(),
9623 newConfigDir.c_str(),
9624 vrc);
9625 break;
9626 }
9627 /* delete subdirectories which are no longer needed */
9628 Utf8Str dir(configDir);
9629 dir.stripFilename();
9630 while (dir != newConfigBaseDir && dir != ".")
9631 {
9632 vrc = RTDirRemove(dir.c_str());
9633 if (RT_FAILURE(vrc))
9634 break;
9635 dir.stripFilename();
9636 }
9637 dirRenamed = true;
9638 }
9639 }
9640
9641 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9642
9643 /* then try to rename the settings file itself */
9644 if (newConfigFile != configFile)
9645 {
9646 /* get the path to old settings file in renamed directory */
9647 Assert(mData->m_strConfigFileFull == configFile);
9648 configFile.printf("%s%c%s",
9649 newConfigDir.c_str(),
9650 RTPATH_DELIMITER,
9651 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9652 if (!fSettingsFileIsNew)
9653 {
9654 /* perform real rename only if the machine is not new */
9655 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9656 if (RT_FAILURE(vrc))
9657 {
9658 rc = setErrorBoth(E_FAIL, vrc,
9659 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9660 configFile.c_str(),
9661 newConfigFile.c_str(),
9662 vrc);
9663 break;
9664 }
9665 fileRenamed = true;
9666 configFilePrev = configFile;
9667 configFilePrev += "-prev";
9668 newConfigFilePrev = newConfigFile;
9669 newConfigFilePrev += "-prev";
9670 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9671 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9672 if (NVRAMFile.isNotEmpty())
9673 {
9674 // in the NVRAM file path, replace the old directory with the new directory
9675 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9676 {
9677 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9678 NVRAMFile = newConfigDir + strNVRAMFile;
9679 }
9680 newNVRAMFile = newConfigFile;
9681 newNVRAMFile.stripSuffix();
9682 newNVRAMFile += ".nvram";
9683 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9684 }
9685 }
9686 }
9687
9688 // update m_strConfigFileFull amd mConfigFile
9689 mData->m_strConfigFileFull = newConfigFile;
9690 // compute the relative path too
9691 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9692
9693 // store the old and new so that VirtualBox::i_saveSettings() can update
9694 // the media registry
9695 if ( mData->mRegistered
9696 && (configDir != newConfigDir || configFile != newConfigFile))
9697 {
9698 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9699
9700 if (pfNeedsGlobalSaveSettings)
9701 *pfNeedsGlobalSaveSettings = true;
9702 }
9703
9704 // in the saved state file path, replace the old directory with the new directory
9705 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9706 {
9707 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9708 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9709 }
9710 if (newNVRAMFile.isNotEmpty())
9711 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9712
9713 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9714 if (mData->mFirstSnapshot)
9715 {
9716 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9717 newConfigDir.c_str());
9718 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9719 newConfigDir.c_str());
9720 }
9721 }
9722 while (0);
9723
9724 if (FAILED(rc))
9725 {
9726 /* silently try to rename everything back */
9727 if (fileRenamed)
9728 {
9729 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9730 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9731 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9732 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9733 }
9734 if (dirRenamed)
9735 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9736 }
9737
9738 if (FAILED(rc)) return rc;
9739 }
9740
9741 if (fSettingsFileIsNew)
9742 {
9743 /* create a virgin config file */
9744 int vrc = VINF_SUCCESS;
9745
9746 /* ensure the settings directory exists */
9747 Utf8Str path(mData->m_strConfigFileFull);
9748 path.stripFilename();
9749 if (!RTDirExists(path.c_str()))
9750 {
9751 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9752 if (RT_FAILURE(vrc))
9753 {
9754 return setErrorBoth(E_FAIL, vrc,
9755 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9756 path.c_str(),
9757 vrc);
9758 }
9759 }
9760
9761 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9762 path = Utf8Str(mData->m_strConfigFileFull);
9763 RTFILE f = NIL_RTFILE;
9764 vrc = RTFileOpen(&f, path.c_str(),
9765 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9766 if (RT_FAILURE(vrc))
9767 return setErrorBoth(E_FAIL, vrc,
9768 tr("Could not create the settings file '%s' (%Rrc)"),
9769 path.c_str(),
9770 vrc);
9771 RTFileClose(f);
9772 }
9773
9774 return rc;
9775}
9776
9777/**
9778 * Saves and commits machine data, user data and hardware data.
9779 *
9780 * Note that on failure, the data remains uncommitted.
9781 *
9782 * @a aFlags may combine the following flags:
9783 *
9784 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9785 * Used when saving settings after an operation that makes them 100%
9786 * correspond to the settings from the current snapshot.
9787 * - SaveS_Force: settings will be saved without doing a deep compare of the
9788 * settings structures. This is used when this is called because snapshots
9789 * have changed to avoid the overhead of the deep compare.
9790 *
9791 * @note Must be called from under this object's write lock. Locks children for
9792 * writing.
9793 *
9794 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9795 * initialized to false and that will be set to true by this function if
9796 * the caller must invoke VirtualBox::i_saveSettings() because the global
9797 * settings have changed. This will happen if a machine rename has been
9798 * saved and the global machine and media registries will therefore need
9799 * updating.
9800 * @param aFlags Flags.
9801 */
9802HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9803 int aFlags /*= 0*/)
9804{
9805 LogFlowThisFuncEnter();
9806
9807 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9808
9809 /* make sure child objects are unable to modify the settings while we are
9810 * saving them */
9811 i_ensureNoStateDependencies();
9812
9813 AssertReturn(!i_isSnapshotMachine(),
9814 E_FAIL);
9815
9816 if (!mData->mAccessible)
9817 return setError(VBOX_E_INVALID_VM_STATE,
9818 tr("The machine is not accessible, so cannot save settings"));
9819
9820 HRESULT rc = S_OK;
9821 bool fNeedsWrite = false;
9822
9823 /* First, prepare to save settings. It will care about renaming the
9824 * settings directory and file if the machine name was changed and about
9825 * creating a new settings file if this is a new machine. */
9826 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9827 if (FAILED(rc)) return rc;
9828
9829 // keep a pointer to the current settings structures
9830 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9831 settings::MachineConfigFile *pNewConfig = NULL;
9832
9833 try
9834 {
9835 // make a fresh one to have everyone write stuff into
9836 pNewConfig = new settings::MachineConfigFile(NULL);
9837 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9838
9839 // now go and copy all the settings data from COM to the settings structures
9840 // (this calls i_saveSettings() on all the COM objects in the machine)
9841 i_copyMachineDataToSettings(*pNewConfig);
9842
9843 if (aFlags & SaveS_ResetCurStateModified)
9844 {
9845 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9846 mData->mCurrentStateModified = FALSE;
9847 fNeedsWrite = true; // always, no need to compare
9848 }
9849 else if (aFlags & SaveS_Force)
9850 {
9851 fNeedsWrite = true; // always, no need to compare
9852 }
9853 else
9854 {
9855 if (!mData->mCurrentStateModified)
9856 {
9857 // do a deep compare of the settings that we just saved with the settings
9858 // previously stored in the config file; this invokes MachineConfigFile::operator==
9859 // which does a deep compare of all the settings, which is expensive but less expensive
9860 // than writing out XML in vain
9861 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9862
9863 // could still be modified if any settings changed
9864 mData->mCurrentStateModified = fAnySettingsChanged;
9865
9866 fNeedsWrite = fAnySettingsChanged;
9867 }
9868 else
9869 fNeedsWrite = true;
9870 }
9871
9872 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9873
9874 if (fNeedsWrite)
9875 // now spit it all out!
9876 pNewConfig->write(mData->m_strConfigFileFull);
9877
9878 mData->pMachineConfigFile = pNewConfig;
9879 delete pOldConfig;
9880 i_commit();
9881
9882 // after saving settings, we are no longer different from the XML on disk
9883 mData->flModifications = 0;
9884 }
9885 catch (HRESULT err)
9886 {
9887 // we assume that error info is set by the thrower
9888 rc = err;
9889
9890 // restore old config
9891 delete pNewConfig;
9892 mData->pMachineConfigFile = pOldConfig;
9893 }
9894 catch (...)
9895 {
9896 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9897 }
9898
9899 if (fNeedsWrite)
9900 {
9901 /* Fire the data change event, even on failure (since we've already
9902 * committed all data). This is done only for SessionMachines because
9903 * mutable Machine instances are always not registered (i.e. private
9904 * to the client process that creates them) and thus don't need to
9905 * inform callbacks. */
9906 if (i_isSessionMachine())
9907 mParent->i_onMachineDataChanged(mData->mUuid);
9908 }
9909
9910 LogFlowThisFunc(("rc=%08X\n", rc));
9911 LogFlowThisFuncLeave();
9912 return rc;
9913}
9914
9915/**
9916 * Implementation for saving the machine settings into the given
9917 * settings::MachineConfigFile instance. This copies machine extradata
9918 * from the previous machine config file in the instance data, if any.
9919 *
9920 * This gets called from two locations:
9921 *
9922 * -- Machine::i_saveSettings(), during the regular XML writing;
9923 *
9924 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9925 * exported to OVF and we write the VirtualBox proprietary XML
9926 * into a <vbox:Machine> tag.
9927 *
9928 * This routine fills all the fields in there, including snapshots, *except*
9929 * for the following:
9930 *
9931 * -- fCurrentStateModified. There is some special logic associated with that.
9932 *
9933 * The caller can then call MachineConfigFile::write() or do something else
9934 * with it.
9935 *
9936 * Caller must hold the machine lock!
9937 *
9938 * This throws XML errors and HRESULT, so the caller must have a catch block!
9939 */
9940void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9941{
9942 // deep copy extradata, being extra careful with self assignment (the STL
9943 // map assignment on Mac OS X clang based Xcode isn't checking)
9944 if (&config != mData->pMachineConfigFile)
9945 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9946
9947 config.uuid = mData->mUuid;
9948
9949 // copy name, description, OS type, teleport, UTC etc.
9950 config.machineUserData = mUserData->s;
9951
9952 if ( mData->mMachineState == MachineState_Saved
9953 || mData->mMachineState == MachineState_Restoring
9954 // when doing certain snapshot operations we may or may not have
9955 // a saved state in the current state, so keep everything as is
9956 || ( ( mData->mMachineState == MachineState_Snapshotting
9957 || mData->mMachineState == MachineState_DeletingSnapshot
9958 || mData->mMachineState == MachineState_RestoringSnapshot)
9959 && (!mSSData->strStateFilePath.isEmpty())
9960 )
9961 )
9962 {
9963 Assert(!mSSData->strStateFilePath.isEmpty());
9964 /* try to make the file name relative to the settings file dir */
9965 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9966 }
9967 else
9968 {
9969 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9970 config.strStateFile.setNull();
9971 }
9972
9973 if (mData->mCurrentSnapshot)
9974 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9975 else
9976 config.uuidCurrentSnapshot.clear();
9977
9978 config.timeLastStateChange = mData->mLastStateChange;
9979 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9980 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9981
9982 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9983 if (FAILED(rc)) throw rc;
9984
9985 // save machine's media registry if this is VirtualBox 4.0 or later
9986 if (config.canHaveOwnMediaRegistry())
9987 {
9988 // determine machine folder
9989 Utf8Str strMachineFolder = i_getSettingsFileFull();
9990 strMachineFolder.stripFilename();
9991 mParent->i_saveMediaRegistry(config.mediaRegistry,
9992 i_getId(), // only media with registry ID == machine UUID
9993 strMachineFolder);
9994 // this throws HRESULT
9995 }
9996
9997 // save snapshots
9998 rc = i_saveAllSnapshots(config);
9999 if (FAILED(rc)) throw rc;
10000}
10001
10002/**
10003 * Saves all snapshots of the machine into the given machine config file. Called
10004 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10005 * @param config
10006 * @return
10007 */
10008HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10009{
10010 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10011
10012 HRESULT rc = S_OK;
10013
10014 try
10015 {
10016 config.llFirstSnapshot.clear();
10017
10018 if (mData->mFirstSnapshot)
10019 {
10020 // the settings use a list for "the first snapshot"
10021 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10022
10023 // get reference to the snapshot on the list and work on that
10024 // element straight in the list to avoid excessive copying later
10025 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10026 if (FAILED(rc)) throw rc;
10027 }
10028
10029// if (mType == IsSessionMachine)
10030// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10031
10032 }
10033 catch (HRESULT err)
10034 {
10035 /* we assume that error info is set by the thrower */
10036 rc = err;
10037 }
10038 catch (...)
10039 {
10040 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10041 }
10042
10043 return rc;
10044}
10045
10046/**
10047 * Saves the VM hardware configuration. It is assumed that the
10048 * given node is empty.
10049 *
10050 * @param data Reference to the settings object for the hardware config.
10051 * @param pDbg Pointer to the settings object for the debugging config
10052 * which happens to live in mHWData.
10053 * @param pAutostart Pointer to the settings object for the autostart config
10054 * which happens to live in mHWData.
10055 */
10056HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10057 settings::Autostart *pAutostart)
10058{
10059 HRESULT rc = S_OK;
10060
10061 try
10062 {
10063 /* The hardware version attribute (optional).
10064 Automatically upgrade from 1 to current default hardware version
10065 when there is no saved state. (ugly!) */
10066 if ( mHWData->mHWVersion == "1"
10067 && mSSData->strStateFilePath.isEmpty()
10068 )
10069 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10070
10071 data.strVersion = mHWData->mHWVersion;
10072 data.uuid = mHWData->mHardwareUUID;
10073
10074 // CPU
10075 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10076 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10077 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10078 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10079 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10080 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10081 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10082 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10083 data.fPAE = !!mHWData->mPAEEnabled;
10084 data.enmLongMode = mHWData->mLongMode;
10085 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10086 data.fAPIC = !!mHWData->mAPIC;
10087 data.fX2APIC = !!mHWData->mX2APIC;
10088 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10089 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10090 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10091 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10092 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10093 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10094 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10095 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10096 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10097 data.cCPUs = mHWData->mCPUCount;
10098 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10099 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10100 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10101 data.strCpuProfile = mHWData->mCpuProfile;
10102
10103 data.llCpus.clear();
10104 if (data.fCpuHotPlug)
10105 {
10106 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10107 {
10108 if (mHWData->mCPUAttached[idx])
10109 {
10110 settings::Cpu cpu;
10111 cpu.ulId = idx;
10112 data.llCpus.push_back(cpu);
10113 }
10114 }
10115 }
10116
10117 /* Standard and Extended CPUID leafs. */
10118 data.llCpuIdLeafs.clear();
10119 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10120
10121 // memory
10122 data.ulMemorySizeMB = mHWData->mMemorySize;
10123 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10124
10125 // firmware
10126 data.firmwareType = mHWData->mFirmwareType;
10127
10128 // HID
10129 data.pointingHIDType = mHWData->mPointingHIDType;
10130 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10131
10132 // chipset
10133 data.chipsetType = mHWData->mChipsetType;
10134
10135 // iommu
10136 data.iommuType = mHWData->mIommuType;
10137
10138 // paravirt
10139 data.paravirtProvider = mHWData->mParavirtProvider;
10140 data.strParavirtDebug = mHWData->mParavirtDebug;
10141
10142 // emulated USB card reader
10143 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10144
10145 // HPET
10146 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10147
10148 // boot order
10149 data.mapBootOrder.clear();
10150 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10151 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10152
10153 /* VRDEServer settings (optional) */
10154 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10155 if (FAILED(rc)) throw rc;
10156
10157 /* BIOS settings (required) */
10158 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10159 if (FAILED(rc)) throw rc;
10160
10161 /* Recording settings (required) */
10162 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10163 if (FAILED(rc)) throw rc;
10164
10165 /* GraphicsAdapter settings (required) */
10166 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10167 if (FAILED(rc)) throw rc;
10168
10169 /* USB Controller (required) */
10170 data.usbSettings.llUSBControllers.clear();
10171 for (USBControllerList::const_iterator
10172 it = mUSBControllers->begin();
10173 it != mUSBControllers->end();
10174 ++it)
10175 {
10176 ComObjPtr<USBController> ctrl = *it;
10177 settings::USBController settingsCtrl;
10178
10179 settingsCtrl.strName = ctrl->i_getName();
10180 settingsCtrl.enmType = ctrl->i_getControllerType();
10181
10182 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10183 }
10184
10185 /* USB device filters (required) */
10186 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10187 if (FAILED(rc)) throw rc;
10188
10189 /* Network adapters (required) */
10190 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10191 data.llNetworkAdapters.clear();
10192 /* Write out only the nominal number of network adapters for this
10193 * chipset type. Since Machine::commit() hasn't been called there
10194 * may be extra NIC settings in the vector. */
10195 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10196 {
10197 settings::NetworkAdapter nic;
10198 nic.ulSlot = (uint32_t)slot;
10199 /* paranoia check... must not be NULL, but must not crash either. */
10200 if (mNetworkAdapters[slot])
10201 {
10202 if (mNetworkAdapters[slot]->i_hasDefaults())
10203 continue;
10204
10205 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10206 if (FAILED(rc)) throw rc;
10207
10208 data.llNetworkAdapters.push_back(nic);
10209 }
10210 }
10211
10212 /* Serial ports */
10213 data.llSerialPorts.clear();
10214 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10215 {
10216 if (mSerialPorts[slot]->i_hasDefaults())
10217 continue;
10218
10219 settings::SerialPort s;
10220 s.ulSlot = slot;
10221 rc = mSerialPorts[slot]->i_saveSettings(s);
10222 if (FAILED(rc)) return rc;
10223
10224 data.llSerialPorts.push_back(s);
10225 }
10226
10227 /* Parallel ports */
10228 data.llParallelPorts.clear();
10229 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10230 {
10231 if (mParallelPorts[slot]->i_hasDefaults())
10232 continue;
10233
10234 settings::ParallelPort p;
10235 p.ulSlot = slot;
10236 rc = mParallelPorts[slot]->i_saveSettings(p);
10237 if (FAILED(rc)) return rc;
10238
10239 data.llParallelPorts.push_back(p);
10240 }
10241
10242 /* Audio adapter */
10243 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10244 if (FAILED(rc)) return rc;
10245
10246 rc = i_saveStorageControllers(data.storage);
10247 if (FAILED(rc)) return rc;
10248
10249 /* Shared folders */
10250 data.llSharedFolders.clear();
10251 for (HWData::SharedFolderList::const_iterator
10252 it = mHWData->mSharedFolders.begin();
10253 it != mHWData->mSharedFolders.end();
10254 ++it)
10255 {
10256 SharedFolder *pSF = *it;
10257 AutoCaller sfCaller(pSF);
10258 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10259 settings::SharedFolder sf;
10260 sf.strName = pSF->i_getName();
10261 sf.strHostPath = pSF->i_getHostPath();
10262 sf.fWritable = !!pSF->i_isWritable();
10263 sf.fAutoMount = !!pSF->i_isAutoMounted();
10264 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10265
10266 data.llSharedFolders.push_back(sf);
10267 }
10268
10269 // clipboard
10270 data.clipboardMode = mHWData->mClipboardMode;
10271 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10272
10273 // drag'n'drop
10274 data.dndMode = mHWData->mDnDMode;
10275
10276 /* Guest */
10277 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10278
10279 // IO settings
10280 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10281 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10282
10283 /* BandwidthControl (required) */
10284 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10285 if (FAILED(rc)) throw rc;
10286
10287 /* Host PCI devices */
10288 data.pciAttachments.clear();
10289 for (HWData::PCIDeviceAssignmentList::const_iterator
10290 it = mHWData->mPCIDeviceAssignments.begin();
10291 it != mHWData->mPCIDeviceAssignments.end();
10292 ++it)
10293 {
10294 ComObjPtr<PCIDeviceAttachment> pda = *it;
10295 settings::HostPCIDeviceAttachment hpda;
10296
10297 rc = pda->i_saveSettings(hpda);
10298 if (FAILED(rc)) throw rc;
10299
10300 data.pciAttachments.push_back(hpda);
10301 }
10302
10303 // guest properties
10304 data.llGuestProperties.clear();
10305#ifdef VBOX_WITH_GUEST_PROPS
10306 for (HWData::GuestPropertyMap::const_iterator
10307 it = mHWData->mGuestProperties.begin();
10308 it != mHWData->mGuestProperties.end();
10309 ++it)
10310 {
10311 HWData::GuestProperty property = it->second;
10312
10313 /* Remove transient guest properties at shutdown unless we
10314 * are saving state. Note that restoring snapshot intentionally
10315 * keeps them, they will be removed if appropriate once the final
10316 * machine state is set (as crashes etc. need to work). */
10317 if ( ( mData->mMachineState == MachineState_PoweredOff
10318 || mData->mMachineState == MachineState_Aborted
10319 || mData->mMachineState == MachineState_Teleported)
10320 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10321 continue;
10322 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10323 prop.strName = it->first;
10324 prop.strValue = property.strValue;
10325 prop.timestamp = (uint64_t)property.mTimestamp;
10326 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10327 GuestPropWriteFlags(property.mFlags, szFlags);
10328 prop.strFlags = szFlags;
10329
10330 data.llGuestProperties.push_back(prop);
10331 }
10332
10333 /* I presume this doesn't require a backup(). */
10334 mData->mGuestPropertiesModified = FALSE;
10335#endif /* VBOX_WITH_GUEST_PROPS defined */
10336
10337 *pDbg = mHWData->mDebugging;
10338 *pAutostart = mHWData->mAutostart;
10339
10340 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10341 }
10342 catch (std::bad_alloc &)
10343 {
10344 return E_OUTOFMEMORY;
10345 }
10346
10347 AssertComRC(rc);
10348 return rc;
10349}
10350
10351/**
10352 * Saves the storage controller configuration.
10353 *
10354 * @param data storage settings.
10355 */
10356HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10357{
10358 data.llStorageControllers.clear();
10359
10360 for (StorageControllerList::const_iterator
10361 it = mStorageControllers->begin();
10362 it != mStorageControllers->end();
10363 ++it)
10364 {
10365 HRESULT rc;
10366 ComObjPtr<StorageController> pCtl = *it;
10367
10368 settings::StorageController ctl;
10369 ctl.strName = pCtl->i_getName();
10370 ctl.controllerType = pCtl->i_getControllerType();
10371 ctl.storageBus = pCtl->i_getStorageBus();
10372 ctl.ulInstance = pCtl->i_getInstance();
10373 ctl.fBootable = pCtl->i_getBootable();
10374
10375 /* Save the port count. */
10376 ULONG portCount;
10377 rc = pCtl->COMGETTER(PortCount)(&portCount);
10378 ComAssertComRCRet(rc, rc);
10379 ctl.ulPortCount = portCount;
10380
10381 /* Save fUseHostIOCache */
10382 BOOL fUseHostIOCache;
10383 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10384 ComAssertComRCRet(rc, rc);
10385 ctl.fUseHostIOCache = !!fUseHostIOCache;
10386
10387 /* save the devices now. */
10388 rc = i_saveStorageDevices(pCtl, ctl);
10389 ComAssertComRCRet(rc, rc);
10390
10391 data.llStorageControllers.push_back(ctl);
10392 }
10393
10394 return S_OK;
10395}
10396
10397/**
10398 * Saves the hard disk configuration.
10399 */
10400HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10401 settings::StorageController &data)
10402{
10403 MediumAttachmentList atts;
10404
10405 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10406 if (FAILED(rc)) return rc;
10407
10408 data.llAttachedDevices.clear();
10409 for (MediumAttachmentList::const_iterator
10410 it = atts.begin();
10411 it != atts.end();
10412 ++it)
10413 {
10414 settings::AttachedDevice dev;
10415 IMediumAttachment *iA = *it;
10416 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10417 Medium *pMedium = pAttach->i_getMedium();
10418
10419 dev.deviceType = pAttach->i_getType();
10420 dev.lPort = pAttach->i_getPort();
10421 dev.lDevice = pAttach->i_getDevice();
10422 dev.fPassThrough = pAttach->i_getPassthrough();
10423 dev.fHotPluggable = pAttach->i_getHotPluggable();
10424 if (pMedium)
10425 {
10426 if (pMedium->i_isHostDrive())
10427 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10428 else
10429 dev.uuid = pMedium->i_getId();
10430 dev.fTempEject = pAttach->i_getTempEject();
10431 dev.fNonRotational = pAttach->i_getNonRotational();
10432 dev.fDiscard = pAttach->i_getDiscard();
10433 }
10434
10435 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10436
10437 data.llAttachedDevices.push_back(dev);
10438 }
10439
10440 return S_OK;
10441}
10442
10443/**
10444 * Saves machine state settings as defined by aFlags
10445 * (SaveSTS_* values).
10446 *
10447 * @param aFlags Combination of SaveSTS_* flags.
10448 *
10449 * @note Locks objects for writing.
10450 */
10451HRESULT Machine::i_saveStateSettings(int aFlags)
10452{
10453 if (aFlags == 0)
10454 return S_OK;
10455
10456 AutoCaller autoCaller(this);
10457 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10458
10459 /* This object's write lock is also necessary to serialize file access
10460 * (prevent concurrent reads and writes) */
10461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10462
10463 HRESULT rc = S_OK;
10464
10465 Assert(mData->pMachineConfigFile);
10466
10467 try
10468 {
10469 if (aFlags & SaveSTS_CurStateModified)
10470 mData->pMachineConfigFile->fCurrentStateModified = true;
10471
10472 if (aFlags & SaveSTS_StateFilePath)
10473 {
10474 if (!mSSData->strStateFilePath.isEmpty())
10475 /* try to make the file name relative to the settings file dir */
10476 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10477 else
10478 mData->pMachineConfigFile->strStateFile.setNull();
10479 }
10480
10481 if (aFlags & SaveSTS_StateTimeStamp)
10482 {
10483 Assert( mData->mMachineState != MachineState_Aborted
10484 || mSSData->strStateFilePath.isEmpty());
10485
10486 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10487
10488 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10489/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10490 }
10491
10492 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10493 }
10494 catch (...)
10495 {
10496 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10497 }
10498
10499 return rc;
10500}
10501
10502/**
10503 * Ensures that the given medium is added to a media registry. If this machine
10504 * was created with 4.0 or later, then the machine registry is used. Otherwise
10505 * the global VirtualBox media registry is used.
10506 *
10507 * Caller must NOT hold machine lock, media tree or any medium locks!
10508 *
10509 * @param pMedium
10510 */
10511void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10512{
10513 /* Paranoia checks: do not hold machine or media tree locks. */
10514 AssertReturnVoid(!isWriteLockOnCurrentThread());
10515 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10516
10517 ComObjPtr<Medium> pBase;
10518 {
10519 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10520 pBase = pMedium->i_getBase();
10521 }
10522
10523 /* Paranoia checks: do not hold medium locks. */
10524 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10525 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10526
10527 // decide which medium registry to use now that the medium is attached:
10528 Guid uuid;
10529 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10530 if (fCanHaveOwnMediaRegistry)
10531 // machine XML is VirtualBox 4.0 or higher:
10532 uuid = i_getId(); // machine UUID
10533 else
10534 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10535
10536 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10537 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10538 if (pMedium->i_addRegistry(uuid))
10539 mParent->i_markRegistryModified(uuid);
10540
10541 /* For more complex hard disk structures it can happen that the base
10542 * medium isn't yet associated with any medium registry. Do that now. */
10543 if (pMedium != pBase)
10544 {
10545 /* Tree lock needed by Medium::addRegistry when recursing. */
10546 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10547 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10548 {
10549 treeLock.release();
10550 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10551 treeLock.acquire();
10552 }
10553 if (pBase->i_addRegistryRecursive(uuid))
10554 {
10555 treeLock.release();
10556 mParent->i_markRegistryModified(uuid);
10557 }
10558 }
10559}
10560
10561/**
10562 * Creates differencing hard disks for all normal hard disks attached to this
10563 * machine and a new set of attachments to refer to created disks.
10564 *
10565 * Used when taking a snapshot or when deleting the current state. Gets called
10566 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10567 *
10568 * This method assumes that mMediumAttachments contains the original hard disk
10569 * attachments it needs to create diffs for. On success, these attachments will
10570 * be replaced with the created diffs.
10571 *
10572 * Attachments with non-normal hard disks are left as is.
10573 *
10574 * If @a aOnline is @c false then the original hard disks that require implicit
10575 * diffs will be locked for reading. Otherwise it is assumed that they are
10576 * already locked for writing (when the VM was started). Note that in the latter
10577 * case it is responsibility of the caller to lock the newly created diffs for
10578 * writing if this method succeeds.
10579 *
10580 * @param aProgress Progress object to run (must contain at least as
10581 * many operations left as the number of hard disks
10582 * attached).
10583 * @param aWeight Weight of this operation.
10584 * @param aOnline Whether the VM was online prior to this operation.
10585 *
10586 * @note The progress object is not marked as completed, neither on success nor
10587 * on failure. This is a responsibility of the caller.
10588 *
10589 * @note Locks this object and the media tree for writing.
10590 */
10591HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10592 ULONG aWeight,
10593 bool aOnline)
10594{
10595 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10596
10597 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10598 AssertReturn(!!pProgressControl, E_INVALIDARG);
10599
10600 AutoCaller autoCaller(this);
10601 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10602
10603 AutoMultiWriteLock2 alock(this->lockHandle(),
10604 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10605
10606 /* must be in a protective state because we release the lock below */
10607 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10608 || mData->mMachineState == MachineState_OnlineSnapshotting
10609 || mData->mMachineState == MachineState_LiveSnapshotting
10610 || mData->mMachineState == MachineState_RestoringSnapshot
10611 || mData->mMachineState == MachineState_DeletingSnapshot
10612 , E_FAIL);
10613
10614 HRESULT rc = S_OK;
10615
10616 // use appropriate locked media map (online or offline)
10617 MediumLockListMap lockedMediaOffline;
10618 MediumLockListMap *lockedMediaMap;
10619 if (aOnline)
10620 lockedMediaMap = &mData->mSession.mLockedMedia;
10621 else
10622 lockedMediaMap = &lockedMediaOffline;
10623
10624 try
10625 {
10626 if (!aOnline)
10627 {
10628 /* lock all attached hard disks early to detect "in use"
10629 * situations before creating actual diffs */
10630 for (MediumAttachmentList::const_iterator
10631 it = mMediumAttachments->begin();
10632 it != mMediumAttachments->end();
10633 ++it)
10634 {
10635 MediumAttachment *pAtt = *it;
10636 if (pAtt->i_getType() == DeviceType_HardDisk)
10637 {
10638 Medium *pMedium = pAtt->i_getMedium();
10639 Assert(pMedium);
10640
10641 MediumLockList *pMediumLockList(new MediumLockList());
10642 alock.release();
10643 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10644 NULL /* pToLockWrite */,
10645 false /* fMediumLockWriteAll */,
10646 NULL,
10647 *pMediumLockList);
10648 alock.acquire();
10649 if (FAILED(rc))
10650 {
10651 delete pMediumLockList;
10652 throw rc;
10653 }
10654 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10655 if (FAILED(rc))
10656 {
10657 throw setError(rc,
10658 tr("Collecting locking information for all attached media failed"));
10659 }
10660 }
10661 }
10662
10663 /* Now lock all media. If this fails, nothing is locked. */
10664 alock.release();
10665 rc = lockedMediaMap->Lock();
10666 alock.acquire();
10667 if (FAILED(rc))
10668 {
10669 throw setError(rc,
10670 tr("Locking of attached media failed"));
10671 }
10672 }
10673
10674 /* remember the current list (note that we don't use backup() since
10675 * mMediumAttachments may be already backed up) */
10676 MediumAttachmentList atts = *mMediumAttachments.data();
10677
10678 /* start from scratch */
10679 mMediumAttachments->clear();
10680
10681 /* go through remembered attachments and create diffs for normal hard
10682 * disks and attach them */
10683 for (MediumAttachmentList::const_iterator
10684 it = atts.begin();
10685 it != atts.end();
10686 ++it)
10687 {
10688 MediumAttachment *pAtt = *it;
10689
10690 DeviceType_T devType = pAtt->i_getType();
10691 Medium *pMedium = pAtt->i_getMedium();
10692
10693 if ( devType != DeviceType_HardDisk
10694 || pMedium == NULL
10695 || pMedium->i_getType() != MediumType_Normal)
10696 {
10697 /* copy the attachment as is */
10698
10699 /** @todo the progress object created in SessionMachine::TakeSnaphot
10700 * only expects operations for hard disks. Later other
10701 * device types need to show up in the progress as well. */
10702 if (devType == DeviceType_HardDisk)
10703 {
10704 if (pMedium == NULL)
10705 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10706 aWeight); // weight
10707 else
10708 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10709 pMedium->i_getBase()->i_getName().c_str()).raw(),
10710 aWeight); // weight
10711 }
10712
10713 mMediumAttachments->push_back(pAtt);
10714 continue;
10715 }
10716
10717 /* need a diff */
10718 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10719 pMedium->i_getBase()->i_getName().c_str()).raw(),
10720 aWeight); // weight
10721
10722 Utf8Str strFullSnapshotFolder;
10723 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10724
10725 ComObjPtr<Medium> diff;
10726 diff.createObject();
10727 // store the diff in the same registry as the parent
10728 // (this cannot fail here because we can't create implicit diffs for
10729 // unregistered images)
10730 Guid uuidRegistryParent;
10731 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10732 Assert(fInRegistry); NOREF(fInRegistry);
10733 rc = diff->init(mParent,
10734 pMedium->i_getPreferredDiffFormat(),
10735 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10736 uuidRegistryParent,
10737 DeviceType_HardDisk);
10738 if (FAILED(rc)) throw rc;
10739
10740 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10741 * the push_back? Looks like we're going to release medium with the
10742 * wrong kind of lock (general issue with if we fail anywhere at all)
10743 * and an orphaned VDI in the snapshots folder. */
10744
10745 /* update the appropriate lock list */
10746 MediumLockList *pMediumLockList;
10747 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10748 AssertComRCThrowRC(rc);
10749 if (aOnline)
10750 {
10751 alock.release();
10752 /* The currently attached medium will be read-only, change
10753 * the lock type to read. */
10754 rc = pMediumLockList->Update(pMedium, false);
10755 alock.acquire();
10756 AssertComRCThrowRC(rc);
10757 }
10758
10759 /* release the locks before the potentially lengthy operation */
10760 alock.release();
10761 rc = pMedium->i_createDiffStorage(diff,
10762 pMedium->i_getPreferredDiffVariant(),
10763 pMediumLockList,
10764 NULL /* aProgress */,
10765 true /* aWait */,
10766 false /* aNotify */);
10767 alock.acquire();
10768 if (FAILED(rc)) throw rc;
10769
10770 /* actual lock list update is done in Machine::i_commitMedia */
10771
10772 rc = diff->i_addBackReference(mData->mUuid);
10773 AssertComRCThrowRC(rc);
10774
10775 /* add a new attachment */
10776 ComObjPtr<MediumAttachment> attachment;
10777 attachment.createObject();
10778 rc = attachment->init(this,
10779 diff,
10780 pAtt->i_getControllerName(),
10781 pAtt->i_getPort(),
10782 pAtt->i_getDevice(),
10783 DeviceType_HardDisk,
10784 true /* aImplicit */,
10785 false /* aPassthrough */,
10786 false /* aTempEject */,
10787 pAtt->i_getNonRotational(),
10788 pAtt->i_getDiscard(),
10789 pAtt->i_getHotPluggable(),
10790 pAtt->i_getBandwidthGroup());
10791 if (FAILED(rc)) throw rc;
10792
10793 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10794 AssertComRCThrowRC(rc);
10795 mMediumAttachments->push_back(attachment);
10796 }
10797 }
10798 catch (HRESULT aRC) { rc = aRC; }
10799
10800 /* unlock all hard disks we locked when there is no VM */
10801 if (!aOnline)
10802 {
10803 ErrorInfoKeeper eik;
10804
10805 HRESULT rc1 = lockedMediaMap->Clear();
10806 AssertComRC(rc1);
10807 }
10808
10809 return rc;
10810}
10811
10812/**
10813 * Deletes implicit differencing hard disks created either by
10814 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10815 * mMediumAttachments.
10816 *
10817 * Note that to delete hard disks created by #attachDevice() this method is
10818 * called from #i_rollbackMedia() when the changes are rolled back.
10819 *
10820 * @note Locks this object and the media tree for writing.
10821 */
10822HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10823{
10824 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10825
10826 AutoCaller autoCaller(this);
10827 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10828
10829 AutoMultiWriteLock2 alock(this->lockHandle(),
10830 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10831
10832 /* We absolutely must have backed up state. */
10833 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10834
10835 /* Check if there are any implicitly created diff images. */
10836 bool fImplicitDiffs = false;
10837 for (MediumAttachmentList::const_iterator
10838 it = mMediumAttachments->begin();
10839 it != mMediumAttachments->end();
10840 ++it)
10841 {
10842 const ComObjPtr<MediumAttachment> &pAtt = *it;
10843 if (pAtt->i_isImplicit())
10844 {
10845 fImplicitDiffs = true;
10846 break;
10847 }
10848 }
10849 /* If there is nothing to do, leave early. This saves lots of image locking
10850 * effort. It also avoids a MachineStateChanged event without real reason.
10851 * This is important e.g. when loading a VM config, because there should be
10852 * no events. Otherwise API clients can become thoroughly confused for
10853 * inaccessible VMs (the code for loading VM configs uses this method for
10854 * cleanup if the config makes no sense), as they take such events as an
10855 * indication that the VM is alive, and they would force the VM config to
10856 * be reread, leading to an endless loop. */
10857 if (!fImplicitDiffs)
10858 return S_OK;
10859
10860 HRESULT rc = S_OK;
10861 MachineState_T oldState = mData->mMachineState;
10862
10863 /* will release the lock before the potentially lengthy operation,
10864 * so protect with the special state (unless already protected) */
10865 if ( oldState != MachineState_Snapshotting
10866 && oldState != MachineState_OnlineSnapshotting
10867 && oldState != MachineState_LiveSnapshotting
10868 && oldState != MachineState_RestoringSnapshot
10869 && oldState != MachineState_DeletingSnapshot
10870 && oldState != MachineState_DeletingSnapshotOnline
10871 && oldState != MachineState_DeletingSnapshotPaused
10872 )
10873 i_setMachineState(MachineState_SettingUp);
10874
10875 // use appropriate locked media map (online or offline)
10876 MediumLockListMap lockedMediaOffline;
10877 MediumLockListMap *lockedMediaMap;
10878 if (aOnline)
10879 lockedMediaMap = &mData->mSession.mLockedMedia;
10880 else
10881 lockedMediaMap = &lockedMediaOffline;
10882
10883 try
10884 {
10885 if (!aOnline)
10886 {
10887 /* lock all attached hard disks early to detect "in use"
10888 * situations before deleting actual diffs */
10889 for (MediumAttachmentList::const_iterator
10890 it = mMediumAttachments->begin();
10891 it != mMediumAttachments->end();
10892 ++it)
10893 {
10894 MediumAttachment *pAtt = *it;
10895 if (pAtt->i_getType() == DeviceType_HardDisk)
10896 {
10897 Medium *pMedium = pAtt->i_getMedium();
10898 Assert(pMedium);
10899
10900 MediumLockList *pMediumLockList(new MediumLockList());
10901 alock.release();
10902 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10903 NULL /* pToLockWrite */,
10904 false /* fMediumLockWriteAll */,
10905 NULL,
10906 *pMediumLockList);
10907 alock.acquire();
10908
10909 if (FAILED(rc))
10910 {
10911 delete pMediumLockList;
10912 throw rc;
10913 }
10914
10915 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10916 if (FAILED(rc))
10917 throw rc;
10918 }
10919 }
10920
10921 if (FAILED(rc))
10922 throw rc;
10923 } // end of offline
10924
10925 /* Lock lists are now up to date and include implicitly created media */
10926
10927 /* Go through remembered attachments and delete all implicitly created
10928 * diffs and fix up the attachment information */
10929 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10930 MediumAttachmentList implicitAtts;
10931 for (MediumAttachmentList::const_iterator
10932 it = mMediumAttachments->begin();
10933 it != mMediumAttachments->end();
10934 ++it)
10935 {
10936 ComObjPtr<MediumAttachment> pAtt = *it;
10937 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10938 if (pMedium.isNull())
10939 continue;
10940
10941 // Implicit attachments go on the list for deletion and back references are removed.
10942 if (pAtt->i_isImplicit())
10943 {
10944 /* Deassociate and mark for deletion */
10945 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10946 rc = pMedium->i_removeBackReference(mData->mUuid);
10947 if (FAILED(rc))
10948 throw rc;
10949 implicitAtts.push_back(pAtt);
10950 continue;
10951 }
10952
10953 /* Was this medium attached before? */
10954 if (!i_findAttachment(oldAtts, pMedium))
10955 {
10956 /* no: de-associate */
10957 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10958 rc = pMedium->i_removeBackReference(mData->mUuid);
10959 if (FAILED(rc))
10960 throw rc;
10961 continue;
10962 }
10963 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10964 }
10965
10966 /* If there are implicit attachments to delete, throw away the lock
10967 * map contents (which will unlock all media) since the medium
10968 * attachments will be rolled back. Below we need to completely
10969 * recreate the lock map anyway since it is infinitely complex to
10970 * do this incrementally (would need reconstructing each attachment
10971 * change, which would be extremely hairy). */
10972 if (implicitAtts.size() != 0)
10973 {
10974 ErrorInfoKeeper eik;
10975
10976 HRESULT rc1 = lockedMediaMap->Clear();
10977 AssertComRC(rc1);
10978 }
10979
10980 /* rollback hard disk changes */
10981 mMediumAttachments.rollback();
10982
10983 MultiResult mrc(S_OK);
10984
10985 // Delete unused implicit diffs.
10986 if (implicitAtts.size() != 0)
10987 {
10988 alock.release();
10989
10990 for (MediumAttachmentList::const_iterator
10991 it = implicitAtts.begin();
10992 it != implicitAtts.end();
10993 ++it)
10994 {
10995 // Remove medium associated with this attachment.
10996 ComObjPtr<MediumAttachment> pAtt = *it;
10997 Assert(pAtt);
10998 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10999 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11000 Assert(pMedium);
11001
11002 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11003 // continue on delete failure, just collect error messages
11004 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11005 pMedium->i_getLocationFull().c_str() ));
11006 mrc = rc;
11007 }
11008 // Clear the list of deleted implicit attachments now, while not
11009 // holding the lock, as it will ultimately trigger Medium::uninit()
11010 // calls which assume that the media tree lock isn't held.
11011 implicitAtts.clear();
11012
11013 alock.acquire();
11014
11015 /* if there is a VM recreate media lock map as mentioned above,
11016 * otherwise it is a waste of time and we leave things unlocked */
11017 if (aOnline)
11018 {
11019 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11020 /* must never be NULL, but better safe than sorry */
11021 if (!pMachine.isNull())
11022 {
11023 alock.release();
11024 rc = mData->mSession.mMachine->i_lockMedia();
11025 alock.acquire();
11026 if (FAILED(rc))
11027 throw rc;
11028 }
11029 }
11030 }
11031 }
11032 catch (HRESULT aRC) {rc = aRC;}
11033
11034 if (mData->mMachineState == MachineState_SettingUp)
11035 i_setMachineState(oldState);
11036
11037 /* unlock all hard disks we locked when there is no VM */
11038 if (!aOnline)
11039 {
11040 ErrorInfoKeeper eik;
11041
11042 HRESULT rc1 = lockedMediaMap->Clear();
11043 AssertComRC(rc1);
11044 }
11045
11046 return rc;
11047}
11048
11049
11050/**
11051 * Looks through the given list of media attachments for one with the given parameters
11052 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11053 * can be searched as well if needed.
11054 *
11055 * @param ll
11056 * @param aControllerName
11057 * @param aControllerPort
11058 * @param aDevice
11059 * @return
11060 */
11061MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11062 const Utf8Str &aControllerName,
11063 LONG aControllerPort,
11064 LONG aDevice)
11065{
11066 for (MediumAttachmentList::const_iterator
11067 it = ll.begin();
11068 it != ll.end();
11069 ++it)
11070 {
11071 MediumAttachment *pAttach = *it;
11072 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11073 return pAttach;
11074 }
11075
11076 return NULL;
11077}
11078
11079/**
11080 * Looks through the given list of media attachments for one with the given parameters
11081 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11082 * can be searched as well if needed.
11083 *
11084 * @param ll
11085 * @param pMedium
11086 * @return
11087 */
11088MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11089 ComObjPtr<Medium> pMedium)
11090{
11091 for (MediumAttachmentList::const_iterator
11092 it = ll.begin();
11093 it != ll.end();
11094 ++it)
11095 {
11096 MediumAttachment *pAttach = *it;
11097 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11098 if (pMediumThis == pMedium)
11099 return pAttach;
11100 }
11101
11102 return NULL;
11103}
11104
11105/**
11106 * Looks through the given list of media attachments for one with the given parameters
11107 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11108 * can be searched as well if needed.
11109 *
11110 * @param ll
11111 * @param id
11112 * @return
11113 */
11114MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11115 Guid &id)
11116{
11117 for (MediumAttachmentList::const_iterator
11118 it = ll.begin();
11119 it != ll.end();
11120 ++it)
11121 {
11122 MediumAttachment *pAttach = *it;
11123 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11124 if (pMediumThis->i_getId() == id)
11125 return pAttach;
11126 }
11127
11128 return NULL;
11129}
11130
11131/**
11132 * Main implementation for Machine::DetachDevice. This also gets called
11133 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11134 *
11135 * @param pAttach Medium attachment to detach.
11136 * @param writeLock Machine write lock which the caller must have locked once.
11137 * This may be released temporarily in here.
11138 * @param pSnapshot If NULL, then the detachment is for the current machine.
11139 * Otherwise this is for a SnapshotMachine, and this must be
11140 * its snapshot.
11141 * @return
11142 */
11143HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11144 AutoWriteLock &writeLock,
11145 Snapshot *pSnapshot)
11146{
11147 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11148 DeviceType_T mediumType = pAttach->i_getType();
11149
11150 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11151
11152 if (pAttach->i_isImplicit())
11153 {
11154 /* attempt to implicitly delete the implicitly created diff */
11155
11156 /// @todo move the implicit flag from MediumAttachment to Medium
11157 /// and forbid any hard disk operation when it is implicit. Or maybe
11158 /// a special media state for it to make it even more simple.
11159
11160 Assert(mMediumAttachments.isBackedUp());
11161
11162 /* will release the lock before the potentially lengthy operation, so
11163 * protect with the special state */
11164 MachineState_T oldState = mData->mMachineState;
11165 i_setMachineState(MachineState_SettingUp);
11166
11167 writeLock.release();
11168
11169 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11170 true /*aWait*/,
11171 false /*aNotify*/);
11172
11173 writeLock.acquire();
11174
11175 i_setMachineState(oldState);
11176
11177 if (FAILED(rc)) return rc;
11178 }
11179
11180 i_setModified(IsModified_Storage);
11181 mMediumAttachments.backup();
11182 mMediumAttachments->remove(pAttach);
11183
11184 if (!oldmedium.isNull())
11185 {
11186 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11187 if (pSnapshot)
11188 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11189 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11190 else if (mediumType != DeviceType_HardDisk)
11191 oldmedium->i_removeBackReference(mData->mUuid);
11192 }
11193
11194 return S_OK;
11195}
11196
11197/**
11198 * Goes thru all media of the given list and
11199 *
11200 * 1) calls i_detachDevice() on each of them for this machine and
11201 * 2) adds all Medium objects found in the process to the given list,
11202 * depending on cleanupMode.
11203 *
11204 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11205 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11206 * media to the list.
11207 *
11208 * This gets called from Machine::Unregister, both for the actual Machine and
11209 * the SnapshotMachine objects that might be found in the snapshots.
11210 *
11211 * Requires caller and locking. The machine lock must be passed in because it
11212 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11213 *
11214 * @param writeLock Machine lock from top-level caller; this gets passed to
11215 * i_detachDevice.
11216 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11217 * object if called for a SnapshotMachine.
11218 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11219 * added to llMedia; if Full, then all media get added;
11220 * otherwise no media get added.
11221 * @param llMedia Caller's list to receive Medium objects which got detached so
11222 * caller can close() them, depending on cleanupMode.
11223 * @return
11224 */
11225HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11226 Snapshot *pSnapshot,
11227 CleanupMode_T cleanupMode,
11228 MediaList &llMedia)
11229{
11230 Assert(isWriteLockOnCurrentThread());
11231
11232 HRESULT rc;
11233
11234 // make a temporary list because i_detachDevice invalidates iterators into
11235 // mMediumAttachments
11236 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11237
11238 for (MediumAttachmentList::iterator
11239 it = llAttachments2.begin();
11240 it != llAttachments2.end();
11241 ++it)
11242 {
11243 ComObjPtr<MediumAttachment> &pAttach = *it;
11244 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11245
11246 if (!pMedium.isNull())
11247 {
11248 AutoCaller mac(pMedium);
11249 if (FAILED(mac.rc())) return mac.rc();
11250 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11251 DeviceType_T devType = pMedium->i_getDeviceType();
11252 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11253 && devType == DeviceType_HardDisk)
11254 || (cleanupMode == CleanupMode_Full)
11255 )
11256 {
11257 llMedia.push_back(pMedium);
11258 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11259 /* Not allowed to keep this lock as below we need the parent
11260 * medium lock, and the lock order is parent to child. */
11261 lock.release();
11262 /*
11263 * Search for medias which are not attached to any machine, but
11264 * in the chain to an attached disk. Mediums are only consided
11265 * if they are:
11266 * - have only one child
11267 * - no references to any machines
11268 * - are of normal medium type
11269 */
11270 while (!pParent.isNull())
11271 {
11272 AutoCaller mac1(pParent);
11273 if (FAILED(mac1.rc())) return mac1.rc();
11274 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11275 if (pParent->i_getChildren().size() == 1)
11276 {
11277 if ( pParent->i_getMachineBackRefCount() == 0
11278 && pParent->i_getType() == MediumType_Normal
11279 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11280 llMedia.push_back(pParent);
11281 }
11282 else
11283 break;
11284 pParent = pParent->i_getParent();
11285 }
11286 }
11287 }
11288
11289 // real machine: then we need to use the proper method
11290 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11291
11292 if (FAILED(rc))
11293 return rc;
11294 }
11295
11296 return S_OK;
11297}
11298
11299/**
11300 * Perform deferred hard disk detachments.
11301 *
11302 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11303 * changed (not backed up).
11304 *
11305 * If @a aOnline is @c true then this method will also unlock the old hard
11306 * disks for which the new implicit diffs were created and will lock these new
11307 * diffs for writing.
11308 *
11309 * @param aOnline Whether the VM was online prior to this operation.
11310 *
11311 * @note Locks this object for writing!
11312 */
11313void Machine::i_commitMedia(bool aOnline /*= false*/)
11314{
11315 AutoCaller autoCaller(this);
11316 AssertComRCReturnVoid(autoCaller.rc());
11317
11318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11319
11320 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11321
11322 HRESULT rc = S_OK;
11323
11324 /* no attach/detach operations -- nothing to do */
11325 if (!mMediumAttachments.isBackedUp())
11326 return;
11327
11328 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11329 bool fMediaNeedsLocking = false;
11330
11331 /* enumerate new attachments */
11332 for (MediumAttachmentList::const_iterator
11333 it = mMediumAttachments->begin();
11334 it != mMediumAttachments->end();
11335 ++it)
11336 {
11337 MediumAttachment *pAttach = *it;
11338
11339 pAttach->i_commit();
11340
11341 Medium *pMedium = pAttach->i_getMedium();
11342 bool fImplicit = pAttach->i_isImplicit();
11343
11344 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11345 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11346 fImplicit));
11347
11348 /** @todo convert all this Machine-based voodoo to MediumAttachment
11349 * based commit logic. */
11350 if (fImplicit)
11351 {
11352 /* convert implicit attachment to normal */
11353 pAttach->i_setImplicit(false);
11354
11355 if ( aOnline
11356 && pMedium
11357 && pAttach->i_getType() == DeviceType_HardDisk
11358 )
11359 {
11360 /* update the appropriate lock list */
11361 MediumLockList *pMediumLockList;
11362 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11363 AssertComRC(rc);
11364 if (pMediumLockList)
11365 {
11366 /* unlock if there's a need to change the locking */
11367 if (!fMediaNeedsLocking)
11368 {
11369 rc = mData->mSession.mLockedMedia.Unlock();
11370 AssertComRC(rc);
11371 fMediaNeedsLocking = true;
11372 }
11373 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11374 AssertComRC(rc);
11375 rc = pMediumLockList->Append(pMedium, true);
11376 AssertComRC(rc);
11377 }
11378 }
11379
11380 continue;
11381 }
11382
11383 if (pMedium)
11384 {
11385 /* was this medium attached before? */
11386 for (MediumAttachmentList::iterator
11387 oldIt = oldAtts.begin();
11388 oldIt != oldAtts.end();
11389 ++oldIt)
11390 {
11391 MediumAttachment *pOldAttach = *oldIt;
11392 if (pOldAttach->i_getMedium() == pMedium)
11393 {
11394 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11395
11396 /* yes: remove from old to avoid de-association */
11397 oldAtts.erase(oldIt);
11398 break;
11399 }
11400 }
11401 }
11402 }
11403
11404 /* enumerate remaining old attachments and de-associate from the
11405 * current machine state */
11406 for (MediumAttachmentList::const_iterator
11407 it = oldAtts.begin();
11408 it != oldAtts.end();
11409 ++it)
11410 {
11411 MediumAttachment *pAttach = *it;
11412 Medium *pMedium = pAttach->i_getMedium();
11413
11414 /* Detach only hard disks, since DVD/floppy media is detached
11415 * instantly in MountMedium. */
11416 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11417 {
11418 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11419
11420 /* now de-associate from the current machine state */
11421 rc = pMedium->i_removeBackReference(mData->mUuid);
11422 AssertComRC(rc);
11423
11424 if (aOnline)
11425 {
11426 /* unlock since medium is not used anymore */
11427 MediumLockList *pMediumLockList;
11428 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11429 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11430 {
11431 /* this happens for online snapshots, there the attachment
11432 * is changing, but only to a diff image created under
11433 * the old one, so there is no separate lock list */
11434 Assert(!pMediumLockList);
11435 }
11436 else
11437 {
11438 AssertComRC(rc);
11439 if (pMediumLockList)
11440 {
11441 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11442 AssertComRC(rc);
11443 }
11444 }
11445 }
11446 }
11447 }
11448
11449 /* take media locks again so that the locking state is consistent */
11450 if (fMediaNeedsLocking)
11451 {
11452 Assert(aOnline);
11453 rc = mData->mSession.mLockedMedia.Lock();
11454 AssertComRC(rc);
11455 }
11456
11457 /* commit the hard disk changes */
11458 mMediumAttachments.commit();
11459
11460 if (i_isSessionMachine())
11461 {
11462 /*
11463 * Update the parent machine to point to the new owner.
11464 * This is necessary because the stored parent will point to the
11465 * session machine otherwise and cause crashes or errors later
11466 * when the session machine gets invalid.
11467 */
11468 /** @todo Change the MediumAttachment class to behave like any other
11469 * class in this regard by creating peer MediumAttachment
11470 * objects for session machines and share the data with the peer
11471 * machine.
11472 */
11473 for (MediumAttachmentList::const_iterator
11474 it = mMediumAttachments->begin();
11475 it != mMediumAttachments->end();
11476 ++it)
11477 (*it)->i_updateParentMachine(mPeer);
11478
11479 /* attach new data to the primary machine and reshare it */
11480 mPeer->mMediumAttachments.attach(mMediumAttachments);
11481 }
11482
11483 return;
11484}
11485
11486/**
11487 * Perform deferred deletion of implicitly created diffs.
11488 *
11489 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11490 * changed (not backed up).
11491 *
11492 * @note Locks this object for writing!
11493 */
11494void Machine::i_rollbackMedia()
11495{
11496 AutoCaller autoCaller(this);
11497 AssertComRCReturnVoid(autoCaller.rc());
11498
11499 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11500 LogFlowThisFunc(("Entering rollbackMedia\n"));
11501
11502 HRESULT rc = S_OK;
11503
11504 /* no attach/detach operations -- nothing to do */
11505 if (!mMediumAttachments.isBackedUp())
11506 return;
11507
11508 /* enumerate new attachments */
11509 for (MediumAttachmentList::const_iterator
11510 it = mMediumAttachments->begin();
11511 it != mMediumAttachments->end();
11512 ++it)
11513 {
11514 MediumAttachment *pAttach = *it;
11515 /* Fix up the backrefs for DVD/floppy media. */
11516 if (pAttach->i_getType() != DeviceType_HardDisk)
11517 {
11518 Medium *pMedium = pAttach->i_getMedium();
11519 if (pMedium)
11520 {
11521 rc = pMedium->i_removeBackReference(mData->mUuid);
11522 AssertComRC(rc);
11523 }
11524 }
11525
11526 (*it)->i_rollback();
11527
11528 pAttach = *it;
11529 /* Fix up the backrefs for DVD/floppy media. */
11530 if (pAttach->i_getType() != DeviceType_HardDisk)
11531 {
11532 Medium *pMedium = pAttach->i_getMedium();
11533 if (pMedium)
11534 {
11535 rc = pMedium->i_addBackReference(mData->mUuid);
11536 AssertComRC(rc);
11537 }
11538 }
11539 }
11540
11541 /** @todo convert all this Machine-based voodoo to MediumAttachment
11542 * based rollback logic. */
11543 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11544
11545 return;
11546}
11547
11548/**
11549 * Returns true if the settings file is located in the directory named exactly
11550 * as the machine; this means, among other things, that the machine directory
11551 * should be auto-renamed.
11552 *
11553 * @param aSettingsDir if not NULL, the full machine settings file directory
11554 * name will be assigned there.
11555 *
11556 * @note Doesn't lock anything.
11557 * @note Not thread safe (must be called from this object's lock).
11558 */
11559bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11560{
11561 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11562 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11563 if (aSettingsDir)
11564 *aSettingsDir = strMachineDirName;
11565 strMachineDirName.stripPath(); // vmname
11566 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11567 strConfigFileOnly.stripPath() // vmname.vbox
11568 .stripSuffix(); // vmname
11569 /** @todo hack, make somehow use of ComposeMachineFilename */
11570 if (mUserData->s.fDirectoryIncludesUUID)
11571 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11572
11573 AssertReturn(!strMachineDirName.isEmpty(), false);
11574 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11575
11576 return strMachineDirName == strConfigFileOnly;
11577}
11578
11579/**
11580 * Discards all changes to machine settings.
11581 *
11582 * @param aNotify Whether to notify the direct session about changes or not.
11583 *
11584 * @note Locks objects for writing!
11585 */
11586void Machine::i_rollback(bool aNotify)
11587{
11588 AutoCaller autoCaller(this);
11589 AssertComRCReturn(autoCaller.rc(), (void)0);
11590
11591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11592
11593 if (!mStorageControllers.isNull())
11594 {
11595 if (mStorageControllers.isBackedUp())
11596 {
11597 /* unitialize all new devices (absent in the backed up list). */
11598 StorageControllerList *backedList = mStorageControllers.backedUpData();
11599 for (StorageControllerList::const_iterator
11600 it = mStorageControllers->begin();
11601 it != mStorageControllers->end();
11602 ++it)
11603 {
11604 if ( std::find(backedList->begin(), backedList->end(), *it)
11605 == backedList->end()
11606 )
11607 {
11608 (*it)->uninit();
11609 }
11610 }
11611
11612 /* restore the list */
11613 mStorageControllers.rollback();
11614 }
11615
11616 /* rollback any changes to devices after restoring the list */
11617 if (mData->flModifications & IsModified_Storage)
11618 {
11619 for (StorageControllerList::const_iterator
11620 it = mStorageControllers->begin();
11621 it != mStorageControllers->end();
11622 ++it)
11623 {
11624 (*it)->i_rollback();
11625 }
11626 }
11627 }
11628
11629 if (!mUSBControllers.isNull())
11630 {
11631 if (mUSBControllers.isBackedUp())
11632 {
11633 /* unitialize all new devices (absent in the backed up list). */
11634 USBControllerList *backedList = mUSBControllers.backedUpData();
11635 for (USBControllerList::const_iterator
11636 it = mUSBControllers->begin();
11637 it != mUSBControllers->end();
11638 ++it)
11639 {
11640 if ( std::find(backedList->begin(), backedList->end(), *it)
11641 == backedList->end()
11642 )
11643 {
11644 (*it)->uninit();
11645 }
11646 }
11647
11648 /* restore the list */
11649 mUSBControllers.rollback();
11650 }
11651
11652 /* rollback any changes to devices after restoring the list */
11653 if (mData->flModifications & IsModified_USB)
11654 {
11655 for (USBControllerList::const_iterator
11656 it = mUSBControllers->begin();
11657 it != mUSBControllers->end();
11658 ++it)
11659 {
11660 (*it)->i_rollback();
11661 }
11662 }
11663 }
11664
11665 mUserData.rollback();
11666
11667 mHWData.rollback();
11668
11669 if (mData->flModifications & IsModified_Storage)
11670 i_rollbackMedia();
11671
11672 if (mBIOSSettings)
11673 mBIOSSettings->i_rollback();
11674
11675 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11676 mRecordingSettings->i_rollback();
11677
11678 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11679 mGraphicsAdapter->i_rollback();
11680
11681 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11682 mVRDEServer->i_rollback();
11683
11684 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11685 mAudioAdapter->i_rollback();
11686
11687 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11688 mUSBDeviceFilters->i_rollback();
11689
11690 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11691 mBandwidthControl->i_rollback();
11692
11693 if (!mHWData.isNull())
11694 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11695 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11696 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11697 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11698
11699 if (mData->flModifications & IsModified_NetworkAdapters)
11700 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11701 if ( mNetworkAdapters[slot]
11702 && mNetworkAdapters[slot]->i_isModified())
11703 {
11704 mNetworkAdapters[slot]->i_rollback();
11705 networkAdapters[slot] = mNetworkAdapters[slot];
11706 }
11707
11708 if (mData->flModifications & IsModified_SerialPorts)
11709 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11710 if ( mSerialPorts[slot]
11711 && mSerialPorts[slot]->i_isModified())
11712 {
11713 mSerialPorts[slot]->i_rollback();
11714 serialPorts[slot] = mSerialPorts[slot];
11715 }
11716
11717 if (mData->flModifications & IsModified_ParallelPorts)
11718 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11719 if ( mParallelPorts[slot]
11720 && mParallelPorts[slot]->i_isModified())
11721 {
11722 mParallelPorts[slot]->i_rollback();
11723 parallelPorts[slot] = mParallelPorts[slot];
11724 }
11725
11726 if (aNotify)
11727 {
11728 /* inform the direct session about changes */
11729
11730 ComObjPtr<Machine> that = this;
11731 uint32_t flModifications = mData->flModifications;
11732 alock.release();
11733
11734 if (flModifications & IsModified_SharedFolders)
11735 that->i_onSharedFolderChange();
11736
11737 if (flModifications & IsModified_VRDEServer)
11738 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11739 if (flModifications & IsModified_USB)
11740 that->i_onUSBControllerChange();
11741
11742 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11743 if (networkAdapters[slot])
11744 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11745 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11746 if (serialPorts[slot])
11747 that->i_onSerialPortChange(serialPorts[slot]);
11748 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11749 if (parallelPorts[slot])
11750 that->i_onParallelPortChange(parallelPorts[slot]);
11751
11752 if (flModifications & IsModified_Storage)
11753 {
11754 for (StorageControllerList::const_iterator
11755 it = mStorageControllers->begin();
11756 it != mStorageControllers->end();
11757 ++it)
11758 {
11759 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11760 }
11761 }
11762
11763
11764#if 0
11765 if (flModifications & IsModified_BandwidthControl)
11766 that->onBandwidthControlChange();
11767#endif
11768 }
11769}
11770
11771/**
11772 * Commits all the changes to machine settings.
11773 *
11774 * Note that this operation is supposed to never fail.
11775 *
11776 * @note Locks this object and children for writing.
11777 */
11778void Machine::i_commit()
11779{
11780 AutoCaller autoCaller(this);
11781 AssertComRCReturnVoid(autoCaller.rc());
11782
11783 AutoCaller peerCaller(mPeer);
11784 AssertComRCReturnVoid(peerCaller.rc());
11785
11786 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11787
11788 /*
11789 * use safe commit to ensure Snapshot machines (that share mUserData)
11790 * will still refer to a valid memory location
11791 */
11792 mUserData.commitCopy();
11793
11794 mHWData.commit();
11795
11796 if (mMediumAttachments.isBackedUp())
11797 i_commitMedia(Global::IsOnline(mData->mMachineState));
11798
11799 mBIOSSettings->i_commit();
11800 mRecordingSettings->i_commit();
11801 mGraphicsAdapter->i_commit();
11802 mVRDEServer->i_commit();
11803 mAudioAdapter->i_commit();
11804 mUSBDeviceFilters->i_commit();
11805 mBandwidthControl->i_commit();
11806
11807 /* Since mNetworkAdapters is a list which might have been changed (resized)
11808 * without using the Backupable<> template we need to handle the copying
11809 * of the list entries manually, including the creation of peers for the
11810 * new objects. */
11811 bool commitNetworkAdapters = false;
11812 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11813 if (mPeer)
11814 {
11815 /* commit everything, even the ones which will go away */
11816 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11817 mNetworkAdapters[slot]->i_commit();
11818 /* copy over the new entries, creating a peer and uninit the original */
11819 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11820 for (size_t slot = 0; slot < newSize; slot++)
11821 {
11822 /* look if this adapter has a peer device */
11823 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11824 if (!peer)
11825 {
11826 /* no peer means the adapter is a newly created one;
11827 * create a peer owning data this data share it with */
11828 peer.createObject();
11829 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11830 }
11831 mPeer->mNetworkAdapters[slot] = peer;
11832 }
11833 /* uninit any no longer needed network adapters */
11834 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11835 mNetworkAdapters[slot]->uninit();
11836 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11837 {
11838 if (mPeer->mNetworkAdapters[slot])
11839 mPeer->mNetworkAdapters[slot]->uninit();
11840 }
11841 /* Keep the original network adapter count until this point, so that
11842 * discarding a chipset type change will not lose settings. */
11843 mNetworkAdapters.resize(newSize);
11844 mPeer->mNetworkAdapters.resize(newSize);
11845 }
11846 else
11847 {
11848 /* we have no peer (our parent is the newly created machine);
11849 * just commit changes to the network adapters */
11850 commitNetworkAdapters = true;
11851 }
11852 if (commitNetworkAdapters)
11853 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11854 mNetworkAdapters[slot]->i_commit();
11855
11856 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11857 mSerialPorts[slot]->i_commit();
11858 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11859 mParallelPorts[slot]->i_commit();
11860
11861 bool commitStorageControllers = false;
11862
11863 if (mStorageControllers.isBackedUp())
11864 {
11865 mStorageControllers.commit();
11866
11867 if (mPeer)
11868 {
11869 /* Commit all changes to new controllers (this will reshare data with
11870 * peers for those who have peers) */
11871 StorageControllerList *newList = new StorageControllerList();
11872 for (StorageControllerList::const_iterator
11873 it = mStorageControllers->begin();
11874 it != mStorageControllers->end();
11875 ++it)
11876 {
11877 (*it)->i_commit();
11878
11879 /* look if this controller has a peer device */
11880 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11881 if (!peer)
11882 {
11883 /* no peer means the device is a newly created one;
11884 * create a peer owning data this device share it with */
11885 peer.createObject();
11886 peer->init(mPeer, *it, true /* aReshare */);
11887 }
11888 else
11889 {
11890 /* remove peer from the old list */
11891 mPeer->mStorageControllers->remove(peer);
11892 }
11893 /* and add it to the new list */
11894 newList->push_back(peer);
11895 }
11896
11897 /* uninit old peer's controllers that are left */
11898 for (StorageControllerList::const_iterator
11899 it = mPeer->mStorageControllers->begin();
11900 it != mPeer->mStorageControllers->end();
11901 ++it)
11902 {
11903 (*it)->uninit();
11904 }
11905
11906 /* attach new list of controllers to our peer */
11907 mPeer->mStorageControllers.attach(newList);
11908 }
11909 else
11910 {
11911 /* we have no peer (our parent is the newly created machine);
11912 * just commit changes to devices */
11913 commitStorageControllers = true;
11914 }
11915 }
11916 else
11917 {
11918 /* the list of controllers itself is not changed,
11919 * just commit changes to controllers themselves */
11920 commitStorageControllers = true;
11921 }
11922
11923 if (commitStorageControllers)
11924 {
11925 for (StorageControllerList::const_iterator
11926 it = mStorageControllers->begin();
11927 it != mStorageControllers->end();
11928 ++it)
11929 {
11930 (*it)->i_commit();
11931 }
11932 }
11933
11934 bool commitUSBControllers = false;
11935
11936 if (mUSBControllers.isBackedUp())
11937 {
11938 mUSBControllers.commit();
11939
11940 if (mPeer)
11941 {
11942 /* Commit all changes to new controllers (this will reshare data with
11943 * peers for those who have peers) */
11944 USBControllerList *newList = new USBControllerList();
11945 for (USBControllerList::const_iterator
11946 it = mUSBControllers->begin();
11947 it != mUSBControllers->end();
11948 ++it)
11949 {
11950 (*it)->i_commit();
11951
11952 /* look if this controller has a peer device */
11953 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11954 if (!peer)
11955 {
11956 /* no peer means the device is a newly created one;
11957 * create a peer owning data this device share it with */
11958 peer.createObject();
11959 peer->init(mPeer, *it, true /* aReshare */);
11960 }
11961 else
11962 {
11963 /* remove peer from the old list */
11964 mPeer->mUSBControllers->remove(peer);
11965 }
11966 /* and add it to the new list */
11967 newList->push_back(peer);
11968 }
11969
11970 /* uninit old peer's controllers that are left */
11971 for (USBControllerList::const_iterator
11972 it = mPeer->mUSBControllers->begin();
11973 it != mPeer->mUSBControllers->end();
11974 ++it)
11975 {
11976 (*it)->uninit();
11977 }
11978
11979 /* attach new list of controllers to our peer */
11980 mPeer->mUSBControllers.attach(newList);
11981 }
11982 else
11983 {
11984 /* we have no peer (our parent is the newly created machine);
11985 * just commit changes to devices */
11986 commitUSBControllers = true;
11987 }
11988 }
11989 else
11990 {
11991 /* the list of controllers itself is not changed,
11992 * just commit changes to controllers themselves */
11993 commitUSBControllers = true;
11994 }
11995
11996 if (commitUSBControllers)
11997 {
11998 for (USBControllerList::const_iterator
11999 it = mUSBControllers->begin();
12000 it != mUSBControllers->end();
12001 ++it)
12002 {
12003 (*it)->i_commit();
12004 }
12005 }
12006
12007 if (i_isSessionMachine())
12008 {
12009 /* attach new data to the primary machine and reshare it */
12010 mPeer->mUserData.attach(mUserData);
12011 mPeer->mHWData.attach(mHWData);
12012 /* mmMediumAttachments is reshared by fixupMedia */
12013 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12014 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12015 }
12016}
12017
12018/**
12019 * Copies all the hardware data from the given machine.
12020 *
12021 * Currently, only called when the VM is being restored from a snapshot. In
12022 * particular, this implies that the VM is not running during this method's
12023 * call.
12024 *
12025 * @note This method must be called from under this object's lock.
12026 *
12027 * @note This method doesn't call #i_commit(), so all data remains backed up and
12028 * unsaved.
12029 */
12030void Machine::i_copyFrom(Machine *aThat)
12031{
12032 AssertReturnVoid(!i_isSnapshotMachine());
12033 AssertReturnVoid(aThat->i_isSnapshotMachine());
12034
12035 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12036
12037 mHWData.assignCopy(aThat->mHWData);
12038
12039 // create copies of all shared folders (mHWData after attaching a copy
12040 // contains just references to original objects)
12041 for (HWData::SharedFolderList::iterator
12042 it = mHWData->mSharedFolders.begin();
12043 it != mHWData->mSharedFolders.end();
12044 ++it)
12045 {
12046 ComObjPtr<SharedFolder> folder;
12047 folder.createObject();
12048 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12049 AssertComRC(rc);
12050 *it = folder;
12051 }
12052
12053 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12054 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12055 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12056 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12057 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12058 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12059 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12060
12061 /* create private copies of all controllers */
12062 mStorageControllers.backup();
12063 mStorageControllers->clear();
12064 for (StorageControllerList::const_iterator
12065 it = aThat->mStorageControllers->begin();
12066 it != aThat->mStorageControllers->end();
12067 ++it)
12068 {
12069 ComObjPtr<StorageController> ctrl;
12070 ctrl.createObject();
12071 ctrl->initCopy(this, *it);
12072 mStorageControllers->push_back(ctrl);
12073 }
12074
12075 /* create private copies of all USB controllers */
12076 mUSBControllers.backup();
12077 mUSBControllers->clear();
12078 for (USBControllerList::const_iterator
12079 it = aThat->mUSBControllers->begin();
12080 it != aThat->mUSBControllers->end();
12081 ++it)
12082 {
12083 ComObjPtr<USBController> ctrl;
12084 ctrl.createObject();
12085 ctrl->initCopy(this, *it);
12086 mUSBControllers->push_back(ctrl);
12087 }
12088
12089 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12090 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12091 {
12092 if (mNetworkAdapters[slot].isNotNull())
12093 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12094 else
12095 {
12096 unconst(mNetworkAdapters[slot]).createObject();
12097 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12098 }
12099 }
12100 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12101 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12102 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12103 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12104}
12105
12106/**
12107 * Returns whether the given storage controller is hotplug capable.
12108 *
12109 * @returns true if the controller supports hotplugging
12110 * false otherwise.
12111 * @param enmCtrlType The controller type to check for.
12112 */
12113bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12114{
12115 ComPtr<ISystemProperties> systemProperties;
12116 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12117 if (FAILED(rc))
12118 return false;
12119
12120 BOOL aHotplugCapable = FALSE;
12121 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12122
12123 return RT_BOOL(aHotplugCapable);
12124}
12125
12126#ifdef VBOX_WITH_RESOURCE_USAGE_API
12127
12128void Machine::i_getDiskList(MediaList &list)
12129{
12130 for (MediumAttachmentList::const_iterator
12131 it = mMediumAttachments->begin();
12132 it != mMediumAttachments->end();
12133 ++it)
12134 {
12135 MediumAttachment *pAttach = *it;
12136 /* just in case */
12137 AssertContinue(pAttach);
12138
12139 AutoCaller localAutoCallerA(pAttach);
12140 if (FAILED(localAutoCallerA.rc())) continue;
12141
12142 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12143
12144 if (pAttach->i_getType() == DeviceType_HardDisk)
12145 list.push_back(pAttach->i_getMedium());
12146 }
12147}
12148
12149void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12150{
12151 AssertReturnVoid(isWriteLockOnCurrentThread());
12152 AssertPtrReturnVoid(aCollector);
12153
12154 pm::CollectorHAL *hal = aCollector->getHAL();
12155 /* Create sub metrics */
12156 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12157 "Percentage of processor time spent in user mode by the VM process.");
12158 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12159 "Percentage of processor time spent in kernel mode by the VM process.");
12160 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12161 "Size of resident portion of VM process in memory.");
12162 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12163 "Actual size of all VM disks combined.");
12164 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12165 "Network receive rate.");
12166 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12167 "Network transmit rate.");
12168 /* Create and register base metrics */
12169 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12170 cpuLoadUser, cpuLoadKernel);
12171 aCollector->registerBaseMetric(cpuLoad);
12172 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12173 ramUsageUsed);
12174 aCollector->registerBaseMetric(ramUsage);
12175 MediaList disks;
12176 i_getDiskList(disks);
12177 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12178 diskUsageUsed);
12179 aCollector->registerBaseMetric(diskUsage);
12180
12181 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12182 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12183 new pm::AggregateAvg()));
12184 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12185 new pm::AggregateMin()));
12186 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12187 new pm::AggregateMax()));
12188 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12189 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12190 new pm::AggregateAvg()));
12191 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12192 new pm::AggregateMin()));
12193 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12194 new pm::AggregateMax()));
12195
12196 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12197 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12198 new pm::AggregateAvg()));
12199 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12200 new pm::AggregateMin()));
12201 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12202 new pm::AggregateMax()));
12203
12204 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12205 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12206 new pm::AggregateAvg()));
12207 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12208 new pm::AggregateMin()));
12209 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12210 new pm::AggregateMax()));
12211
12212
12213 /* Guest metrics collector */
12214 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12215 aCollector->registerGuest(mCollectorGuest);
12216 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12217
12218 /* Create sub metrics */
12219 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12220 "Percentage of processor time spent in user mode as seen by the guest.");
12221 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12222 "Percentage of processor time spent in kernel mode as seen by the guest.");
12223 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12224 "Percentage of processor time spent idling as seen by the guest.");
12225
12226 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12227 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12228 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12229 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12230 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12231 pm::SubMetric *guestMemCache = new pm::SubMetric(
12232 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12233
12234 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12235 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12236
12237 /* Create and register base metrics */
12238 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12239 machineNetRx, machineNetTx);
12240 aCollector->registerBaseMetric(machineNetRate);
12241
12242 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12243 guestLoadUser, guestLoadKernel, guestLoadIdle);
12244 aCollector->registerBaseMetric(guestCpuLoad);
12245
12246 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12247 guestMemTotal, guestMemFree,
12248 guestMemBalloon, guestMemShared,
12249 guestMemCache, guestPagedTotal);
12250 aCollector->registerBaseMetric(guestCpuMem);
12251
12252 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12253 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12256
12257 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12258 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12261
12262 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12263 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12264 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12265 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12266
12267 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12268 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12269 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12270 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12271
12272 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12273 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12274 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12275 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12276
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12281
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12284 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12286
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12289 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12291
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12294 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12295 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12296
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12299 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12304 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12306}
12307
12308void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12309{
12310 AssertReturnVoid(isWriteLockOnCurrentThread());
12311
12312 if (aCollector)
12313 {
12314 aCollector->unregisterMetricsFor(aMachine);
12315 aCollector->unregisterBaseMetricsFor(aMachine);
12316 }
12317}
12318
12319#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12320
12321
12322////////////////////////////////////////////////////////////////////////////////
12323
12324DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12325
12326HRESULT SessionMachine::FinalConstruct()
12327{
12328 LogFlowThisFunc(("\n"));
12329
12330 mClientToken = NULL;
12331
12332 return BaseFinalConstruct();
12333}
12334
12335void SessionMachine::FinalRelease()
12336{
12337 LogFlowThisFunc(("\n"));
12338
12339 Assert(!mClientToken);
12340 /* paranoia, should not hang around any more */
12341 if (mClientToken)
12342 {
12343 delete mClientToken;
12344 mClientToken = NULL;
12345 }
12346
12347 uninit(Uninit::Unexpected);
12348
12349 BaseFinalRelease();
12350}
12351
12352/**
12353 * @note Must be called only by Machine::LockMachine() from its own write lock.
12354 */
12355HRESULT SessionMachine::init(Machine *aMachine)
12356{
12357 LogFlowThisFuncEnter();
12358 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12359
12360 AssertReturn(aMachine, E_INVALIDARG);
12361
12362 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12363
12364 /* Enclose the state transition NotReady->InInit->Ready */
12365 AutoInitSpan autoInitSpan(this);
12366 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12367
12368 HRESULT rc = S_OK;
12369
12370 RT_ZERO(mAuthLibCtx);
12371
12372 /* create the machine client token */
12373 try
12374 {
12375 mClientToken = new ClientToken(aMachine, this);
12376 if (!mClientToken->isReady())
12377 {
12378 delete mClientToken;
12379 mClientToken = NULL;
12380 rc = E_FAIL;
12381 }
12382 }
12383 catch (std::bad_alloc &)
12384 {
12385 rc = E_OUTOFMEMORY;
12386 }
12387 if (FAILED(rc))
12388 return rc;
12389
12390 /* memorize the peer Machine */
12391 unconst(mPeer) = aMachine;
12392 /* share the parent pointer */
12393 unconst(mParent) = aMachine->mParent;
12394
12395 /* take the pointers to data to share */
12396 mData.share(aMachine->mData);
12397 mSSData.share(aMachine->mSSData);
12398
12399 mUserData.share(aMachine->mUserData);
12400 mHWData.share(aMachine->mHWData);
12401 mMediumAttachments.share(aMachine->mMediumAttachments);
12402
12403 mStorageControllers.allocate();
12404 for (StorageControllerList::const_iterator
12405 it = aMachine->mStorageControllers->begin();
12406 it != aMachine->mStorageControllers->end();
12407 ++it)
12408 {
12409 ComObjPtr<StorageController> ctl;
12410 ctl.createObject();
12411 ctl->init(this, *it);
12412 mStorageControllers->push_back(ctl);
12413 }
12414
12415 mUSBControllers.allocate();
12416 for (USBControllerList::const_iterator
12417 it = aMachine->mUSBControllers->begin();
12418 it != aMachine->mUSBControllers->end();
12419 ++it)
12420 {
12421 ComObjPtr<USBController> ctl;
12422 ctl.createObject();
12423 ctl->init(this, *it);
12424 mUSBControllers->push_back(ctl);
12425 }
12426
12427 unconst(mBIOSSettings).createObject();
12428 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12429 unconst(mRecordingSettings).createObject();
12430 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12431 /* create another GraphicsAdapter object that will be mutable */
12432 unconst(mGraphicsAdapter).createObject();
12433 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12434 /* create another VRDEServer object that will be mutable */
12435 unconst(mVRDEServer).createObject();
12436 mVRDEServer->init(this, aMachine->mVRDEServer);
12437 /* create another audio adapter object that will be mutable */
12438 unconst(mAudioAdapter).createObject();
12439 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12440 /* create a list of serial ports that will be mutable */
12441 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12442 {
12443 unconst(mSerialPorts[slot]).createObject();
12444 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12445 }
12446 /* create a list of parallel ports that will be mutable */
12447 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12448 {
12449 unconst(mParallelPorts[slot]).createObject();
12450 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12451 }
12452
12453 /* create another USB device filters object that will be mutable */
12454 unconst(mUSBDeviceFilters).createObject();
12455 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12456
12457 /* create a list of network adapters that will be mutable */
12458 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12459 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12460 {
12461 unconst(mNetworkAdapters[slot]).createObject();
12462 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12463 }
12464
12465 /* create another bandwidth control object that will be mutable */
12466 unconst(mBandwidthControl).createObject();
12467 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12468
12469 /* default is to delete saved state on Saved -> PoweredOff transition */
12470 mRemoveSavedState = true;
12471
12472 /* Confirm a successful initialization when it's the case */
12473 autoInitSpan.setSucceeded();
12474
12475 miNATNetworksStarted = 0;
12476
12477 LogFlowThisFuncLeave();
12478 return rc;
12479}
12480
12481/**
12482 * Uninitializes this session object. If the reason is other than
12483 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12484 * or the client watcher code.
12485 *
12486 * @param aReason uninitialization reason
12487 *
12488 * @note Locks mParent + this object for writing.
12489 */
12490void SessionMachine::uninit(Uninit::Reason aReason)
12491{
12492 LogFlowThisFuncEnter();
12493 LogFlowThisFunc(("reason=%d\n", aReason));
12494
12495 /*
12496 * Strongly reference ourselves to prevent this object deletion after
12497 * mData->mSession.mMachine.setNull() below (which can release the last
12498 * reference and call the destructor). Important: this must be done before
12499 * accessing any members (and before AutoUninitSpan that does it as well).
12500 * This self reference will be released as the very last step on return.
12501 */
12502 ComObjPtr<SessionMachine> selfRef;
12503 if (aReason != Uninit::Unexpected)
12504 selfRef = this;
12505
12506 /* Enclose the state transition Ready->InUninit->NotReady */
12507 AutoUninitSpan autoUninitSpan(this);
12508 if (autoUninitSpan.uninitDone())
12509 {
12510 LogFlowThisFunc(("Already uninitialized\n"));
12511 LogFlowThisFuncLeave();
12512 return;
12513 }
12514
12515 if (autoUninitSpan.initFailed())
12516 {
12517 /* We've been called by init() because it's failed. It's not really
12518 * necessary (nor it's safe) to perform the regular uninit sequence
12519 * below, the following is enough.
12520 */
12521 LogFlowThisFunc(("Initialization failed.\n"));
12522 /* destroy the machine client token */
12523 if (mClientToken)
12524 {
12525 delete mClientToken;
12526 mClientToken = NULL;
12527 }
12528 uninitDataAndChildObjects();
12529 mData.free();
12530 unconst(mParent) = NULL;
12531 unconst(mPeer) = NULL;
12532 LogFlowThisFuncLeave();
12533 return;
12534 }
12535
12536 MachineState_T lastState;
12537 {
12538 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12539 lastState = mData->mMachineState;
12540 }
12541 NOREF(lastState);
12542
12543#ifdef VBOX_WITH_USB
12544 // release all captured USB devices, but do this before requesting the locks below
12545 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12546 {
12547 /* Console::captureUSBDevices() is called in the VM process only after
12548 * setting the machine state to Starting or Restoring.
12549 * Console::detachAllUSBDevices() will be called upon successful
12550 * termination. So, we need to release USB devices only if there was
12551 * an abnormal termination of a running VM.
12552 *
12553 * This is identical to SessionMachine::DetachAllUSBDevices except
12554 * for the aAbnormal argument. */
12555 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12556 AssertComRC(rc);
12557 NOREF(rc);
12558
12559 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12560 if (service)
12561 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12562 }
12563#endif /* VBOX_WITH_USB */
12564
12565 // we need to lock this object in uninit() because the lock is shared
12566 // with mPeer (as well as data we modify below). mParent lock is needed
12567 // by several calls to it.
12568 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12569
12570#ifdef VBOX_WITH_RESOURCE_USAGE_API
12571 /*
12572 * It is safe to call Machine::i_unregisterMetrics() here because
12573 * PerformanceCollector::samplerCallback no longer accesses guest methods
12574 * holding the lock.
12575 */
12576 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12577 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12578 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12579 if (mCollectorGuest)
12580 {
12581 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12582 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12583 mCollectorGuest = NULL;
12584 }
12585#endif
12586
12587 if (aReason == Uninit::Abnormal)
12588 {
12589 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12590
12591 /* reset the state to Aborted */
12592 if (mData->mMachineState != MachineState_Aborted)
12593 i_setMachineState(MachineState_Aborted);
12594 }
12595
12596 // any machine settings modified?
12597 if (mData->flModifications)
12598 {
12599 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12600 i_rollback(false /* aNotify */);
12601 }
12602
12603 mData->mSession.mPID = NIL_RTPROCESS;
12604
12605 if (aReason == Uninit::Unexpected)
12606 {
12607 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12608 * client watcher thread to update the set of machines that have open
12609 * sessions. */
12610 mParent->i_updateClientWatcher();
12611 }
12612
12613 /* uninitialize all remote controls */
12614 if (mData->mSession.mRemoteControls.size())
12615 {
12616 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12617 mData->mSession.mRemoteControls.size()));
12618
12619 /* Always restart a the beginning, since the iterator is invalidated
12620 * by using erase(). */
12621 for (Data::Session::RemoteControlList::iterator
12622 it = mData->mSession.mRemoteControls.begin();
12623 it != mData->mSession.mRemoteControls.end();
12624 it = mData->mSession.mRemoteControls.begin())
12625 {
12626 ComPtr<IInternalSessionControl> pControl = *it;
12627 mData->mSession.mRemoteControls.erase(it);
12628 multilock.release();
12629 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12630 HRESULT rc = pControl->Uninitialize();
12631 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12632 if (FAILED(rc))
12633 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12634 multilock.acquire();
12635 }
12636 mData->mSession.mRemoteControls.clear();
12637 }
12638
12639 /* Remove all references to the NAT network service. The service will stop
12640 * if all references (also from other VMs) are removed. */
12641 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12642 {
12643 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12644 {
12645 BOOL enabled;
12646 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12647 if ( FAILED(hrc)
12648 || !enabled)
12649 continue;
12650
12651 NetworkAttachmentType_T type;
12652 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12653 if ( SUCCEEDED(hrc)
12654 && type == NetworkAttachmentType_NATNetwork)
12655 {
12656 Bstr name;
12657 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12658 if (SUCCEEDED(hrc))
12659 {
12660 multilock.release();
12661 Utf8Str strName(name);
12662 LogRel(("VM '%s' stops using NAT network '%s'\n",
12663 mUserData->s.strName.c_str(), strName.c_str()));
12664 mParent->i_natNetworkRefDec(strName);
12665 multilock.acquire();
12666 }
12667 }
12668 }
12669 }
12670
12671 /*
12672 * An expected uninitialization can come only from #i_checkForDeath().
12673 * Otherwise it means that something's gone really wrong (for example,
12674 * the Session implementation has released the VirtualBox reference
12675 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12676 * etc). However, it's also possible, that the client releases the IPC
12677 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12678 * but the VirtualBox release event comes first to the server process.
12679 * This case is practically possible, so we should not assert on an
12680 * unexpected uninit, just log a warning.
12681 */
12682
12683 if (aReason == Uninit::Unexpected)
12684 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12685
12686 if (aReason != Uninit::Normal)
12687 {
12688 mData->mSession.mDirectControl.setNull();
12689 }
12690 else
12691 {
12692 /* this must be null here (see #OnSessionEnd()) */
12693 Assert(mData->mSession.mDirectControl.isNull());
12694 Assert(mData->mSession.mState == SessionState_Unlocking);
12695 Assert(!mData->mSession.mProgress.isNull());
12696 }
12697 if (mData->mSession.mProgress)
12698 {
12699 if (aReason == Uninit::Normal)
12700 mData->mSession.mProgress->i_notifyComplete(S_OK);
12701 else
12702 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12703 COM_IIDOF(ISession),
12704 getComponentName(),
12705 tr("The VM session was aborted"));
12706 mData->mSession.mProgress.setNull();
12707 }
12708
12709 if (mConsoleTaskData.mProgress)
12710 {
12711 Assert(aReason == Uninit::Abnormal);
12712 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12713 COM_IIDOF(ISession),
12714 getComponentName(),
12715 tr("The VM session was aborted"));
12716 mConsoleTaskData.mProgress.setNull();
12717 }
12718
12719 /* remove the association between the peer machine and this session machine */
12720 Assert( (SessionMachine*)mData->mSession.mMachine == this
12721 || aReason == Uninit::Unexpected);
12722
12723 /* reset the rest of session data */
12724 mData->mSession.mLockType = LockType_Null;
12725 mData->mSession.mMachine.setNull();
12726 mData->mSession.mState = SessionState_Unlocked;
12727 mData->mSession.mName.setNull();
12728
12729 /* destroy the machine client token before leaving the exclusive lock */
12730 if (mClientToken)
12731 {
12732 delete mClientToken;
12733 mClientToken = NULL;
12734 }
12735
12736 /* fire an event */
12737 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12738
12739 uninitDataAndChildObjects();
12740
12741 /* free the essential data structure last */
12742 mData.free();
12743
12744 /* release the exclusive lock before setting the below two to NULL */
12745 multilock.release();
12746
12747 unconst(mParent) = NULL;
12748 unconst(mPeer) = NULL;
12749
12750 AuthLibUnload(&mAuthLibCtx);
12751
12752 LogFlowThisFuncLeave();
12753}
12754
12755// util::Lockable interface
12756////////////////////////////////////////////////////////////////////////////////
12757
12758/**
12759 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12760 * with the primary Machine instance (mPeer).
12761 */
12762RWLockHandle *SessionMachine::lockHandle() const
12763{
12764 AssertReturn(mPeer != NULL, NULL);
12765 return mPeer->lockHandle();
12766}
12767
12768// IInternalMachineControl methods
12769////////////////////////////////////////////////////////////////////////////////
12770
12771/**
12772 * Passes collected guest statistics to performance collector object
12773 */
12774HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12775 ULONG aCpuKernel, ULONG aCpuIdle,
12776 ULONG aMemTotal, ULONG aMemFree,
12777 ULONG aMemBalloon, ULONG aMemShared,
12778 ULONG aMemCache, ULONG aPageTotal,
12779 ULONG aAllocVMM, ULONG aFreeVMM,
12780 ULONG aBalloonedVMM, ULONG aSharedVMM,
12781 ULONG aVmNetRx, ULONG aVmNetTx)
12782{
12783#ifdef VBOX_WITH_RESOURCE_USAGE_API
12784 if (mCollectorGuest)
12785 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12786 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12787 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12788 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12789
12790 return S_OK;
12791#else
12792 NOREF(aValidStats);
12793 NOREF(aCpuUser);
12794 NOREF(aCpuKernel);
12795 NOREF(aCpuIdle);
12796 NOREF(aMemTotal);
12797 NOREF(aMemFree);
12798 NOREF(aMemBalloon);
12799 NOREF(aMemShared);
12800 NOREF(aMemCache);
12801 NOREF(aPageTotal);
12802 NOREF(aAllocVMM);
12803 NOREF(aFreeVMM);
12804 NOREF(aBalloonedVMM);
12805 NOREF(aSharedVMM);
12806 NOREF(aVmNetRx);
12807 NOREF(aVmNetTx);
12808 return E_NOTIMPL;
12809#endif
12810}
12811
12812////////////////////////////////////////////////////////////////////////////////
12813//
12814// SessionMachine task records
12815//
12816////////////////////////////////////////////////////////////////////////////////
12817
12818/**
12819 * Task record for saving the machine state.
12820 */
12821class SessionMachine::SaveStateTask
12822 : public Machine::Task
12823{
12824public:
12825 SaveStateTask(SessionMachine *m,
12826 Progress *p,
12827 const Utf8Str &t,
12828 Reason_T enmReason,
12829 const Utf8Str &strStateFilePath)
12830 : Task(m, p, t),
12831 m_enmReason(enmReason),
12832 m_strStateFilePath(strStateFilePath)
12833 {}
12834
12835private:
12836 void handler()
12837 {
12838 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12839 }
12840
12841 Reason_T m_enmReason;
12842 Utf8Str m_strStateFilePath;
12843
12844 friend class SessionMachine;
12845};
12846
12847/**
12848 * Task thread implementation for SessionMachine::SaveState(), called from
12849 * SessionMachine::taskHandler().
12850 *
12851 * @note Locks this object for writing.
12852 *
12853 * @param task
12854 * @return
12855 */
12856void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12857{
12858 LogFlowThisFuncEnter();
12859
12860 AutoCaller autoCaller(this);
12861 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12862 if (FAILED(autoCaller.rc()))
12863 {
12864 /* we might have been uninitialized because the session was accidentally
12865 * closed by the client, so don't assert */
12866 HRESULT rc = setError(E_FAIL,
12867 tr("The session has been accidentally closed"));
12868 task.m_pProgress->i_notifyComplete(rc);
12869 LogFlowThisFuncLeave();
12870 return;
12871 }
12872
12873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12874
12875 HRESULT rc = S_OK;
12876
12877 try
12878 {
12879 ComPtr<IInternalSessionControl> directControl;
12880 if (mData->mSession.mLockType == LockType_VM)
12881 directControl = mData->mSession.mDirectControl;
12882 if (directControl.isNull())
12883 throw setError(VBOX_E_INVALID_VM_STATE,
12884 tr("Trying to save state without a running VM"));
12885 alock.release();
12886 BOOL fSuspendedBySave;
12887 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12888 Assert(!fSuspendedBySave);
12889 alock.acquire();
12890
12891 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12892 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12893 throw E_FAIL);
12894
12895 if (SUCCEEDED(rc))
12896 {
12897 mSSData->strStateFilePath = task.m_strStateFilePath;
12898
12899 /* save all VM settings */
12900 rc = i_saveSettings(NULL);
12901 // no need to check whether VirtualBox.xml needs saving also since
12902 // we can't have a name change pending at this point
12903 }
12904 else
12905 {
12906 // On failure, set the state to the state we had at the beginning.
12907 i_setMachineState(task.m_machineStateBackup);
12908 i_updateMachineStateOnClient();
12909
12910 // Delete the saved state file (might have been already created).
12911 // No need to check whether this is shared with a snapshot here
12912 // because we certainly created a fresh saved state file here.
12913 RTFileDelete(task.m_strStateFilePath.c_str());
12914 }
12915 }
12916 catch (HRESULT aRC) { rc = aRC; }
12917
12918 task.m_pProgress->i_notifyComplete(rc);
12919
12920 LogFlowThisFuncLeave();
12921}
12922
12923/**
12924 * @note Locks this object for writing.
12925 */
12926HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12927{
12928 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12929}
12930
12931HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12932{
12933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12934
12935 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12936 if (FAILED(rc)) return rc;
12937
12938 if ( mData->mMachineState != MachineState_Running
12939 && mData->mMachineState != MachineState_Paused
12940 )
12941 return setError(VBOX_E_INVALID_VM_STATE,
12942 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12943 Global::stringifyMachineState(mData->mMachineState));
12944
12945 ComObjPtr<Progress> pProgress;
12946 pProgress.createObject();
12947 rc = pProgress->init(i_getVirtualBox(),
12948 static_cast<IMachine *>(this) /* aInitiator */,
12949 tr("Saving the execution state of the virtual machine"),
12950 FALSE /* aCancelable */);
12951 if (FAILED(rc))
12952 return rc;
12953
12954 Utf8Str strStateFilePath;
12955 i_composeSavedStateFilename(strStateFilePath);
12956
12957 /* create and start the task on a separate thread (note that it will not
12958 * start working until we release alock) */
12959 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12960 rc = pTask->createThread();
12961 if (FAILED(rc))
12962 return rc;
12963
12964 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12965 i_setMachineState(MachineState_Saving);
12966 i_updateMachineStateOnClient();
12967
12968 pProgress.queryInterfaceTo(aProgress.asOutParam());
12969
12970 return S_OK;
12971}
12972
12973/**
12974 * @note Locks this object for writing.
12975 */
12976HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12977{
12978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12979
12980 HRESULT rc = i_checkStateDependency(MutableStateDep);
12981 if (FAILED(rc)) return rc;
12982
12983 if ( mData->mMachineState != MachineState_PoweredOff
12984 && mData->mMachineState != MachineState_Teleported
12985 && mData->mMachineState != MachineState_Aborted
12986 )
12987 return setError(VBOX_E_INVALID_VM_STATE,
12988 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12989 Global::stringifyMachineState(mData->mMachineState));
12990
12991 com::Utf8Str stateFilePathFull;
12992 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12993 if (RT_FAILURE(vrc))
12994 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12995 tr("Invalid saved state file path '%s' (%Rrc)"),
12996 aSavedStateFile.c_str(),
12997 vrc);
12998
12999 mSSData->strStateFilePath = stateFilePathFull;
13000
13001 /* The below i_setMachineState() will detect the state transition and will
13002 * update the settings file */
13003
13004 return i_setMachineState(MachineState_Saved);
13005}
13006
13007/**
13008 * @note Locks this object for writing.
13009 */
13010HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13011{
13012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13013
13014 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13015 if (FAILED(rc)) return rc;
13016
13017 if (mData->mMachineState != MachineState_Saved)
13018 return setError(VBOX_E_INVALID_VM_STATE,
13019 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13020 Global::stringifyMachineState(mData->mMachineState));
13021
13022 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13023
13024 /*
13025 * Saved -> PoweredOff transition will be detected in the SessionMachine
13026 * and properly handled.
13027 */
13028 rc = i_setMachineState(MachineState_PoweredOff);
13029 return rc;
13030}
13031
13032
13033/**
13034 * @note Locks the same as #i_setMachineState() does.
13035 */
13036HRESULT SessionMachine::updateState(MachineState_T aState)
13037{
13038 return i_setMachineState(aState);
13039}
13040
13041/**
13042 * @note Locks this object for writing.
13043 */
13044HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13045{
13046 IProgress *pProgress(aProgress);
13047
13048 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13049
13050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13051
13052 if (mData->mSession.mState != SessionState_Locked)
13053 return VBOX_E_INVALID_OBJECT_STATE;
13054
13055 if (!mData->mSession.mProgress.isNull())
13056 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13057
13058 /* If we didn't reference the NAT network service yet, add a reference to
13059 * force a start */
13060 if (miNATNetworksStarted < 1)
13061 {
13062 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13063 {
13064 BOOL enabled;
13065 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13066 if ( FAILED(hrc)
13067 || !enabled)
13068 continue;
13069
13070 NetworkAttachmentType_T type;
13071 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13072 if ( SUCCEEDED(hrc)
13073 && type == NetworkAttachmentType_NATNetwork)
13074 {
13075 Bstr name;
13076 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13077 if (SUCCEEDED(hrc))
13078 {
13079 Utf8Str strName(name);
13080 LogRel(("VM '%s' starts using NAT network '%s'\n",
13081 mUserData->s.strName.c_str(), strName.c_str()));
13082 mPeer->lockHandle()->unlockWrite();
13083 mParent->i_natNetworkRefInc(strName);
13084#ifdef RT_LOCK_STRICT
13085 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13086#else
13087 mPeer->lockHandle()->lockWrite();
13088#endif
13089 }
13090 }
13091 }
13092 miNATNetworksStarted++;
13093 }
13094
13095 LogFlowThisFunc(("returns S_OK.\n"));
13096 return S_OK;
13097}
13098
13099/**
13100 * @note Locks this object for writing.
13101 */
13102HRESULT SessionMachine::endPowerUp(LONG aResult)
13103{
13104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13105
13106 if (mData->mSession.mState != SessionState_Locked)
13107 return VBOX_E_INVALID_OBJECT_STATE;
13108
13109 /* Finalize the LaunchVMProcess progress object. */
13110 if (mData->mSession.mProgress)
13111 {
13112 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13113 mData->mSession.mProgress.setNull();
13114 }
13115
13116 if (SUCCEEDED((HRESULT)aResult))
13117 {
13118#ifdef VBOX_WITH_RESOURCE_USAGE_API
13119 /* The VM has been powered up successfully, so it makes sense
13120 * now to offer the performance metrics for a running machine
13121 * object. Doing it earlier wouldn't be safe. */
13122 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13123 mData->mSession.mPID);
13124#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13125 }
13126
13127 return S_OK;
13128}
13129
13130/**
13131 * @note Locks this object for writing.
13132 */
13133HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13134{
13135 LogFlowThisFuncEnter();
13136
13137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13138
13139 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13140 E_FAIL);
13141
13142 /* create a progress object to track operation completion */
13143 ComObjPtr<Progress> pProgress;
13144 pProgress.createObject();
13145 pProgress->init(i_getVirtualBox(),
13146 static_cast<IMachine *>(this) /* aInitiator */,
13147 tr("Stopping the virtual machine"),
13148 FALSE /* aCancelable */);
13149
13150 /* fill in the console task data */
13151 mConsoleTaskData.mLastState = mData->mMachineState;
13152 mConsoleTaskData.mProgress = pProgress;
13153
13154 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13155 i_setMachineState(MachineState_Stopping);
13156
13157 pProgress.queryInterfaceTo(aProgress.asOutParam());
13158
13159 return S_OK;
13160}
13161
13162/**
13163 * @note Locks this object for writing.
13164 */
13165HRESULT SessionMachine::endPoweringDown(LONG aResult,
13166 const com::Utf8Str &aErrMsg)
13167{
13168 HRESULT const hrcResult = (HRESULT)aResult;
13169 LogFlowThisFuncEnter();
13170
13171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13172
13173 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13174 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13175 && mConsoleTaskData.mLastState != MachineState_Null,
13176 E_FAIL);
13177
13178 /*
13179 * On failure, set the state to the state we had when BeginPoweringDown()
13180 * was called (this is expected by Console::PowerDown() and the associated
13181 * task). On success the VM process already changed the state to
13182 * MachineState_PoweredOff, so no need to do anything.
13183 */
13184 if (FAILED(hrcResult))
13185 i_setMachineState(mConsoleTaskData.mLastState);
13186
13187 /* notify the progress object about operation completion */
13188 Assert(mConsoleTaskData.mProgress);
13189 if (SUCCEEDED(hrcResult))
13190 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13191 else
13192 {
13193 if (aErrMsg.length())
13194 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13195 COM_IIDOF(ISession),
13196 getComponentName(),
13197 aErrMsg.c_str());
13198 else
13199 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13200 }
13201
13202 /* clear out the temporary saved state data */
13203 mConsoleTaskData.mLastState = MachineState_Null;
13204 mConsoleTaskData.mProgress.setNull();
13205
13206 LogFlowThisFuncLeave();
13207 return S_OK;
13208}
13209
13210
13211/**
13212 * Goes through the USB filters of the given machine to see if the given
13213 * device matches any filter or not.
13214 *
13215 * @note Locks the same as USBController::hasMatchingFilter() does.
13216 */
13217HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13218 BOOL *aMatched,
13219 ULONG *aMaskedInterfaces)
13220{
13221 LogFlowThisFunc(("\n"));
13222
13223#ifdef VBOX_WITH_USB
13224 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13225#else
13226 NOREF(aDevice);
13227 NOREF(aMaskedInterfaces);
13228 *aMatched = FALSE;
13229#endif
13230
13231 return S_OK;
13232}
13233
13234/**
13235 * @note Locks the same as Host::captureUSBDevice() does.
13236 */
13237HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13238{
13239 LogFlowThisFunc(("\n"));
13240
13241#ifdef VBOX_WITH_USB
13242 /* if captureDeviceForVM() fails, it must have set extended error info */
13243 clearError();
13244 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13245 if (FAILED(rc)) return rc;
13246
13247 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13248 AssertReturn(service, E_FAIL);
13249 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13250#else
13251 RT_NOREF(aId, aCaptureFilename);
13252 return E_NOTIMPL;
13253#endif
13254}
13255
13256/**
13257 * @note Locks the same as Host::detachUSBDevice() does.
13258 */
13259HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13260 BOOL aDone)
13261{
13262 LogFlowThisFunc(("\n"));
13263
13264#ifdef VBOX_WITH_USB
13265 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13266 AssertReturn(service, E_FAIL);
13267 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13268#else
13269 NOREF(aId);
13270 NOREF(aDone);
13271 return E_NOTIMPL;
13272#endif
13273}
13274
13275/**
13276 * Inserts all machine filters to the USB proxy service and then calls
13277 * Host::autoCaptureUSBDevices().
13278 *
13279 * Called by Console from the VM process upon VM startup.
13280 *
13281 * @note Locks what called methods lock.
13282 */
13283HRESULT SessionMachine::autoCaptureUSBDevices()
13284{
13285 LogFlowThisFunc(("\n"));
13286
13287#ifdef VBOX_WITH_USB
13288 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13289 AssertComRC(rc);
13290 NOREF(rc);
13291
13292 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13293 AssertReturn(service, E_FAIL);
13294 return service->autoCaptureDevicesForVM(this);
13295#else
13296 return S_OK;
13297#endif
13298}
13299
13300/**
13301 * Removes all machine filters from the USB proxy service and then calls
13302 * Host::detachAllUSBDevices().
13303 *
13304 * Called by Console from the VM process upon normal VM termination or by
13305 * SessionMachine::uninit() upon abnormal VM termination (from under the
13306 * Machine/SessionMachine lock).
13307 *
13308 * @note Locks what called methods lock.
13309 */
13310HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13311{
13312 LogFlowThisFunc(("\n"));
13313
13314#ifdef VBOX_WITH_USB
13315 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13316 AssertComRC(rc);
13317 NOREF(rc);
13318
13319 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13320 AssertReturn(service, E_FAIL);
13321 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13322#else
13323 NOREF(aDone);
13324 return S_OK;
13325#endif
13326}
13327
13328/**
13329 * @note Locks this object for writing.
13330 */
13331HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13332 ComPtr<IProgress> &aProgress)
13333{
13334 LogFlowThisFuncEnter();
13335
13336 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13337 /*
13338 * We don't assert below because it might happen that a non-direct session
13339 * informs us it is closed right after we've been uninitialized -- it's ok.
13340 */
13341
13342 /* get IInternalSessionControl interface */
13343 ComPtr<IInternalSessionControl> control(aSession);
13344
13345 ComAssertRet(!control.isNull(), E_INVALIDARG);
13346
13347 /* Creating a Progress object requires the VirtualBox lock, and
13348 * thus locking it here is required by the lock order rules. */
13349 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13350
13351 if (control == mData->mSession.mDirectControl)
13352 {
13353 /* The direct session is being normally closed by the client process
13354 * ----------------------------------------------------------------- */
13355
13356 /* go to the closing state (essential for all open*Session() calls and
13357 * for #i_checkForDeath()) */
13358 Assert(mData->mSession.mState == SessionState_Locked);
13359 mData->mSession.mState = SessionState_Unlocking;
13360
13361 /* set direct control to NULL to release the remote instance */
13362 mData->mSession.mDirectControl.setNull();
13363 LogFlowThisFunc(("Direct control is set to NULL\n"));
13364
13365 if (mData->mSession.mProgress)
13366 {
13367 /* finalize the progress, someone might wait if a frontend
13368 * closes the session before powering on the VM. */
13369 mData->mSession.mProgress->notifyComplete(E_FAIL,
13370 COM_IIDOF(ISession),
13371 getComponentName(),
13372 tr("The VM session was closed before any attempt to power it on"));
13373 mData->mSession.mProgress.setNull();
13374 }
13375
13376 /* Create the progress object the client will use to wait until
13377 * #i_checkForDeath() is called to uninitialize this session object after
13378 * it releases the IPC semaphore.
13379 * Note! Because we're "reusing" mProgress here, this must be a proxy
13380 * object just like for LaunchVMProcess. */
13381 Assert(mData->mSession.mProgress.isNull());
13382 ComObjPtr<ProgressProxy> progress;
13383 progress.createObject();
13384 ComPtr<IUnknown> pPeer(mPeer);
13385 progress->init(mParent, pPeer,
13386 Bstr(tr("Closing session")).raw(),
13387 FALSE /* aCancelable */);
13388 progress.queryInterfaceTo(aProgress.asOutParam());
13389 mData->mSession.mProgress = progress;
13390 }
13391 else
13392 {
13393 /* the remote session is being normally closed */
13394 bool found = false;
13395 for (Data::Session::RemoteControlList::iterator
13396 it = mData->mSession.mRemoteControls.begin();
13397 it != mData->mSession.mRemoteControls.end();
13398 ++it)
13399 {
13400 if (control == *it)
13401 {
13402 found = true;
13403 // This MUST be erase(it), not remove(*it) as the latter
13404 // triggers a very nasty use after free due to the place where
13405 // the value "lives".
13406 mData->mSession.mRemoteControls.erase(it);
13407 break;
13408 }
13409 }
13410 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13411 E_INVALIDARG);
13412 }
13413
13414 /* signal the client watcher thread, because the client is going away */
13415 mParent->i_updateClientWatcher();
13416
13417 LogFlowThisFuncLeave();
13418 return S_OK;
13419}
13420
13421HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13422 std::vector<com::Utf8Str> &aValues,
13423 std::vector<LONG64> &aTimestamps,
13424 std::vector<com::Utf8Str> &aFlags)
13425{
13426 LogFlowThisFunc(("\n"));
13427
13428#ifdef VBOX_WITH_GUEST_PROPS
13429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13430
13431 size_t cEntries = mHWData->mGuestProperties.size();
13432 aNames.resize(cEntries);
13433 aValues.resize(cEntries);
13434 aTimestamps.resize(cEntries);
13435 aFlags.resize(cEntries);
13436
13437 size_t i = 0;
13438 for (HWData::GuestPropertyMap::const_iterator
13439 it = mHWData->mGuestProperties.begin();
13440 it != mHWData->mGuestProperties.end();
13441 ++it, ++i)
13442 {
13443 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13444 aNames[i] = it->first;
13445 aValues[i] = it->second.strValue;
13446 aTimestamps[i] = it->second.mTimestamp;
13447
13448 /* If it is NULL, keep it NULL. */
13449 if (it->second.mFlags)
13450 {
13451 GuestPropWriteFlags(it->second.mFlags, szFlags);
13452 aFlags[i] = szFlags;
13453 }
13454 else
13455 aFlags[i] = "";
13456 }
13457 return S_OK;
13458#else
13459 ReturnComNotImplemented();
13460#endif
13461}
13462
13463HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13464 const com::Utf8Str &aValue,
13465 LONG64 aTimestamp,
13466 const com::Utf8Str &aFlags)
13467{
13468 LogFlowThisFunc(("\n"));
13469
13470#ifdef VBOX_WITH_GUEST_PROPS
13471 try
13472 {
13473 /*
13474 * Convert input up front.
13475 */
13476 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13477 if (aFlags.length())
13478 {
13479 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13480 AssertRCReturn(vrc, E_INVALIDARG);
13481 }
13482
13483 /*
13484 * Now grab the object lock, validate the state and do the update.
13485 */
13486
13487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13488
13489 if (!Global::IsOnline(mData->mMachineState))
13490 {
13491 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13492 VBOX_E_INVALID_VM_STATE);
13493 }
13494
13495 i_setModified(IsModified_MachineData);
13496 mHWData.backup();
13497
13498 bool fDelete = !aValue.length();
13499 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13500 if (it != mHWData->mGuestProperties.end())
13501 {
13502 if (!fDelete)
13503 {
13504 it->second.strValue = aValue;
13505 it->second.mTimestamp = aTimestamp;
13506 it->second.mFlags = fFlags;
13507 }
13508 else
13509 mHWData->mGuestProperties.erase(it);
13510
13511 mData->mGuestPropertiesModified = TRUE;
13512 }
13513 else if (!fDelete)
13514 {
13515 HWData::GuestProperty prop;
13516 prop.strValue = aValue;
13517 prop.mTimestamp = aTimestamp;
13518 prop.mFlags = fFlags;
13519
13520 mHWData->mGuestProperties[aName] = prop;
13521 mData->mGuestPropertiesModified = TRUE;
13522 }
13523
13524 alock.release();
13525
13526 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13527 }
13528 catch (...)
13529 {
13530 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13531 }
13532 return S_OK;
13533#else
13534 ReturnComNotImplemented();
13535#endif
13536}
13537
13538
13539HRESULT SessionMachine::lockMedia()
13540{
13541 AutoMultiWriteLock2 alock(this->lockHandle(),
13542 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13543
13544 AssertReturn( mData->mMachineState == MachineState_Starting
13545 || mData->mMachineState == MachineState_Restoring
13546 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13547
13548 clearError();
13549 alock.release();
13550 return i_lockMedia();
13551}
13552
13553HRESULT SessionMachine::unlockMedia()
13554{
13555 HRESULT hrc = i_unlockMedia();
13556 return hrc;
13557}
13558
13559HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13560 ComPtr<IMediumAttachment> &aNewAttachment)
13561{
13562 // request the host lock first, since might be calling Host methods for getting host drives;
13563 // next, protect the media tree all the while we're in here, as well as our member variables
13564 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13565 this->lockHandle(),
13566 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13567
13568 IMediumAttachment *iAttach = aAttachment;
13569 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13570
13571 Utf8Str ctrlName;
13572 LONG lPort;
13573 LONG lDevice;
13574 bool fTempEject;
13575 {
13576 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13577
13578 /* Need to query the details first, as the IMediumAttachment reference
13579 * might be to the original settings, which we are going to change. */
13580 ctrlName = pAttach->i_getControllerName();
13581 lPort = pAttach->i_getPort();
13582 lDevice = pAttach->i_getDevice();
13583 fTempEject = pAttach->i_getTempEject();
13584 }
13585
13586 if (!fTempEject)
13587 {
13588 /* Remember previously mounted medium. The medium before taking the
13589 * backup is not necessarily the same thing. */
13590 ComObjPtr<Medium> oldmedium;
13591 oldmedium = pAttach->i_getMedium();
13592
13593 i_setModified(IsModified_Storage);
13594 mMediumAttachments.backup();
13595
13596 // The backup operation makes the pAttach reference point to the
13597 // old settings. Re-get the correct reference.
13598 pAttach = i_findAttachment(*mMediumAttachments.data(),
13599 ctrlName,
13600 lPort,
13601 lDevice);
13602
13603 {
13604 AutoCaller autoAttachCaller(this);
13605 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13606
13607 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13608 if (!oldmedium.isNull())
13609 oldmedium->i_removeBackReference(mData->mUuid);
13610
13611 pAttach->i_updateMedium(NULL);
13612 pAttach->i_updateEjected();
13613 }
13614
13615 i_setModified(IsModified_Storage);
13616 }
13617 else
13618 {
13619 {
13620 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13621 pAttach->i_updateEjected();
13622 }
13623 }
13624
13625 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13626
13627 return S_OK;
13628}
13629
13630HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13631 com::Utf8Str &aResult)
13632{
13633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13634
13635 HRESULT hr = S_OK;
13636
13637 if (!mAuthLibCtx.hAuthLibrary)
13638 {
13639 /* Load the external authentication library. */
13640 Bstr authLibrary;
13641 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13642
13643 Utf8Str filename = authLibrary;
13644
13645 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13646 if (RT_FAILURE(vrc))
13647 hr = setErrorBoth(E_FAIL, vrc,
13648 tr("Could not load the external authentication library '%s' (%Rrc)"),
13649 filename.c_str(), vrc);
13650 }
13651
13652 /* The auth library might need the machine lock. */
13653 alock.release();
13654
13655 if (FAILED(hr))
13656 return hr;
13657
13658 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13659 {
13660 enum VRDEAuthParams
13661 {
13662 parmUuid = 1,
13663 parmGuestJudgement,
13664 parmUser,
13665 parmPassword,
13666 parmDomain,
13667 parmClientId
13668 };
13669
13670 AuthResult result = AuthResultAccessDenied;
13671
13672 Guid uuid(aAuthParams[parmUuid]);
13673 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13674 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13675
13676 result = AuthLibAuthenticate(&mAuthLibCtx,
13677 uuid.raw(), guestJudgement,
13678 aAuthParams[parmUser].c_str(),
13679 aAuthParams[parmPassword].c_str(),
13680 aAuthParams[parmDomain].c_str(),
13681 u32ClientId);
13682
13683 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13684 size_t cbPassword = aAuthParams[parmPassword].length();
13685 if (cbPassword)
13686 {
13687 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13688 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13689 }
13690
13691 if (result == AuthResultAccessGranted)
13692 aResult = "granted";
13693 else
13694 aResult = "denied";
13695
13696 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13697 aAuthParams[parmUser].c_str(), aResult.c_str()));
13698 }
13699 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13700 {
13701 enum VRDEAuthDisconnectParams
13702 {
13703 parmUuid = 1,
13704 parmClientId
13705 };
13706
13707 Guid uuid(aAuthParams[parmUuid]);
13708 uint32_t u32ClientId = 0;
13709 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13710 }
13711 else
13712 {
13713 hr = E_INVALIDARG;
13714 }
13715
13716 return hr;
13717}
13718
13719// public methods only for internal purposes
13720/////////////////////////////////////////////////////////////////////////////
13721
13722#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13723/**
13724 * Called from the client watcher thread to check for expected or unexpected
13725 * death of the client process that has a direct session to this machine.
13726 *
13727 * On Win32 and on OS/2, this method is called only when we've got the
13728 * mutex (i.e. the client has either died or terminated normally) so it always
13729 * returns @c true (the client is terminated, the session machine is
13730 * uninitialized).
13731 *
13732 * On other platforms, the method returns @c true if the client process has
13733 * terminated normally or abnormally and the session machine was uninitialized,
13734 * and @c false if the client process is still alive.
13735 *
13736 * @note Locks this object for writing.
13737 */
13738bool SessionMachine::i_checkForDeath()
13739{
13740 Uninit::Reason reason;
13741 bool terminated = false;
13742
13743 /* Enclose autoCaller with a block because calling uninit() from under it
13744 * will deadlock. */
13745 {
13746 AutoCaller autoCaller(this);
13747 if (!autoCaller.isOk())
13748 {
13749 /* return true if not ready, to cause the client watcher to exclude
13750 * the corresponding session from watching */
13751 LogFlowThisFunc(("Already uninitialized!\n"));
13752 return true;
13753 }
13754
13755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13756
13757 /* Determine the reason of death: if the session state is Closing here,
13758 * everything is fine. Otherwise it means that the client did not call
13759 * OnSessionEnd() before it released the IPC semaphore. This may happen
13760 * either because the client process has abnormally terminated, or
13761 * because it simply forgot to call ISession::Close() before exiting. We
13762 * threat the latter also as an abnormal termination (see
13763 * Session::uninit() for details). */
13764 reason = mData->mSession.mState == SessionState_Unlocking ?
13765 Uninit::Normal :
13766 Uninit::Abnormal;
13767
13768 if (mClientToken)
13769 terminated = mClientToken->release();
13770 } /* AutoCaller block */
13771
13772 if (terminated)
13773 uninit(reason);
13774
13775 return terminated;
13776}
13777
13778void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13779{
13780 LogFlowThisFunc(("\n"));
13781
13782 strTokenId.setNull();
13783
13784 AutoCaller autoCaller(this);
13785 AssertComRCReturnVoid(autoCaller.rc());
13786
13787 Assert(mClientToken);
13788 if (mClientToken)
13789 mClientToken->getId(strTokenId);
13790}
13791#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13792IToken *SessionMachine::i_getToken()
13793{
13794 LogFlowThisFunc(("\n"));
13795
13796 AutoCaller autoCaller(this);
13797 AssertComRCReturn(autoCaller.rc(), NULL);
13798
13799 Assert(mClientToken);
13800 if (mClientToken)
13801 return mClientToken->getToken();
13802 else
13803 return NULL;
13804}
13805#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13806
13807Machine::ClientToken *SessionMachine::i_getClientToken()
13808{
13809 LogFlowThisFunc(("\n"));
13810
13811 AutoCaller autoCaller(this);
13812 AssertComRCReturn(autoCaller.rc(), NULL);
13813
13814 return mClientToken;
13815}
13816
13817
13818/**
13819 * @note Locks this object for reading.
13820 */
13821HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13822{
13823 LogFlowThisFunc(("\n"));
13824
13825 AutoCaller autoCaller(this);
13826 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13827
13828 ComPtr<IInternalSessionControl> directControl;
13829 {
13830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13831 if (mData->mSession.mLockType == LockType_VM)
13832 directControl = mData->mSession.mDirectControl;
13833 }
13834
13835 /* ignore notifications sent after #OnSessionEnd() is called */
13836 if (!directControl)
13837 return S_OK;
13838
13839 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13840}
13841
13842/**
13843 * @note Locks this object for reading.
13844 */
13845HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13846 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13847 const Utf8Str &aGuestIp, LONG aGuestPort)
13848{
13849 LogFlowThisFunc(("\n"));
13850
13851 AutoCaller autoCaller(this);
13852 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13853
13854 ComPtr<IInternalSessionControl> directControl;
13855 {
13856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13857 if (mData->mSession.mLockType == LockType_VM)
13858 directControl = mData->mSession.mDirectControl;
13859 }
13860
13861 /* ignore notifications sent after #OnSessionEnd() is called */
13862 if (!directControl)
13863 return S_OK;
13864 /*
13865 * instead acting like callback we ask IVirtualBox deliver corresponding event
13866 */
13867
13868 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13869 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13870 return S_OK;
13871}
13872
13873/**
13874 * @note Locks this object for reading.
13875 */
13876HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13877{
13878 LogFlowThisFunc(("\n"));
13879
13880 AutoCaller autoCaller(this);
13881 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13882
13883 ComPtr<IInternalSessionControl> directControl;
13884 {
13885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13886 if (mData->mSession.mLockType == LockType_VM)
13887 directControl = mData->mSession.mDirectControl;
13888 }
13889
13890 /* ignore notifications sent after #OnSessionEnd() is called */
13891 if (!directControl)
13892 return S_OK;
13893
13894 return directControl->OnAudioAdapterChange(audioAdapter);
13895}
13896
13897/**
13898 * @note Locks this object for reading.
13899 */
13900HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13901{
13902 LogFlowThisFunc(("\n"));
13903
13904 AutoCaller autoCaller(this);
13905 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13906
13907 ComPtr<IInternalSessionControl> directControl;
13908 {
13909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13910 if (mData->mSession.mLockType == LockType_VM)
13911 directControl = mData->mSession.mDirectControl;
13912 }
13913
13914 /* ignore notifications sent after #OnSessionEnd() is called */
13915 if (!directControl)
13916 return S_OK;
13917
13918 return directControl->OnSerialPortChange(serialPort);
13919}
13920
13921/**
13922 * @note Locks this object for reading.
13923 */
13924HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13925{
13926 LogFlowThisFunc(("\n"));
13927
13928 AutoCaller autoCaller(this);
13929 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13930
13931 ComPtr<IInternalSessionControl> directControl;
13932 {
13933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13934 if (mData->mSession.mLockType == LockType_VM)
13935 directControl = mData->mSession.mDirectControl;
13936 }
13937
13938 /* ignore notifications sent after #OnSessionEnd() is called */
13939 if (!directControl)
13940 return S_OK;
13941
13942 return directControl->OnParallelPortChange(parallelPort);
13943}
13944
13945/**
13946 * @note Locks this object for reading.
13947 */
13948HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 AutoCaller autoCaller(this);
13953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13954
13955 ComPtr<IInternalSessionControl> directControl;
13956 {
13957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13958 if (mData->mSession.mLockType == LockType_VM)
13959 directControl = mData->mSession.mDirectControl;
13960 }
13961
13962 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13963
13964 /* ignore notifications sent after #OnSessionEnd() is called */
13965 if (!directControl)
13966 return S_OK;
13967
13968 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13969}
13970
13971/**
13972 * @note Locks this object for reading.
13973 */
13974HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 if (mData->mSession.mLockType == LockType_VM)
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 mParent->i_onMediumChanged(aAttachment);
13989
13990 /* ignore notifications sent after #OnSessionEnd() is called */
13991 if (!directControl)
13992 return S_OK;
13993
13994 return directControl->OnMediumChange(aAttachment, aForce);
13995}
13996
13997HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
13998{
13999 LogFlowThisFunc(("\n"));
14000
14001 AutoCaller autoCaller(this);
14002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14003
14004 ComPtr<IInternalSessionControl> directControl;
14005 {
14006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14007 if (mData->mSession.mLockType == LockType_VM)
14008 directControl = mData->mSession.mDirectControl;
14009 }
14010
14011 /* ignore notifications sent after #OnSessionEnd() is called */
14012 if (!directControl)
14013 return S_OK;
14014
14015 return directControl->OnVMProcessPriorityChange(aPriority);
14016}
14017
14018/**
14019 * @note Locks this object for reading.
14020 */
14021HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14022{
14023 LogFlowThisFunc(("\n"));
14024
14025 AutoCaller autoCaller(this);
14026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14027
14028 ComPtr<IInternalSessionControl> directControl;
14029 {
14030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14031 if (mData->mSession.mLockType == LockType_VM)
14032 directControl = mData->mSession.mDirectControl;
14033 }
14034
14035 /* ignore notifications sent after #OnSessionEnd() is called */
14036 if (!directControl)
14037 return S_OK;
14038
14039 return directControl->OnCPUChange(aCPU, aRemove);
14040}
14041
14042HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14043{
14044 LogFlowThisFunc(("\n"));
14045
14046 AutoCaller autoCaller(this);
14047 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14048
14049 ComPtr<IInternalSessionControl> directControl;
14050 {
14051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14052 if (mData->mSession.mLockType == LockType_VM)
14053 directControl = mData->mSession.mDirectControl;
14054 }
14055
14056 /* ignore notifications sent after #OnSessionEnd() is called */
14057 if (!directControl)
14058 return S_OK;
14059
14060 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14061}
14062
14063/**
14064 * @note Locks this object for reading.
14065 */
14066HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14067{
14068 LogFlowThisFunc(("\n"));
14069
14070 AutoCaller autoCaller(this);
14071 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14072
14073 ComPtr<IInternalSessionControl> directControl;
14074 {
14075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14076 if (mData->mSession.mLockType == LockType_VM)
14077 directControl = mData->mSession.mDirectControl;
14078 }
14079
14080 /* ignore notifications sent after #OnSessionEnd() is called */
14081 if (!directControl)
14082 return S_OK;
14083
14084 return directControl->OnVRDEServerChange(aRestart);
14085}
14086
14087/**
14088 * @note Locks this object for reading.
14089 */
14090HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14091{
14092 LogFlowThisFunc(("\n"));
14093
14094 AutoCaller autoCaller(this);
14095 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14096
14097 ComPtr<IInternalSessionControl> directControl;
14098 {
14099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14100 if (mData->mSession.mLockType == LockType_VM)
14101 directControl = mData->mSession.mDirectControl;
14102 }
14103
14104 /* ignore notifications sent after #OnSessionEnd() is called */
14105 if (!directControl)
14106 return S_OK;
14107
14108 return directControl->OnRecordingChange(aEnable);
14109}
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::i_onUSBControllerChange()
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 if (mData->mSession.mLockType == LockType_VM)
14125 directControl = mData->mSession.mDirectControl;
14126 }
14127
14128 /* ignore notifications sent after #OnSessionEnd() is called */
14129 if (!directControl)
14130 return S_OK;
14131
14132 return directControl->OnUSBControllerChange();
14133}
14134
14135/**
14136 * @note Locks this object for reading.
14137 */
14138HRESULT SessionMachine::i_onSharedFolderChange()
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturnRC(autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 if (mData->mSession.mLockType == LockType_VM)
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* ignore notifications sent after #OnSessionEnd() is called */
14153 if (!directControl)
14154 return S_OK;
14155
14156 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14157}
14158
14159/**
14160 * @note Locks this object for reading.
14161 */
14162HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturnRC(autoCaller.rc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 if (mData->mSession.mLockType == LockType_VM)
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179
14180 return directControl->OnClipboardModeChange(aClipboardMode);
14181}
14182
14183/**
14184 * @note Locks this object for reading.
14185 */
14186HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14187{
14188 LogFlowThisFunc(("\n"));
14189
14190 AutoCaller autoCaller(this);
14191 AssertComRCReturnRC(autoCaller.rc());
14192
14193 ComPtr<IInternalSessionControl> directControl;
14194 {
14195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14196 if (mData->mSession.mLockType == LockType_VM)
14197 directControl = mData->mSession.mDirectControl;
14198 }
14199
14200 /* ignore notifications sent after #OnSessionEnd() is called */
14201 if (!directControl)
14202 return S_OK;
14203
14204 return directControl->OnClipboardFileTransferModeChange(aEnable);
14205}
14206
14207/**
14208 * @note Locks this object for reading.
14209 */
14210HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14211{
14212 LogFlowThisFunc(("\n"));
14213
14214 AutoCaller autoCaller(this);
14215 AssertComRCReturnRC(autoCaller.rc());
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 if (mData->mSession.mLockType == LockType_VM)
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* ignore notifications sent after #OnSessionEnd() is called */
14225 if (!directControl)
14226 return S_OK;
14227
14228 return directControl->OnDnDModeChange(aDnDMode);
14229}
14230
14231/**
14232 * @note Locks this object for reading.
14233 */
14234HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 if (mData->mSession.mLockType == LockType_VM)
14245 directControl = mData->mSession.mDirectControl;
14246 }
14247
14248 /* ignore notifications sent after #OnSessionEnd() is called */
14249 if (!directControl)
14250 return S_OK;
14251
14252 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14253}
14254
14255/**
14256 * @note Locks this object for reading.
14257 */
14258HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14259{
14260 LogFlowThisFunc(("\n"));
14261
14262 AutoCaller autoCaller(this);
14263 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14264
14265 ComPtr<IInternalSessionControl> directControl;
14266 {
14267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14268 if (mData->mSession.mLockType == LockType_VM)
14269 directControl = mData->mSession.mDirectControl;
14270 }
14271
14272 /* ignore notifications sent after #OnSessionEnd() is called */
14273 if (!directControl)
14274 return S_OK;
14275
14276 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14277}
14278
14279/**
14280 * Returns @c true if this machine's USB controller reports it has a matching
14281 * filter for the given USB device and @c false otherwise.
14282 *
14283 * @note locks this object for reading.
14284 */
14285bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14286{
14287 AutoCaller autoCaller(this);
14288 /* silently return if not ready -- this method may be called after the
14289 * direct machine session has been called */
14290 if (!autoCaller.isOk())
14291 return false;
14292
14293#ifdef VBOX_WITH_USB
14294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14295
14296 switch (mData->mMachineState)
14297 {
14298 case MachineState_Starting:
14299 case MachineState_Restoring:
14300 case MachineState_TeleportingIn:
14301 case MachineState_Paused:
14302 case MachineState_Running:
14303 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14304 * elsewhere... */
14305 alock.release();
14306 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14307 default: break;
14308 }
14309#else
14310 NOREF(aDevice);
14311 NOREF(aMaskedIfs);
14312#endif
14313 return false;
14314}
14315
14316/**
14317 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14318 */
14319HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14320 IVirtualBoxErrorInfo *aError,
14321 ULONG aMaskedIfs,
14322 const com::Utf8Str &aCaptureFilename)
14323{
14324 LogFlowThisFunc(("\n"));
14325
14326 AutoCaller autoCaller(this);
14327
14328 /* This notification may happen after the machine object has been
14329 * uninitialized (the session was closed), so don't assert. */
14330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14331
14332 ComPtr<IInternalSessionControl> directControl;
14333 {
14334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14335 if (mData->mSession.mLockType == LockType_VM)
14336 directControl = mData->mSession.mDirectControl;
14337 }
14338
14339 /* fail on notifications sent after #OnSessionEnd() is called, it is
14340 * expected by the caller */
14341 if (!directControl)
14342 return E_FAIL;
14343
14344 /* No locks should be held at this point. */
14345 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14346 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14347
14348 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14349}
14350
14351/**
14352 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14353 */
14354HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14355 IVirtualBoxErrorInfo *aError)
14356{
14357 LogFlowThisFunc(("\n"));
14358
14359 AutoCaller autoCaller(this);
14360
14361 /* This notification may happen after the machine object has been
14362 * uninitialized (the session was closed), so don't assert. */
14363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14364
14365 ComPtr<IInternalSessionControl> directControl;
14366 {
14367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14368 if (mData->mSession.mLockType == LockType_VM)
14369 directControl = mData->mSession.mDirectControl;
14370 }
14371
14372 /* fail on notifications sent after #OnSessionEnd() is called, it is
14373 * expected by the caller */
14374 if (!directControl)
14375 return E_FAIL;
14376
14377 /* No locks should be held at this point. */
14378 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14379 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14380
14381 return directControl->OnUSBDeviceDetach(aId, aError);
14382}
14383
14384// protected methods
14385/////////////////////////////////////////////////////////////////////////////
14386
14387/**
14388 * Deletes the given file if it is no longer in use by either the current machine state
14389 * (if the machine is "saved") or any of the machine's snapshots.
14390 *
14391 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14392 * but is different for each SnapshotMachine. When calling this, the order of calling this
14393 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14394 * is therefore critical. I know, it's all rather messy.
14395 *
14396 * @param strStateFile
14397 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14398 * the test for whether the saved state file is in use.
14399 */
14400void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14401 Snapshot *pSnapshotToIgnore)
14402{
14403 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14404 if ( (strStateFile.isNotEmpty())
14405 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14406 )
14407 // ... and it must also not be shared with other snapshots
14408 if ( !mData->mFirstSnapshot
14409 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14410 // this checks the SnapshotMachine's state file paths
14411 )
14412 RTFileDelete(strStateFile.c_str());
14413}
14414
14415/**
14416 * Locks the attached media.
14417 *
14418 * All attached hard disks are locked for writing and DVD/floppy are locked for
14419 * reading. Parents of attached hard disks (if any) are locked for reading.
14420 *
14421 * This method also performs accessibility check of all media it locks: if some
14422 * media is inaccessible, the method will return a failure and a bunch of
14423 * extended error info objects per each inaccessible medium.
14424 *
14425 * Note that this method is atomic: if it returns a success, all media are
14426 * locked as described above; on failure no media is locked at all (all
14427 * succeeded individual locks will be undone).
14428 *
14429 * The caller is responsible for doing the necessary state sanity checks.
14430 *
14431 * The locks made by this method must be undone by calling #unlockMedia() when
14432 * no more needed.
14433 */
14434HRESULT SessionMachine::i_lockMedia()
14435{
14436 AutoCaller autoCaller(this);
14437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14438
14439 AutoMultiWriteLock2 alock(this->lockHandle(),
14440 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14441
14442 /* bail out if trying to lock things with already set up locking */
14443 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14444
14445 MultiResult mrc(S_OK);
14446
14447 /* Collect locking information for all medium objects attached to the VM. */
14448 for (MediumAttachmentList::const_iterator
14449 it = mMediumAttachments->begin();
14450 it != mMediumAttachments->end();
14451 ++it)
14452 {
14453 MediumAttachment *pAtt = *it;
14454 DeviceType_T devType = pAtt->i_getType();
14455 Medium *pMedium = pAtt->i_getMedium();
14456
14457 MediumLockList *pMediumLockList(new MediumLockList());
14458 // There can be attachments without a medium (floppy/dvd), and thus
14459 // it's impossible to create a medium lock list. It still makes sense
14460 // to have the empty medium lock list in the map in case a medium is
14461 // attached later.
14462 if (pMedium != NULL)
14463 {
14464 MediumType_T mediumType = pMedium->i_getType();
14465 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14466 || mediumType == MediumType_Shareable;
14467 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14468
14469 alock.release();
14470 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14471 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14472 false /* fMediumLockWriteAll */,
14473 NULL,
14474 *pMediumLockList);
14475 alock.acquire();
14476 if (FAILED(mrc))
14477 {
14478 delete pMediumLockList;
14479 mData->mSession.mLockedMedia.Clear();
14480 break;
14481 }
14482 }
14483
14484 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14485 if (FAILED(rc))
14486 {
14487 mData->mSession.mLockedMedia.Clear();
14488 mrc = setError(rc,
14489 tr("Collecting locking information for all attached media failed"));
14490 break;
14491 }
14492 }
14493
14494 if (SUCCEEDED(mrc))
14495 {
14496 /* Now lock all media. If this fails, nothing is locked. */
14497 alock.release();
14498 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14499 alock.acquire();
14500 if (FAILED(rc))
14501 {
14502 mrc = setError(rc,
14503 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14504 }
14505 }
14506
14507 return mrc;
14508}
14509
14510/**
14511 * Undoes the locks made by by #lockMedia().
14512 */
14513HRESULT SessionMachine::i_unlockMedia()
14514{
14515 AutoCaller autoCaller(this);
14516 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14517
14518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14519
14520 /* we may be holding important error info on the current thread;
14521 * preserve it */
14522 ErrorInfoKeeper eik;
14523
14524 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14525 AssertComRC(rc);
14526 return rc;
14527}
14528
14529/**
14530 * Helper to change the machine state (reimplementation).
14531 *
14532 * @note Locks this object for writing.
14533 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14534 * it can cause crashes in random places due to unexpectedly committing
14535 * the current settings. The caller is responsible for that. The call
14536 * to saveStateSettings is fine, because this method does not commit.
14537 */
14538HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14539{
14540 LogFlowThisFuncEnter();
14541 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14542
14543 AutoCaller autoCaller(this);
14544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14545
14546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14547
14548 MachineState_T oldMachineState = mData->mMachineState;
14549
14550 AssertMsgReturn(oldMachineState != aMachineState,
14551 ("oldMachineState=%s, aMachineState=%s\n",
14552 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14553 E_FAIL);
14554
14555 HRESULT rc = S_OK;
14556
14557 int stsFlags = 0;
14558 bool deleteSavedState = false;
14559
14560 /* detect some state transitions */
14561
14562 if ( ( oldMachineState == MachineState_Saved
14563 && aMachineState == MachineState_Restoring)
14564 || ( ( oldMachineState == MachineState_PoweredOff
14565 || oldMachineState == MachineState_Teleported
14566 || oldMachineState == MachineState_Aborted
14567 )
14568 && ( aMachineState == MachineState_TeleportingIn
14569 || aMachineState == MachineState_Starting
14570 )
14571 )
14572 )
14573 {
14574 /* The EMT thread is about to start */
14575
14576 /* Nothing to do here for now... */
14577
14578 /// @todo NEWMEDIA don't let mDVDDrive and other children
14579 /// change anything when in the Starting/Restoring state
14580 }
14581 else if ( ( oldMachineState == MachineState_Running
14582 || oldMachineState == MachineState_Paused
14583 || oldMachineState == MachineState_Teleporting
14584 || oldMachineState == MachineState_OnlineSnapshotting
14585 || oldMachineState == MachineState_LiveSnapshotting
14586 || oldMachineState == MachineState_Stuck
14587 || oldMachineState == MachineState_Starting
14588 || oldMachineState == MachineState_Stopping
14589 || oldMachineState == MachineState_Saving
14590 || oldMachineState == MachineState_Restoring
14591 || oldMachineState == MachineState_TeleportingPausedVM
14592 || oldMachineState == MachineState_TeleportingIn
14593 )
14594 && ( aMachineState == MachineState_PoweredOff
14595 || aMachineState == MachineState_Saved
14596 || aMachineState == MachineState_Teleported
14597 || aMachineState == MachineState_Aborted
14598 )
14599 )
14600 {
14601 /* The EMT thread has just stopped, unlock attached media. Note that as
14602 * opposed to locking that is done from Console, we do unlocking here
14603 * because the VM process may have aborted before having a chance to
14604 * properly unlock all media it locked. */
14605
14606 unlockMedia();
14607 }
14608
14609 if (oldMachineState == MachineState_Restoring)
14610 {
14611 if (aMachineState != MachineState_Saved)
14612 {
14613 /*
14614 * delete the saved state file once the machine has finished
14615 * restoring from it (note that Console sets the state from
14616 * Restoring to Saved if the VM couldn't restore successfully,
14617 * to give the user an ability to fix an error and retry --
14618 * we keep the saved state file in this case)
14619 */
14620 deleteSavedState = true;
14621 }
14622 }
14623 else if ( oldMachineState == MachineState_Saved
14624 && ( aMachineState == MachineState_PoweredOff
14625 || aMachineState == MachineState_Aborted
14626 || aMachineState == MachineState_Teleported
14627 )
14628 )
14629 {
14630 /*
14631 * delete the saved state after SessionMachine::ForgetSavedState() is called
14632 * or if the VM process (owning a direct VM session) crashed while the
14633 * VM was Saved
14634 */
14635
14636 /// @todo (dmik)
14637 // Not sure that deleting the saved state file just because of the
14638 // client death before it attempted to restore the VM is a good
14639 // thing. But when it crashes we need to go to the Aborted state
14640 // which cannot have the saved state file associated... The only
14641 // way to fix this is to make the Aborted condition not a VM state
14642 // but a bool flag: i.e., when a crash occurs, set it to true and
14643 // change the state to PoweredOff or Saved depending on the
14644 // saved state presence.
14645
14646 deleteSavedState = true;
14647 mData->mCurrentStateModified = TRUE;
14648 stsFlags |= SaveSTS_CurStateModified;
14649 }
14650
14651 if ( aMachineState == MachineState_Starting
14652 || aMachineState == MachineState_Restoring
14653 || aMachineState == MachineState_TeleportingIn
14654 )
14655 {
14656 /* set the current state modified flag to indicate that the current
14657 * state is no more identical to the state in the
14658 * current snapshot */
14659 if (!mData->mCurrentSnapshot.isNull())
14660 {
14661 mData->mCurrentStateModified = TRUE;
14662 stsFlags |= SaveSTS_CurStateModified;
14663 }
14664 }
14665
14666 if (deleteSavedState)
14667 {
14668 if (mRemoveSavedState)
14669 {
14670 Assert(!mSSData->strStateFilePath.isEmpty());
14671
14672 // it is safe to delete the saved state file if ...
14673 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14674 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14675 // ... none of the snapshots share the saved state file
14676 )
14677 RTFileDelete(mSSData->strStateFilePath.c_str());
14678 }
14679
14680 mSSData->strStateFilePath.setNull();
14681 stsFlags |= SaveSTS_StateFilePath;
14682 }
14683
14684 /* redirect to the underlying peer machine */
14685 mPeer->i_setMachineState(aMachineState);
14686
14687 if ( oldMachineState != MachineState_RestoringSnapshot
14688 && ( aMachineState == MachineState_PoweredOff
14689 || aMachineState == MachineState_Teleported
14690 || aMachineState == MachineState_Aborted
14691 || aMachineState == MachineState_Saved))
14692 {
14693 /* the machine has stopped execution
14694 * (or the saved state file was adopted) */
14695 stsFlags |= SaveSTS_StateTimeStamp;
14696 }
14697
14698 if ( ( oldMachineState == MachineState_PoweredOff
14699 || oldMachineState == MachineState_Aborted
14700 || oldMachineState == MachineState_Teleported
14701 )
14702 && aMachineState == MachineState_Saved)
14703 {
14704 /* the saved state file was adopted */
14705 Assert(!mSSData->strStateFilePath.isEmpty());
14706 stsFlags |= SaveSTS_StateFilePath;
14707 }
14708
14709#ifdef VBOX_WITH_GUEST_PROPS
14710 if ( aMachineState == MachineState_PoweredOff
14711 || aMachineState == MachineState_Aborted
14712 || aMachineState == MachineState_Teleported)
14713 {
14714 /* Make sure any transient guest properties get removed from the
14715 * property store on shutdown. */
14716 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14717
14718 /* remove it from the settings representation */
14719 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14720 for (settings::GuestPropertiesList::iterator
14721 it = llGuestProperties.begin();
14722 it != llGuestProperties.end();
14723 /*nothing*/)
14724 {
14725 const settings::GuestProperty &prop = *it;
14726 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14727 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14728 {
14729 it = llGuestProperties.erase(it);
14730 fNeedsSaving = true;
14731 }
14732 else
14733 {
14734 ++it;
14735 }
14736 }
14737
14738 /* Additionally remove it from the HWData representation. Required to
14739 * keep everything in sync, as this is what the API keeps using. */
14740 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14741 for (HWData::GuestPropertyMap::iterator
14742 it = llHWGuestProperties.begin();
14743 it != llHWGuestProperties.end();
14744 /*nothing*/)
14745 {
14746 uint32_t fFlags = it->second.mFlags;
14747 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14748 {
14749 /* iterator where we need to continue after the erase call
14750 * (C++03 is a fact still, and it doesn't return the iterator
14751 * which would allow continuing) */
14752 HWData::GuestPropertyMap::iterator it2 = it;
14753 ++it2;
14754 llHWGuestProperties.erase(it);
14755 it = it2;
14756 fNeedsSaving = true;
14757 }
14758 else
14759 {
14760 ++it;
14761 }
14762 }
14763
14764 if (fNeedsSaving)
14765 {
14766 mData->mCurrentStateModified = TRUE;
14767 stsFlags |= SaveSTS_CurStateModified;
14768 }
14769 }
14770#endif /* VBOX_WITH_GUEST_PROPS */
14771
14772 rc = i_saveStateSettings(stsFlags);
14773
14774 if ( ( oldMachineState != MachineState_PoweredOff
14775 && oldMachineState != MachineState_Aborted
14776 && oldMachineState != MachineState_Teleported
14777 )
14778 && ( aMachineState == MachineState_PoweredOff
14779 || aMachineState == MachineState_Aborted
14780 || aMachineState == MachineState_Teleported
14781 )
14782 )
14783 {
14784 /* we've been shut down for any reason */
14785 /* no special action so far */
14786 }
14787
14788 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14789 LogFlowThisFuncLeave();
14790 return rc;
14791}
14792
14793/**
14794 * Sends the current machine state value to the VM process.
14795 *
14796 * @note Locks this object for reading, then calls a client process.
14797 */
14798HRESULT SessionMachine::i_updateMachineStateOnClient()
14799{
14800 AutoCaller autoCaller(this);
14801 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14802
14803 ComPtr<IInternalSessionControl> directControl;
14804 {
14805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14806 AssertReturn(!!mData, E_FAIL);
14807 if (mData->mSession.mLockType == LockType_VM)
14808 directControl = mData->mSession.mDirectControl;
14809
14810 /* directControl may be already set to NULL here in #OnSessionEnd()
14811 * called too early by the direct session process while there is still
14812 * some operation (like deleting the snapshot) in progress. The client
14813 * process in this case is waiting inside Session::close() for the
14814 * "end session" process object to complete, while #uninit() called by
14815 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14816 * operation to complete. For now, we accept this inconsistent behavior
14817 * and simply do nothing here. */
14818
14819 if (mData->mSession.mState == SessionState_Unlocking)
14820 return S_OK;
14821 }
14822
14823 /* ignore notifications sent after #OnSessionEnd() is called */
14824 if (!directControl)
14825 return S_OK;
14826
14827 return directControl->UpdateMachineState(mData->mMachineState);
14828}
14829
14830
14831/*static*/
14832HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14833{
14834 va_list args;
14835 va_start(args, pcszMsg);
14836 HRESULT rc = setErrorInternal(aResultCode,
14837 getStaticClassIID(),
14838 getStaticComponentName(),
14839 Utf8Str(pcszMsg, args),
14840 false /* aWarning */,
14841 true /* aLogIt */);
14842 va_end(args);
14843 return rc;
14844}
14845
14846
14847HRESULT Machine::updateState(MachineState_T aState)
14848{
14849 NOREF(aState);
14850 ReturnComNotImplemented();
14851}
14852
14853HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14854{
14855 NOREF(aProgress);
14856 ReturnComNotImplemented();
14857}
14858
14859HRESULT Machine::endPowerUp(LONG aResult)
14860{
14861 NOREF(aResult);
14862 ReturnComNotImplemented();
14863}
14864
14865HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14866{
14867 NOREF(aProgress);
14868 ReturnComNotImplemented();
14869}
14870
14871HRESULT Machine::endPoweringDown(LONG aResult,
14872 const com::Utf8Str &aErrMsg)
14873{
14874 NOREF(aResult);
14875 NOREF(aErrMsg);
14876 ReturnComNotImplemented();
14877}
14878
14879HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14880 BOOL *aMatched,
14881 ULONG *aMaskedInterfaces)
14882{
14883 NOREF(aDevice);
14884 NOREF(aMatched);
14885 NOREF(aMaskedInterfaces);
14886 ReturnComNotImplemented();
14887
14888}
14889
14890HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14891{
14892 NOREF(aId); NOREF(aCaptureFilename);
14893 ReturnComNotImplemented();
14894}
14895
14896HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14897 BOOL aDone)
14898{
14899 NOREF(aId);
14900 NOREF(aDone);
14901 ReturnComNotImplemented();
14902}
14903
14904HRESULT Machine::autoCaptureUSBDevices()
14905{
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14910{
14911 NOREF(aDone);
14912 ReturnComNotImplemented();
14913}
14914
14915HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14916 ComPtr<IProgress> &aProgress)
14917{
14918 NOREF(aSession);
14919 NOREF(aProgress);
14920 ReturnComNotImplemented();
14921}
14922
14923HRESULT Machine::finishOnlineMergeMedium()
14924{
14925 ReturnComNotImplemented();
14926}
14927
14928HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14929 std::vector<com::Utf8Str> &aValues,
14930 std::vector<LONG64> &aTimestamps,
14931 std::vector<com::Utf8Str> &aFlags)
14932{
14933 NOREF(aNames);
14934 NOREF(aValues);
14935 NOREF(aTimestamps);
14936 NOREF(aFlags);
14937 ReturnComNotImplemented();
14938}
14939
14940HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14941 const com::Utf8Str &aValue,
14942 LONG64 aTimestamp,
14943 const com::Utf8Str &aFlags)
14944{
14945 NOREF(aName);
14946 NOREF(aValue);
14947 NOREF(aTimestamp);
14948 NOREF(aFlags);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::lockMedia()
14953{
14954 ReturnComNotImplemented();
14955}
14956
14957HRESULT Machine::unlockMedia()
14958{
14959 ReturnComNotImplemented();
14960}
14961
14962HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14963 ComPtr<IMediumAttachment> &aNewAttachment)
14964{
14965 NOREF(aAttachment);
14966 NOREF(aNewAttachment);
14967 ReturnComNotImplemented();
14968}
14969
14970HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14971 ULONG aCpuUser,
14972 ULONG aCpuKernel,
14973 ULONG aCpuIdle,
14974 ULONG aMemTotal,
14975 ULONG aMemFree,
14976 ULONG aMemBalloon,
14977 ULONG aMemShared,
14978 ULONG aMemCache,
14979 ULONG aPagedTotal,
14980 ULONG aMemAllocTotal,
14981 ULONG aMemFreeTotal,
14982 ULONG aMemBalloonTotal,
14983 ULONG aMemSharedTotal,
14984 ULONG aVmNetRx,
14985 ULONG aVmNetTx)
14986{
14987 NOREF(aValidStats);
14988 NOREF(aCpuUser);
14989 NOREF(aCpuKernel);
14990 NOREF(aCpuIdle);
14991 NOREF(aMemTotal);
14992 NOREF(aMemFree);
14993 NOREF(aMemBalloon);
14994 NOREF(aMemShared);
14995 NOREF(aMemCache);
14996 NOREF(aPagedTotal);
14997 NOREF(aMemAllocTotal);
14998 NOREF(aMemFreeTotal);
14999 NOREF(aMemBalloonTotal);
15000 NOREF(aMemSharedTotal);
15001 NOREF(aVmNetRx);
15002 NOREF(aVmNetTx);
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15007 com::Utf8Str &aResult)
15008{
15009 NOREF(aAuthParams);
15010 NOREF(aResult);
15011 ReturnComNotImplemented();
15012}
15013
15014com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15015{
15016 com::Utf8Str strControllerName = "Unknown";
15017 switch (aBusType)
15018 {
15019 case StorageBus_IDE:
15020 {
15021 strControllerName = "IDE";
15022 break;
15023 }
15024 case StorageBus_SATA:
15025 {
15026 strControllerName = "SATA";
15027 break;
15028 }
15029 case StorageBus_SCSI:
15030 {
15031 strControllerName = "SCSI";
15032 break;
15033 }
15034 case StorageBus_Floppy:
15035 {
15036 strControllerName = "Floppy";
15037 break;
15038 }
15039 case StorageBus_SAS:
15040 {
15041 strControllerName = "SAS";
15042 break;
15043 }
15044 case StorageBus_USB:
15045 {
15046 strControllerName = "USB";
15047 break;
15048 }
15049 default:
15050 break;
15051 }
15052 return strControllerName;
15053}
15054
15055HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15056{
15057 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15058
15059 AutoCaller autoCaller(this);
15060 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15061
15062 HRESULT rc = S_OK;
15063
15064 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15065 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15066 rc = getUSBDeviceFilters(usbDeviceFilters);
15067 if (FAILED(rc)) return rc;
15068
15069 NOREF(aFlags);
15070 com::Utf8Str osTypeId;
15071 ComObjPtr<GuestOSType> osType = NULL;
15072
15073 /* Get the guest os type as a string from the VB. */
15074 rc = getOSTypeId(osTypeId);
15075 if (FAILED(rc)) return rc;
15076
15077 /* Get the os type obj that coresponds, can be used to get
15078 * the defaults for this guest OS. */
15079 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15080 if (FAILED(rc)) return rc;
15081
15082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15083
15084 /* Let the OS type select 64-bit ness. */
15085 mHWData->mLongMode = osType->i_is64Bit()
15086 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15087
15088 /* Let the OS type enable the X2APIC */
15089 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15090
15091 /* This one covers IOAPICEnabled. */
15092 mBIOSSettings->i_applyDefaults(osType);
15093
15094 /* Initialize default record settings. */
15095 mRecordingSettings->i_applyDefaults();
15096
15097 /* Initialize default BIOS settings here */
15098 /* Hardware virtualization must be ON by default */
15099 mHWData->mAPIC = true;
15100 mHWData->mHWVirtExEnabled = true;
15101
15102 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15103 if (FAILED(rc)) return rc;
15104
15105 /* Graphics stuff. */
15106 GraphicsControllerType_T graphicsController;
15107 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15108 if (FAILED(rc)) return rc;
15109
15110 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15111 if (FAILED(rc)) return rc;
15112
15113 ULONG vramSize;
15114 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15115 if (FAILED(rc)) return rc;
15116
15117 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15118 if (FAILED(rc)) return rc;
15119
15120 BOOL fAccelerate2DVideoEnabled;
15121 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15122 if (FAILED(rc)) return rc;
15123
15124 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15125 if (FAILED(rc)) return rc;
15126
15127 BOOL fAccelerate3DEnabled;
15128 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15129 if (FAILED(rc)) return rc;
15130
15131 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15132 if (FAILED(rc)) return rc;
15133
15134 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15135 if (FAILED(rc)) return rc;
15136
15137 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15138 if (FAILED(rc)) return rc;
15139
15140 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15141 if (FAILED(rc)) return rc;
15142
15143 BOOL mRTCUseUTC;
15144 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15145 if (FAILED(rc)) return rc;
15146
15147 setRTCUseUTC(mRTCUseUTC);
15148 if (FAILED(rc)) return rc;
15149
15150 /* the setter does more than just the assignment, so use it */
15151 ChipsetType_T enmChipsetType;
15152 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15153 if (FAILED(rc)) return rc;
15154
15155 rc = COMSETTER(ChipsetType)(enmChipsetType);
15156 if (FAILED(rc)) return rc;
15157
15158 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15159 if (FAILED(rc)) return rc;
15160
15161 /* Apply IOMMU defaults. */
15162 IommuType_T enmIommuType;
15163 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15164 if (FAILED(rc)) return rc;
15165
15166 rc = COMSETTER(IommuType)(enmIommuType);
15167 if (FAILED(rc)) return rc;
15168
15169 /* Apply network adapters defaults */
15170 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15171 mNetworkAdapters[slot]->i_applyDefaults(osType);
15172
15173 /* Apply serial port defaults */
15174 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15175 mSerialPorts[slot]->i_applyDefaults(osType);
15176
15177 /* Apply parallel port defaults - not OS dependent*/
15178 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15179 mParallelPorts[slot]->i_applyDefaults();
15180
15181 /* Audio stuff. */
15182 AudioControllerType_T audioController;
15183 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15184 if (FAILED(rc)) return rc;
15185
15186 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15187 if (FAILED(rc)) return rc;
15188
15189 AudioCodecType_T audioCodec;
15190 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15194 if (FAILED(rc)) return rc;
15195
15196 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15197 if (FAILED(rc)) return rc;
15198
15199 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15200 if (FAILED(rc)) return rc;
15201
15202 /* Storage Controllers */
15203 StorageControllerType_T hdStorageControllerType;
15204 StorageBus_T hdStorageBusType;
15205 StorageControllerType_T dvdStorageControllerType;
15206 StorageBus_T dvdStorageBusType;
15207 BOOL recommendedFloppy;
15208 ComPtr<IStorageController> floppyController;
15209 ComPtr<IStorageController> hdController;
15210 ComPtr<IStorageController> dvdController;
15211 Utf8Str strFloppyName, strDVDName, strHDName;
15212
15213 /* GUI auto generates controller names using bus type. Do the same*/
15214 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15215
15216 /* Floppy recommended? add one. */
15217 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15218 if (FAILED(rc)) return rc;
15219 if (recommendedFloppy)
15220 {
15221 rc = addStorageController(strFloppyName,
15222 StorageBus_Floppy,
15223 floppyController);
15224 if (FAILED(rc)) return rc;
15225 }
15226
15227 /* Setup one DVD storage controller. */
15228 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15229 if (FAILED(rc)) return rc;
15230
15231 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15232 if (FAILED(rc)) return rc;
15233
15234 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15235
15236 rc = addStorageController(strDVDName,
15237 dvdStorageBusType,
15238 dvdController);
15239 if (FAILED(rc)) return rc;
15240
15241 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15242 if (FAILED(rc)) return rc;
15243
15244 /* Setup one HDD storage controller. */
15245 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15246 if (FAILED(rc)) return rc;
15247
15248 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15249 if (FAILED(rc)) return rc;
15250
15251 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15252
15253 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15254 {
15255 rc = addStorageController(strHDName,
15256 hdStorageBusType,
15257 hdController);
15258 if (FAILED(rc)) return rc;
15259
15260 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15261 if (FAILED(rc)) return rc;
15262 }
15263 else
15264 {
15265 /* The HD controller is the same as DVD: */
15266 hdController = dvdController;
15267 }
15268
15269 /* Limit the AHCI port count if it's used because windows has trouble with
15270 * too many ports and other guest (OS X in particular) may take extra long
15271 * boot: */
15272
15273 // pParent = static_cast<Medium*>(aP)
15274 IStorageController *temp = hdController;
15275 ComObjPtr<StorageController> storageController;
15276 storageController = static_cast<StorageController *>(temp);
15277
15278 // tempHDController = aHDController;
15279 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15280 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15281 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15282 storageController->COMSETTER(PortCount)(1);
15283
15284 /* USB stuff */
15285
15286 bool ohciEnabled = false;
15287
15288 ComPtr<IUSBController> usbController;
15289 BOOL recommendedUSB3;
15290 BOOL recommendedUSB;
15291 BOOL usbProxyAvailable;
15292
15293 getUSBProxyAvailable(&usbProxyAvailable);
15294 if (FAILED(rc)) return rc;
15295
15296 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15297 if (FAILED(rc)) return rc;
15298 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15299 if (FAILED(rc)) return rc;
15300
15301 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15302 {
15303#ifdef VBOX_WITH_EXTPACK
15304 /* USB 3.0 is only available if the proper ExtPack is installed. */
15305 ExtPackManager *aManager = mParent->i_getExtPackManager();
15306 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15307 {
15308 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15309 if (FAILED(rc)) return rc;
15310
15311 /* xHci includes OHCI */
15312 ohciEnabled = true;
15313 }
15314#endif
15315 }
15316 if ( !ohciEnabled
15317 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15318 {
15319 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15320 if (FAILED(rc)) return rc;
15321 ohciEnabled = true;
15322
15323#ifdef VBOX_WITH_EXTPACK
15324 /* USB 2.0 is only available if the proper ExtPack is installed.
15325 * Note. Configuring EHCI here and providing messages about
15326 * the missing extpack isn't exactly clean, but it is a
15327 * necessary evil to patch over legacy compatability issues
15328 * introduced by the new distribution model. */
15329 ExtPackManager *manager = mParent->i_getExtPackManager();
15330 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15331 {
15332 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15333 if (FAILED(rc)) return rc;
15334 }
15335#endif
15336 }
15337
15338 /* Set recommended human interface device types: */
15339 BOOL recommendedUSBHID;
15340 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15341 if (FAILED(rc)) return rc;
15342
15343 if (recommendedUSBHID)
15344 {
15345 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15346 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15347 if (!ohciEnabled && !usbDeviceFilters.isNull())
15348 {
15349 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15350 if (FAILED(rc)) return rc;
15351 }
15352 }
15353
15354 BOOL recommendedUSBTablet;
15355 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15356 if (FAILED(rc)) return rc;
15357
15358 if (recommendedUSBTablet)
15359 {
15360 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15361 if (!ohciEnabled && !usbDeviceFilters.isNull())
15362 {
15363 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15364 if (FAILED(rc)) return rc;
15365 }
15366 }
15367 return S_OK;
15368}
15369
15370/* This isn't handled entirely by the wrapper generator yet. */
15371#ifdef VBOX_WITH_XPCOM
15372NS_DECL_CLASSINFO(SessionMachine)
15373NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15374
15375NS_DECL_CLASSINFO(SnapshotMachine)
15376NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15377#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